浏览代码

Merge branch 'master' into pugi_xml

Kim Kulling 5 年之前
父节点
当前提交
2be731d1bf
共有 100 个文件被更改,包括 16046 次插入6707 次删除
  1. 3 3
      .clang-format
  2. 2 1
      .github/FUNDING.yml
  3. 59 0
      .github/workflows/ccpp.yml
  4. 56 0
      .github/workflows/sanitizer.yml
  5. 6 0
      .gitignore
  6. 171 154
      CMakeLists.txt
  7. 50 50
      INSTALL
  8. 24 0
      Readme.md
  9. 3 3
      appveyor.yml
  10. 2 4
      assimp.pc.in
  11. 18 15
      assimpTargets-debug.cmake.in
  12. 18 13
      assimpTargets-release.cmake.in
  13. 1 14
      assimpTargets.cmake.in
  14. 0 701
      code/AMF/AMFImporter.cpp
  15. 0 847
      code/Assbin/AssbinExporter.cpp
  16. 249 308
      code/AssetLib/3DS/3DSConverter.cpp
  17. 129 144
      code/AssetLib/3DS/3DSExporter.cpp
  18. 0 0
      code/AssetLib/3DS/3DSExporter.h
  19. 287 235
      code/AssetLib/3DS/3DSHelper.h
  20. 257 317
      code/AssetLib/3DS/3DSLoader.cpp
  21. 1 5
      code/AssetLib/3DS/3DSLoader.h
  22. 0 0
      code/AssetLib/3MF/3MFXmlTags.h
  23. 124 122
      code/AssetLib/3MF/D3MFExporter.cpp
  24. 0 0
      code/AssetLib/3MF/D3MFExporter.h
  25. 138 140
      code/AssetLib/3MF/D3MFImporter.cpp
  26. 0 0
      code/AssetLib/3MF/D3MFImporter.h
  27. 1 1
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  28. 0 0
      code/AssetLib/3MF/D3MFOpcPackage.h
  29. 286 387
      code/AssetLib/AC/ACLoader.cpp
  30. 34 67
      code/AssetLib/AC/ACLoader.h
  31. 672 0
      code/AssetLib/AMF/AMFImporter.cpp
  32. 0 0
      code/AssetLib/AMF/AMFImporter.hpp
  33. 357 0
      code/AssetLib/AMF/AMFImporter_Geometry.cpp
  34. 166 0
      code/AssetLib/AMF/AMFImporter_Macro.hpp
  35. 1 1
      code/AssetLib/AMF/AMFImporter_Material.cpp
  36. 1 1
      code/AssetLib/AMF/AMFImporter_Node.hpp
  37. 872 0
      code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  38. 271 285
      code/AssetLib/ASE/ASELoader.cpp
  39. 0 0
      code/AssetLib/ASE/ASELoader.h
  40. 242 371
      code/AssetLib/ASE/ASEParser.cpp
  41. 15 4
      code/AssetLib/ASE/ASEParser.h
  42. 68 0
      code/AssetLib/Assbin/AssbinExporter.cpp
  43. 0 0
      code/AssetLib/Assbin/AssbinExporter.h
  44. 832 0
      code/AssetLib/Assbin/AssbinFileWriter.cpp
  45. 65 0
      code/AssetLib/Assbin/AssbinFileWriter.h
  46. 225 228
      code/AssetLib/Assbin/AssbinLoader.cpp
  47. 0 0
      code/AssetLib/Assbin/AssbinLoader.h
  48. 4 0
      code/AssetLib/Assjson/cencode.c
  49. 4 0
      code/AssetLib/Assjson/cencode.h
  50. 88 99
      code/AssetLib/Assjson/json_exporter.cpp
  51. 0 0
      code/AssetLib/Assjson/mesh_splitter.cpp
  52. 0 0
      code/AssetLib/Assjson/mesh_splitter.h
  53. 68 0
      code/AssetLib/Assxml/AssxmlExporter.cpp
  54. 0 0
      code/AssetLib/Assxml/AssxmlExporter.h
  55. 659 0
      code/AssetLib/Assxml/AssxmlFileWriter.cpp
  56. 65 0
      code/AssetLib/Assxml/AssxmlFileWriter.h
  57. 744 0
      code/AssetLib/B3D/B3DImporter.cpp
  58. 3 3
      code/AssetLib/B3D/B3DImporter.h
  59. 205 247
      code/AssetLib/BVH/BVHLoader.cpp
  60. 22 29
      code/AssetLib/BVH/BVHLoader.h
  61. 62 84
      code/AssetLib/Blender/BlenderBMesh.cpp
  62. 0 0
      code/AssetLib/Blender/BlenderBMesh.h
  63. 181 0
      code/AssetLib/Blender/BlenderCustomData.cpp
  64. 0 0
      code/AssetLib/Blender/BlenderCustomData.h
  65. 75 90
      code/AssetLib/Blender/BlenderDNA.cpp
  66. 162 205
      code/AssetLib/Blender/BlenderDNA.h
  67. 1 1
      code/AssetLib/Blender/BlenderDNA.inl
  68. 0 0
      code/AssetLib/Blender/BlenderIntermediate.h
  69. 17 18
      code/AssetLib/Blender/BlenderLoader.cpp
  70. 0 0
      code/AssetLib/Blender/BlenderLoader.h
  71. 86 86
      code/AssetLib/Blender/BlenderModifier.cpp
  72. 0 0
      code/AssetLib/Blender/BlenderModifier.h
  73. 838 0
      code/AssetLib/Blender/BlenderScene.cpp
  74. 252 277
      code/AssetLib/Blender/BlenderScene.h
  75. 0 0
      code/AssetLib/Blender/BlenderSceneGen.h
  76. 0 0
      code/AssetLib/Blender/BlenderTessellator.cpp
  77. 0 0
      code/AssetLib/Blender/BlenderTessellator.h
  78. 0 0
      code/AssetLib/C4D/C4DImporter.cpp
  79. 0 0
      code/AssetLib/C4D/C4DImporter.h
  80. 248 287
      code/AssetLib/COB/COBLoader.cpp
  81. 0 0
      code/AssetLib/COB/COBLoader.h
  82. 2 2
      code/AssetLib/COB/COBScene.h
  83. 1 1
      code/AssetLib/CSM/CSMLoader.cpp
  84. 0 0
      code/AssetLib/CSM/CSMLoader.h
  85. 1748 0
      code/AssetLib/Collada/ColladaExporter.cpp
  86. 257 0
      code/AssetLib/Collada/ColladaExporter.h
  87. 8 15
      code/AssetLib/Collada/ColladaHelper.cpp
  88. 185 224
      code/AssetLib/Collada/ColladaHelper.h
  89. 230 249
      code/AssetLib/Collada/ColladaLoader.cpp
  90. 0 0
      code/AssetLib/Collada/ColladaLoader.h
  91. 179 328
      code/AssetLib/Collada/ColladaParser.cpp
  92. 392 0
      code/AssetLib/Collada/ColladaParser.h
  93. 1 1
      code/AssetLib/DXF/DXFHelper.h
  94. 5 5
      code/AssetLib/DXF/DXFLoader.cpp
  95. 0 0
      code/AssetLib/DXF/DXFLoader.h
  96. 0 0
      code/AssetLib/FBX/FBXAnimation.cpp
  97. 4 1
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  98. 34 34
      code/AssetLib/FBX/FBXCommon.h
  99. 0 0
      code/AssetLib/FBX/FBXCompileConfig.h
  100. 3510 0
      code/AssetLib/FBX/FBXConverter.cpp

+ 3 - 3
.clang-format

@@ -70,8 +70,8 @@ IncludeCategories:
   - Regex:           '^<.*'
     Priority:        3
 # IncludeIsMainRegex: '(Test)?$'
-IndentCaseLabels: true
-# IndentPPDirectives: None
+IndentCaseLabels: false 
+#IndentPPDirectives: AfterHash
 IndentWidth:     4
 # IndentWrappedFunctionNames: false
 # JavaScriptQuotes: Leave
@@ -108,7 +108,7 @@ IndentWidth:     4
 # SpacesInParentheses: false
 # SpacesInSquareBrackets: false
 TabWidth:        4
-UseTab:          Always
+UseTab:          Never
 ---
 ### C++ specific config ###
 Language:        Cpp

+ 2 - 1
.github/FUNDING.yml

@@ -1,2 +1,3 @@
 patreon: assimp
-ko_fi: kimkulling
+custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JRJVPXC4QJM4
+open_collective: assimp

+ 59 - 0
.github/workflows/ccpp.yml

@@ -0,0 +1,59 @@
+name: C/C++ CI
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  job:
+    name: ${{ matrix.os }}-${{ matrix.cxx }}-build-and-test
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        name: [ubuntu-gcc, macos-clang, windows-msvc, ubuntu-clang]
+        # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
+        include:
+          - name: windows-msvc
+            os: windows-latest
+            cxx: cl.exe
+            cc: cl.exe
+          - name: ubuntu-clang
+            os: ubuntu-latest
+            cxx: clang++
+            cc: clang
+          - name: macos-clang
+            os: macos-latest
+            cxx: clang++
+            cc: clang
+          - name: ubuntu-gcc
+            os: ubuntu-latest
+            cxx: g++
+            cc: gcc
+
+    steps:
+    - uses: actions/checkout@v2
+    
+    - uses: lukka/get-cmake@latest
+    
+    - uses: ilammy/msvc-dev-cmd@v1
+    
+    - uses: lukka/set-shell-env@v1
+      with:
+        CXX: ${{ matrix.cxx }}
+        CC: ${{ matrix.cc }}
+    
+    - name: configure and build
+      uses: lukka/run-cmake@v2
+      with:
+        cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
+        cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
+        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Release'
+        buildWithCMakeArgs: '-- -v'
+        buildDirectory: '${{ github.workspace }}/build/'
+        
+    - name: test
+      run: cd build/bin && ./unit
+      shell: bash

+ 56 - 0
.github/workflows/sanitizer.yml

@@ -0,0 +1,56 @@
+name: C/C++ Sanitizer
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  job1:
+    name: adress-sanitizer
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: lukka/get-cmake@latest    
+    - uses: lukka/set-shell-env@v1
+      with:
+        CXX: clang++
+        CC: clang
+    
+    - name: configure and build
+      uses: lukka/run-cmake@v2
+      with:
+        cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
+        cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
+        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Debug -DASSIMP_ASAN=ON'
+        buildWithCMakeArgs: '-- -v'
+        buildDirectory: '${{ github.workspace }}/build/'
+    
+    - name: test
+      run: cd build/bin && ./unit
+      shell: bash
+
+  job2:
+    name: undefined-behavior-sanitizer
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: lukka/get-cmake@latest    
+    - uses: lukka/set-shell-env@v1
+      with:
+        CXX: clang++
+        CC: clang
+    
+    - name: configure and build
+      uses: lukka/run-cmake@v2
+      with:
+        cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
+        cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
+        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Debug -DASSIMP_UBSAN=ON'
+        buildWithCMakeArgs: '-- -v'
+        buildDirectory: '${{ github.workspace }}/build/'
+    
+    - name: test
+      run: cd build/bin && ./unit
+      shell: bash

+ 6 - 0
.gitignore

@@ -79,6 +79,12 @@ test/gtest/src/gtest-stamp/Debug/
 tools/assimp_view/assimp_viewer.vcxproj.user
 *.pyc
 
+### Rust ###
+# Generated by Cargo; will have compiled files and executables
+port/assimp_rs/target/
+# Backup files generated by rustfmt
+port/assimp_rs/**/*.rs.bk
+
 # Unix editor backups
 *~
 test/gtest/src/gtest-stamp/gtest-gitinfo.txt

+ 171 - 154
CMakeLists.txt

@@ -39,17 +39,17 @@ SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
 CMAKE_MINIMUM_REQUIRED( VERSION 3.0 )
 
 # Toggles the use of the hunter package manager
-option(HUNTER_ENABLED "Enable Hunter package manager support" OFF)
+option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
 
-include("cmake/HunterGate.cmake")
-HunterGate(
+IF(ASSIMP_HUNTER_ENABLED)
+  include("cmake/HunterGate.cmake")
+  HunterGate(
     URL "https://github.com/ruslo/hunter/archive/v0.23.176.tar.gz"
     SHA1 "2e9ae973d028660b735ac4c6142725ca36a0048a"
-)
+  )
 
-IF(HUNTER_ENABLED)
   add_definitions(-DASSIMP_USE_HUNTER)
-ENDIF(HUNTER_ENABLED)
+ENDIF()
 
 PROJECT( Assimp VERSION 5.0.1 )
 
@@ -60,7 +60,7 @@ OPTION( BUILD_SHARED_LIBS
   ON
 )
 
-OPTION( BUILD_FRAMEWORK
+OPTION( ASSIMP_BUILD_FRAMEWORK
   "Build package as Mac OS X Framework bundle."
   OFF
 )
@@ -100,14 +100,14 @@ OPTION ( ASSIMP_COVERALLS
   "Enable this to measure test coverage."
   OFF
 )
+OPTION( ASSIMP_INSTALL
+  "Disable this if you want to use assimp as a submodule."
+  ON
+)
 OPTION ( ASSIMP_ERROR_MAX
   "Enable all warnings."
   OFF
 )
-OPTION ( ASSIMP_WERROR
-  "Treat warnings as errors."
-  OFF
-)
 OPTION ( ASSIMP_ASAN
   "Enable AddressSanitizer."
   OFF
@@ -116,30 +116,36 @@ OPTION ( ASSIMP_UBSAN
   "Enable Undefined Behavior sanitizer."
   OFF
 )
-OPTION ( SYSTEM_IRRXML
+OPTION ( ASSIMP_SYSTEM_IRRXML
   "Use system installed Irrlicht/IrrXML library."
   OFF
 )
-OPTION ( BUILD_DOCS
+OPTION ( ASSIMP_BUILD_DOCS
   "Build documentation using Doxygen."
   OFF
 )
-OPTION( INJECT_DEBUG_POSTFIX
+OPTION( ASSIMP_INJECT_DEBUG_POSTFIX
   "Inject debug postfix in .a/.so/.dll lib names"
   ON
 )
 
-OPTION ( IGNORE_GIT_HASH
+OPTION ( ASSIMP_IGNORE_GIT_HASH
    "Don't call git to get the hash."
    OFF
 )
 
-IF (IOS AND NOT HUNTER_ENABLED)
+IF ( WIN32 )
+    OPTION ( ASSIMP_BUILD_ASSIMP_VIEW 
+      "If the Assimp view tool is built. (requires DirectX)" 
+      OFF )
+ENDIF()
+
+IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
   IF (NOT CMAKE_BUILD_TYPE)
     SET(CMAKE_BUILD_TYPE "Release")
-  ENDIF (NOT CMAKE_BUILD_TYPE)
+  ENDIF ()
   ADD_DEFINITIONS(-DENABLE_BITCODE)
-ENDIF (IOS AND NOT HUNTER_ENABLED)
+ENDIF ()
 
 # Use subset of Windows.h
 if (WIN32)
@@ -155,19 +161,19 @@ IF(MSVC)
     # Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
     ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
   ENDIF()
-ENDIF(MSVC)
+ENDIF()
 
-IF (BUILD_FRAMEWORK)
+IF (ASSIMP_BUILD_FRAMEWORK)
   SET (BUILD_SHARED_LIBS ON)
   MESSAGE(STATUS "Framework bundle building enabled")
-ENDIF(BUILD_FRAMEWORK)
+ENDIF()
 
 IF(NOT BUILD_SHARED_LIBS)
   MESSAGE(STATUS "Shared libraries disabled")
   SET(LINK_SEARCH_START_STATIC TRUE)
 ELSE()
   MESSAGE(STATUS "Shared libraries enabled")
-ENDIF(NOT BUILD_SHARED_LIBS)
+ENDIF()
 
 # Define here the needed parameters
 SET (ASSIMP_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
@@ -177,12 +183,12 @@ SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VER
 SET (ASSIMP_SOVERSION 5)
 
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
-if(NOT HUNTER_ENABLED)
+if(NOT ASSIMP_HUNTER_ENABLED)
   # Enable C++11 support globally
   set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
 endif()
 
-IF(NOT IGNORE_GIT_HASH)
+IF(NOT ASSIMP_IGNORE_GIT_HASH)
   # Get the current working branch
   EXECUTE_PROCESS(
     COMMAND git rev-parse --abbrev-ref HEAD
@@ -204,11 +210,11 @@ ENDIF()
 
 IF(NOT GIT_COMMIT_HASH)
   SET(GIT_COMMIT_HASH 0)
-ENDIF(NOT GIT_COMMIT_HASH)
+ENDIF()
 
 IF(ASSIMP_DOUBLE_PRECISION)
     ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION)
-ENDIF(ASSIMP_DOUBLE_PRECISION)
+ENDIF()
 
 CONFIGURE_FILE(
   ${CMAKE_CURRENT_LIST_DIR}/revision.h.in
@@ -238,33 +244,32 @@ SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names")
 IF( UNIX )
   # Use GNUInstallDirs for Unix predefined directories
   INCLUDE(GNUInstallDirs)
-ENDIF( UNIX )
+ENDIF()
 
-# Grouped compiler settings
+# Grouped compiler settings ########################################
 IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
-  IF(NOT HUNTER_ENABLED)
-    SET(CMAKE_CXX_FLAGS "-fPIC -std=c++0x ${CMAKE_CXX_FLAGS}")
-    SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+  IF(NOT ASSIMP_HUNTER_ENABLED)
+    SET(CMAKE_CXX_STANDARD 11)
+    SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
   ENDIF()
   # hide all not-exported symbols
-  SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
+  SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
   SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
   SET(LIBSTDC++_LIBRARIES -lstdc++)
 ELSEIF(MSVC)
   # enable multi-core compilation with MSVC
-  ADD_COMPILE_OPTIONS(/MP)
-  ADD_COMPILE_OPTIONS( /bigobj )
+  ADD_COMPILE_OPTIONS(/MP /bigobj /W4 /WX ) 
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   IF(MSVC12)
     ADD_COMPILE_OPTIONS(/wd4351)
   ENDIF()
   SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
 ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
-  IF(NOT HUNTER_ENABLED)
-    SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")
-    SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+  IF(NOT ASSIMP_HUNTER_ENABLED)
+    SET(CMAKE_CXX_STANDARD 11)
+    SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
   ENDIF()
-  SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
+  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 )
   IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
@@ -272,7 +277,7 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW )
   ELSEIF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.3)
     message(WARNING "MinGW is old, if you experience errors, update MinGW.")
   ENDIF()
-  IF(NOT HUNTER_ENABLED)
+  IF(NOT ASSIMP_HUNTER_ENABLED)
     SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
     SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
   ENDIF()
@@ -281,7 +286,7 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW )
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
 ENDIF()
 
-IF ( IOS AND NOT HUNTER_ENABLED)
+IF ( IOS AND NOT ASSIMP_HUNTER_ENABLED)
   IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
     SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -Og")
     SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -Og")
@@ -290,7 +295,7 @@ IF ( IOS AND NOT HUNTER_ENABLED)
     SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
     # Experimental for pdb generation
   ENDIF()
-ENDIF( IOS AND NOT HUNTER_ENABLED)
+ENDIF()
 
 IF (ASSIMP_COVERALLS)
   MESSAGE(STATUS "Coveralls enabled")
@@ -309,16 +314,6 @@ IF (ASSIMP_ERROR_MAX)
   ENDIF()
 ENDIF()
 
-IF (ASSIMP_WERROR)
-  MESSAGE(STATUS "Treating warnings as errors")
-  IF (MSVC)
-    ADD_COMPILE_OPTIONS(/WX)
-  ELSE()
-    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
-    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
-  ENDIF()
-ENDIF()
-
 IF (ASSIMP_ASAN)
   MESSAGE(STATUS "AddressSanitizer enabled")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
@@ -339,22 +334,18 @@ INCLUDE (PrecompiledHeader)
 # source tree. During an out-of-source build, however, do not litter this
 # directory, since that is probably what the user wanted to avoid.
 IF ( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
-  SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib" )
+  SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" )
   SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib" )
   SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" )
-ENDIF ( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
-
-# Cache these to allow the user to override them manually.
-SET( ASSIMP_LIB_INSTALL_DIR "lib" CACHE STRING
-  "Path the built library files are installed to." )
-SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE STRING
-  "Path the header files are installed to." )
-SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
-  "Path the tool executables are installed to." )
+ELSE()
+  SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
+  SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
+  SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
+ENDIF ()
 
 get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
 
-IF (INJECT_DEBUG_POSTFIX AND (is_multi_config OR CMAKE_BUILD_TYPE STREQUAL "Debug"))
+IF (ASSIMP_INJECT_DEBUG_POSTFIX AND (is_multi_config OR CMAKE_BUILD_TYPE STREQUAL "Debug"))
   SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfix for lib, samples and tools")
 ELSE()
   SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Debug Postfix for lib, samples and tools")
@@ -367,7 +358,7 @@ IF (NOT TARGET uninstall)
   ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
 ENDIF()
 
-IF(HUNTER_ENABLED)
+IF(ASSIMP_HUNTER_ENABLED)
   set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
   set(INCLUDE_INSTALL_DIR "include")
 
@@ -404,13 +395,35 @@ IF(HUNTER_ENABLED)
       NAMESPACE "${NAMESPACE}"
       DESTINATION "${CONFIG_INSTALL_DIR}"
   )
-ELSE(HUNTER_ENABLED)
+ELSE()
   # cmake configuration files
   if(${BUILD_SHARED_LIBS})
     set(BUILD_LIB_TYPE SHARED)
   else()
     set(BUILD_LIB_TYPE STATIC)
   endif()
+
+  IF( UNIX )
+    # Use GNUInstallDirs for Unix predefined directories
+    INCLUDE(GNUInstallDirs)
+
+    SET( ASSIMP_LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
+    SET( ASSIMP_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
+    SET( ASSIMP_BIN_INSTALL_DIR ${CMAKE_INSTALL_BINDIR})
+  ELSE()
+    # Cache these to allow the user to override them on non-Unix platforms
+    SET( ASSIMP_LIB_INSTALL_DIR "lib" CACHE STRING
+      "Path the built library files are installed to." )
+    SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE STRING
+      "Path the header files are installed to." )
+    SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
+      "Path the tool executables are installed to." )
+
+    SET(CMAKE_INSTALL_FULL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_INCLUDE_INSTALL_DIR})
+    SET(CMAKE_INSTALL_FULL_LIBDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_LIB_INSTALL_DIR})
+    SET(CMAKE_INSTALL_FULL_BINDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_BIN_INSTALL_DIR})
+  ENDIF()
+
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
   IF (is_multi_config)
@@ -432,22 +445,20 @@ ELSE(HUNTER_ENABLED)
     "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake"
     ${PACKAGE_TARGETS_FILE}
     DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT})
-ENDIF(HUNTER_ENABLED)
-
-FIND_PACKAGE( DirectX )
+ENDIF()
 
-IF( BUILD_DOCS )
+IF( ASSIMP_BUILD_DOCS )
   ADD_SUBDIRECTORY(doc)
-ENDIF( BUILD_DOCS )
+ENDIF()
 
 # Look for system installed irrXML
-IF ( SYSTEM_IRRXML )
+IF ( ASSIMP_SYSTEM_IRRXML )
   FIND_PACKAGE( IrrXML REQUIRED )
-ENDIF( SYSTEM_IRRXML )
+ENDIF()
 
 # Search for external dependencies, and build them from source if not found
 # Search for zlib
-IF(HUNTER_ENABLED)
+IF(ASSIMP_HUNTER_ENABLED)
   hunter_add_package(ZLIB)
   find_package(ZLIB CONFIG REQUIRED)
 
@@ -455,10 +466,10 @@ IF(HUNTER_ENABLED)
   set(ZLIB_FOUND TRUE)
   set(ZLIB_LIBRARIES ZLIB::zlib)
   set(ASSIMP_BUILD_MINIZIP TRUE)
-ELSE(HUNTER_ENABLED)
+ELSE()
   IF ( NOT ASSIMP_BUILD_ZLIB )
     FIND_PACKAGE(ZLIB)
-  ENDIF( NOT ASSIMP_BUILD_ZLIB )
+  ENDIF()
 
   IF( NOT ZLIB_FOUND )
     MESSAGE(STATUS "compiling zlib from sources")
@@ -481,46 +492,46 @@ ELSE(HUNTER_ENABLED)
     SET(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/contrib/zlib ${CMAKE_CURRENT_BINARY_DIR}/contrib/zlib)
     # need to ensure we don't link with system zlib or minizip as well.
     SET(ASSIMP_BUILD_MINIZIP 1)
-  ELSE(NOT ZLIB_FOUND)
+  ELSE()
     ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB)
     SET(ZLIB_LIBRARIES_LINKED -lz)
-  ENDIF(NOT ZLIB_FOUND)
+  ENDIF()
   INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
-ENDIF(HUNTER_ENABLED)
+ENDIF()
 
 IF( NOT IOS )
   IF( NOT ASSIMP_BUILD_MINIZIP )
     use_pkgconfig(UNZIP minizip)
-  ENDIF( NOT ASSIMP_BUILD_MINIZIP )
-ELSE ( NOT IOS )
+  ENDIF()
+ELSE ()
   IF( NOT BUILD_SHARED_LIBS )
     IF( NOT ASSIMP_BUILD_MINIZIP )
       use_pkgconfig(UNZIP minizip)
-    ENDIF( NOT ASSIMP_BUILD_MINIZIP )
-  ENDIF ( NOT BUILD_SHARED_LIBS )
-ENDIF ( NOT IOS )
+    ENDIF()
+  ENDIF ()
+ENDIF ()
 
 IF ( ASSIMP_NO_EXPORT )
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_EXPORT)
   MESSAGE( STATUS "Build an import-only version of Assimp." )
-ENDIF( ASSIMP_NO_EXPORT )
+ENDIF()
 
 SET ( ASSIMP_BUILD_ARCHITECTURE "" CACHE STRING
   "describe the current architecture."
 )
-IF    ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
-ELSE  ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
+IF( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
+ELSE()
   ADD_DEFINITIONS ( -D'ASSIMP_BUILD_ARCHITECTURE="${ASSIMP_BUILD_ARCHITECTURE}"' )
-ENDIF ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
+ENDIF()
 
 # ${CMAKE_GENERATOR}
 SET ( ASSIMP_BUILD_COMPILER "" CACHE STRING
   "describe the current compiler."
 )
-IF    ( ASSIMP_BUILD_COMPILER STREQUAL "")
-ELSE  ( ASSIMP_BUILD_COMPILER STREQUAL "")
+IF( ASSIMP_BUILD_COMPILER STREQUAL "")
+ELSE()
   ADD_DEFINITIONS ( -D'ASSIMP_BUILD_COMPILER="${ASSIMP_BUILD_COMPILER}"' )
-ENDIF ( ASSIMP_BUILD_COMPILER STREQUAL "")
+ENDIF()
 
 MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
 
@@ -566,91 +577,97 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
     MESSAGE( FATAL_ERROR
       "C4D is currently only available on Windows with melange SDK installed in contrib/Melange"
     )
-  ENDIF ( MSVC )
-ELSE (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+  ENDIF ()
+ELSE ()
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
-ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
+ENDIF ()
 
-IF(NOT HUNTER_ENABLED)
+IF(NOT ASSIMP_HUNTER_ENABLED)
   ADD_SUBDIRECTORY(contrib)
-ENDIF(NOT HUNTER_ENABLED)
+ENDIF()
 
 ADD_SUBDIRECTORY( code/ )
 IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
   # The viewer for windows only
-  IF ( WIN32 AND DirectX_D3DX9_LIBRARY )
-    OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} )
+  IF ( 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 ( ASSIMP_BUILD_ASSIMP_VIEW )
-  ENDIF ( WIN32 AND DirectX_D3DX9_LIBRARY )
-  # Te command line tool
+    ENDIF ()
+  ENDIF ()
+  # The command line tool
   ADD_SUBDIRECTORY( tools/assimp_cmd/ )
-ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
+ENDIF ()
 
 IF ( ASSIMP_BUILD_SAMPLES)
+  SET( SAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/samples )
+  SET( SAMPLES_SHARED_CODE_DIR ${SAMPLES_DIR}/SharedCode )
   IF ( WIN32 )
     ADD_SUBDIRECTORY( samples/SimpleTexturedOpenGL/ )
     ADD_SUBDIRECTORY( samples/SimpleTexturedDirectx11 )
-  ENDIF ( WIN32 )
+  ENDIF ()
   ADD_SUBDIRECTORY( samples/SimpleOpenGL/ )
-ENDIF ( ASSIMP_BUILD_SAMPLES )
+ENDIF ()
 
 IF ( ASSIMP_BUILD_TESTS )
   ADD_SUBDIRECTORY( test/ )
-ENDIF ( ASSIMP_BUILD_TESTS )
+ENDIF ()
 
 # Generate a pkg-config .pc for the Assimp library.
 CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/assimp.pc.in" "${PROJECT_BINARY_DIR}/assimp.pc" @ONLY )
-INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTALL_DIR}/pkgconfig/ COMPONENT ${LIBASSIMP-DEV_COMPONENT})
-
-IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES)
-  # Packing information
-  SET(CPACK_PACKAGE_NAME                    "assimp{ASSIMP_VERSION_MAJOR}.{ASSIMP_VERSION_MINOR}")
-  SET(CPACK_PACKAGE_CONTACT "" CACHE STRING "Package maintainer and PGP signer.")
-  SET(CPACK_PACKAGE_VENDOR                  "https://github.com/assimp")
-  SET(CPACK_PACKAGE_DISPLAY_NAME            "Assimp ${ASSIMP_VERSION}")
-  SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY     " - Open Asset Import Library ${ASSIMP_VERSION}")
-  SET(CPACK_PACKAGE_VERSION                 "${ASSIMP_VERSION}.${ASSIMP_PACKAGE_VERSION}" )
-  SET(CPACK_PACKAGE_VERSION_MAJOR           "${ASSIMP_VERSION_MAJOR}")
-  SET(CPACK_PACKAGE_VERSION_MINOR           "${ASSIMP_VERSION_MINOR}")
-  SET(CPACK_PACKAGE_VERSION_PATCH           "${ASSIMP_VERSION_PATCH}")
-  SET(CPACK_PACKAGE_INSTALL_DIRECTORY       "assimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
-  SET(CPACK_RESOURCE_FILE_LICENSE           "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
-
-  STRING(TOUPPER ${LIBASSIMP_COMPONENT}     "LIBASSIMP_COMPONENT_UPPER")
-  STRING(TOUPPER ${LIBASSIMP-DEV_COMPONENT} "LIBASSIMP-DEV_COMPONENT_UPPER")
-
-  SET(CPACK_COMPONENT_ASSIMP-BIN_DISPLAY_NAME                       "tools")
-  SET(CPACK_COMPONENT_ASSIMP-BIN_DEPENDS                            "${LIBASSIMP_COMPONENT}" )
-  SET(CPACK_COMPONENT_${LIBASSIMP_COMPONENT_UPPER}_DISPLAY_NAME     "libraries")
-  SET(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DISPLAY_NAME "common headers and installs")
-  SET(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DEPENDS $    "{LIBASSIMP_COMPONENT}" )
-  SET(CPACK_COMPONENT_ASSIMP-DEV_DISPLAY_NAME                       "${CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT}_DISPLAY_NAME}" )
-  SET(CPACK_COMPONENT_ASSIMP-DEV_DEPENDS                            "${LIBASSIMP-DEV_COMPONENT}" )
-  SET(CPACK_DEBIAN_BUILD_DEPENDS debhelper cmake zlib1g-dev pkg-config)
-
-  # debian
-  SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
-  SET(CPACK_DEBIAN_CMAKE_OPTIONS    "-DBUILD_ASSIMP_SAMPLES:BOOL=${ASSIMP_BUILD_SAMPLES}")
-  SET(CPACK_DEBIAN_PACKAGE_SECTION  "libs" )
-  SET(CPACK_DEBIAN_PACKAGE_DEPENDS  "${CPACK_COMPONENTS_ALL}")
-  SET(CPACK_DEBIAN_PACKAGE_SUGGESTS)
-  set(cPACK_DEBIAN_PACKAGE_NAME     "assimp")
-  SET(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES contrib/gtest contrib/zlib workspaces test doc obj samples packaging)
-  SET(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force)
-  SET(CPACK_DEBIAN_CHANGELOG)
-  execute_process(COMMAND lsb_release -is
-    OUTPUT_VARIABLE _lsb_distribution OUTPUT_STRIP_TRAILING_WHITESPACE
-    RESULT_VARIABLE _lsb_release_failed)
-  SET(CPACK_DEBIAN_DISTRIBUTION_NAME ${_lsb_distribution} CACHE STRING "Name of the distrubiton")
-  STRING(TOLOWER ${CPACK_DEBIAN_DISTRIBUTION_NAME} CPACK_DEBIAN_DISTRIBUTION_NAME)
-  IF( ${CPACK_DEBIAN_DISTRIBUTION_NAME} STREQUAL "ubuntu" )
-    SET(CPACK_DEBIAN_DISTRIBUTION_RELEASES lucid maverick natty oneiric precise CACHE STRING "Release code-names of the distrubiton release")
+IF ( ASSIMP_INSTALL )
+  INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTALL_DIR}/pkgconfig/ COMPONENT ${LIBASSIMP-DEV_COMPONENT})
+ENDIF()
+
+IF ( ASSIMP_INSTALL )
+  IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES)
+    # Packing information
+    SET(CPACK_PACKAGE_NAME                    "assimp{ASSIMP_VERSION_MAJOR}.{ASSIMP_VERSION_MINOR}")
+    SET(CPACK_PACKAGE_CONTACT "" CACHE STRING "Package maintainer and PGP signer.")
+    SET(CPACK_PACKAGE_VENDOR                  "https://github.com/assimp")
+    SET(CPACK_PACKAGE_DISPLAY_NAME            "Assimp ${ASSIMP_VERSION}")
+    SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY     " - Open Asset Import Library ${ASSIMP_VERSION}")
+    SET(CPACK_PACKAGE_VERSION                 "${ASSIMP_VERSION}.${ASSIMP_PACKAGE_VERSION}" )
+    SET(CPACK_PACKAGE_VERSION_MAJOR           "${ASSIMP_VERSION_MAJOR}")
+    SET(CPACK_PACKAGE_VERSION_MINOR           "${ASSIMP_VERSION_MINOR}")
+    SET(CPACK_PACKAGE_VERSION_PATCH           "${ASSIMP_VERSION_PATCH}")
+    SET(CPACK_PACKAGE_INSTALL_DIRECTORY       "assimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
+    SET(CPACK_RESOURCE_FILE_LICENSE           "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+
+    STRING(TOUPPER ${LIBASSIMP_COMPONENT}     "LIBASSIMP_COMPONENT_UPPER")
+    STRING(TOUPPER ${LIBASSIMP-DEV_COMPONENT} "LIBASSIMP-DEV_COMPONENT_UPPER")
+
+    SET(CPACK_COMPONENT_ASSIMP-BIN_DISPLAY_NAME                       "tools")
+    SET(CPACK_COMPONENT_ASSIMP-BIN_DEPENDS                            "${LIBASSIMP_COMPONENT}" )
+    SET(CPACK_COMPONENT_${LIBASSIMP_COMPONENT_UPPER}_DISPLAY_NAME     "libraries")
+    SET(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DISPLAY_NAME "common headers and installs")
+    SET(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DEPENDS $    "{LIBASSIMP_COMPONENT}" )
+    SET(CPACK_COMPONENT_ASSIMP-DEV_DISPLAY_NAME                       "${CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT}_DISPLAY_NAME}" )
+    SET(CPACK_COMPONENT_ASSIMP-DEV_DEPENDS                            "${LIBASSIMP-DEV_COMPONENT}" )
+    SET(CPACK_DEBIAN_BUILD_DEPENDS debhelper cmake zlib1g-dev pkg-config)
+
+    # debian
+    SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
+    SET(CPACK_DEBIAN_CMAKE_OPTIONS    "-DBUILD_ASSIMP_SAMPLES:BOOL=${ASSIMP_BUILD_SAMPLES}")
+    SET(CPACK_DEBIAN_PACKAGE_SECTION  "libs" )
+    SET(CPACK_DEBIAN_PACKAGE_DEPENDS  "${CPACK_COMPONENTS_ALL}")
+    SET(CPACK_DEBIAN_PACKAGE_SUGGESTS)
+    SET(cPACK_DEBIAN_PACKAGE_NAME     "assimp")
+    SET(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES contrib/gtest contrib/zlib workspaces test doc obj samples packaging)
+    SET(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force)
+    SET(CPACK_DEBIAN_CHANGELOG)
+    execute_process(COMMAND lsb_release -is
+      OUTPUT_VARIABLE _lsb_distribution OUTPUT_STRIP_TRAILING_WHITESPACE
+      RESULT_VARIABLE _lsb_release_failed)
+    SET(CPACK_DEBIAN_DISTRIBUTION_NAME ${_lsb_distribution} CACHE STRING "Name of the distrubiton")
+    STRING(TOLOWER ${CPACK_DEBIAN_DISTRIBUTION_NAME} CPACK_DEBIAN_DISTRIBUTION_NAME)
+    IF( ${CPACK_DEBIAN_DISTRIBUTION_NAME} STREQUAL "ubuntu" )
+      SET(CPACK_DEBIAN_DISTRIBUTION_RELEASES lucid maverick natty oneiric precise CACHE STRING "Release code-names of the distrubiton release")
+    ENDIF()
+    SET(DPUT_HOST "" CACHE STRING "PPA repository to upload the debian sources")
+    INCLUDE(CPack)
+    INCLUDE(DebSourcePPA)
   ENDIF()
-  SET(DPUT_HOST "" CACHE STRING "PPA repository to upload the debian sources")
-  INCLUDE(CPack)
-  INCLUDE(DebSourcePPA)
 ENDIF()
 
 if(WIN32)
@@ -671,7 +688,7 @@ if(WIN32)
       SET(ASSIMP_MSVC_VERSION "vc140")
     ELSEIF(MSVC15)
       SET(ASSIMP_MSVC_VERSION "vc141")
-    ENDIF(MSVC12)
+    ENDIF()
   ENDIF()
 
   IF(MSVC12 OR MSVC14 OR MSVC15 )
@@ -696,5 +713,5 @@ if(WIN32)
       ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
       ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
     ENDIF()
-  ENDIF(MSVC12 OR MSVC14 OR MSVC15 )
-ENDIF (WIN32)
+  ENDIF()
+ENDIF ()

+ 50 - 50
INSTALL

@@ -1,50 +1,50 @@
-	
-========================================================================
-Open Asset Import Library (assimp) INSTALL 
-========================================================================
-
-------------------------------
-Getting the documentation
-------------------------------
-
-A regularly-updated copy is available at 
-http://assimp.sourceforge.net/lib_html/index.html
-
-A CHM file is included in the SVN repos: ./doc/AssimpDoc_Html/AssimpDoc.chm.
-To build the doxygen documentation on your own, follow these steps:
-
-a) download & install latest doxygen 
-b) make sure doxygen is in the executable search path
-c) navigate to ./doc
-d) and run 'doxygen'
-
-Open the generated HTML (AssimpDoc_Html/index.html) in the browser of your choice.
-Windows only: To generate the CHM doc, install 'Microsoft HTML Workshop'
-and configure the path to it in the DOXYFILE first. 
-
-------------------------------
-Building Assimp 
-------------------------------
-
-More detailed build instructions can be found in the documentation,
-this section is just for the inpatient among you.
-
-CMake is the preferred build system for Assimp. The minimum required version 
-is 2.6. If you don't have it yet, downloads for CMake can be found on
-http://www.cmake.org/. 
-
-For Unix:
-
-1. mkdir build && cd build
-2. cmake .. -G 'Unix Makefiles'
-3. make -j4
-
-For Windows:
-1. Open a command prompt
-2. mkdir build
-3. cd build
-4. cmake ..
-5. cmake --build .
-
-For iOS:
-Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS
+	
+========================================================================
+Open Asset Import Library (assimp) INSTALL 
+========================================================================
+
+------------------------------
+Getting the documentation
+------------------------------
+
+A regularly-updated copy is available at 
+http://assimp.sourceforge.net/lib_html/index.html
+
+A CHM file is included in the SVN repos: ./doc/AssimpDoc_Html/AssimpDoc.chm.
+To build the doxygen documentation on your own, follow these steps:
+
+a) download & install latest doxygen 
+b) make sure doxygen is in the executable search path
+c) navigate to ./doc
+d) and run 'doxygen'
+
+Open the generated HTML (AssimpDoc_Html/index.html) in the browser of your choice.
+Windows only: To generate the CHM doc, install 'Microsoft HTML Workshop'
+and configure the path to it in the DOXYFILE first. 
+
+------------------------------
+Building Assimp 
+------------------------------
+
+More detailed build instructions can be found in the documentation,
+this section is just for the inpatient among you.
+
+CMake is the preferred build system for Assimp. The minimum required version 
+is 2.6. If you don't have it yet, downloads for CMake can be found on
+http://www.cmake.org/. 
+
+For Unix:
+
+1. mkdir build && cd build
+2. cmake .. -G 'Unix Makefiles'
+3. make -j4
+
+For Windows:
+1. Open a command prompt
+2. mkdir build
+3. cd build
+4. cmake ..
+5. cmake --build .
+
+For iOS:
+Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS

+ 24 - 0
Readme.md

@@ -2,6 +2,8 @@ Open Asset Import Library (assimp)
 ==================================
 A library to import and export various 3d-model-formats including scene-post-processing to generate missing render data.
 ### Current project status ###
+[![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp) 
+![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
 [![Linux Build Status](https://travis-ci.org/assimp/assimp.svg)](https://travis-ci.org/assimp/assimp)
 [![Windows Build Status](https://ci.appveyor.com/api/projects/status/tmo433wax6u6cjp4?svg=true)](https://ci.appveyor.com/project/kimkulling/assimp)
 <a href="https://scan.coverity.com/projects/5607">
@@ -178,6 +180,28 @@ And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/a
 Contributions to assimp are highly appreciated. The easiest way to get involved is to submit
 a pull request with your changes against the main repository's `master` branch.
 
+## Contributors
+
+### Code Contributors
+
+This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
+
+<a href="https://github.com/assimp/assimp/graphs/contributors"><img src="https://opencollective.com/assimp/contributors.svg?width=890&button=false" /></a>
+
+### Financial Contributors
+
+Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/assimp/contribute)]
+
+#### Individuals
+
+<a href="https://opencollective.com/assimp"><img src="https://opencollective.com/assimp/individuals.svg?width=890"></a>
+
+#### Organizations
+
+Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/assimp/contribute)]
+
+<a href="https://opencollective.com/assimp/organization/0/website"><img src="https://opencollective.com/assimp/organization/0/avatar.svg"></a>
+
 ### License ###
 Our license is based on the modified, __3-clause BSD__-License.
 

+ 3 - 3
appveyor.yml

@@ -17,10 +17,10 @@ matrix:
     
 image:
   - Visual Studio 2013
-  - Visual Studio 2015
-  - Visual Studio 2017
+  #- Visual Studio 2015
+  #- Visual Studio 2017
   - Visual Studio 2019
-  - MinGW  
+  #- MinGW  
     
 platform:
   - Win32

+ 2 - 4
assimp.pc.in

@@ -1,7 +1,5 @@
-prefix=@CMAKE_INSTALL_PREFIX@
-exec_prefix=@CMAKE_INSTALL_PREFIX@/
-libdir=@CMAKE_INSTALL_PREFIX@/@ASSIMP_LIB_INSTALL_DIR@
-includedir=@CMAKE_INSTALL_PREFIX@/../include/@ASSIMP_INCLUDE_INSTALL_DIR@
+libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
 
 Name: @CMAKE_PROJECT_NAME@
 Description: Import various well-known 3D model formats in an uniform manner.

+ 18 - 15
assimpTargets-debug.cmake.in

@@ -7,6 +7,8 @@ set(CMAKE_IMPORT_FILE_VERSION 1)
 
 set(ASSIMP_BUILD_SHARED_LIBS @BUILD_SHARED_LIBS@)
 
+get_property(LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS)
+
 if(MSVC)
   if(MSVC_TOOLSET_VERSION)
     set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
@@ -35,8 +37,6 @@ if(MSVC)
   endif()
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
 
-  file(TO_NATIVE_PATH ${_IMPORT_PREFIX} _IMPORT_PREFIX)
-
   if(ASSIMP_BUILD_SHARED_LIBS)
     set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@")
     set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_IMPORT_LIBRARY_SUFFIX@")
@@ -44,22 +44,22 @@ if(MSVC)
     # Import target "assimp::assimp" for configuration "Debug"
     set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
     set_target_properties(assimp::assimp PROPERTIES
-      IMPORTED_IMPLIB_DEBUG "${_IMPORT_PREFIX}/lib/${importLibraryName}"
-      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
+      IMPORTED_IMPLIB_DEBUG "@CMAKE_INSTALL_FULL_LIBDIR@/${importLibraryName}"
+      IMPORTED_LOCATION_DEBUG "@CMAKE_INSTALL_FULL_BINDIR@/${sharedLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${importLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_BINDIR@/${sharedLibraryName}" )
   else()
     set(staticLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
 
     # Import target "assimp::assimp" for configuration "Debug"
     set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
     set_target_properties(assimp::assimp PROPERTIES
-      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+      IMPORTED_LOCATION_DEBUG "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}")
   endif()
 
 else()
@@ -73,25 +73,28 @@ else()
     else()
       set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
     endif()
+
+    # Import target "assimp::assimp" for configuration "Debug"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
     set_target_properties(assimp::assimp PROPERTIES
       IMPORTED_SONAME_DEBUG "${sharedLibraryName}"
-      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
+      IMPORTED_LOCATION_DEBUG "@CMAKE_INSTALL_FULL_LIBDIR@/${sharedLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${sharedLibraryName}" )
   else()
     set(staticLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
+
+    # Import target "assimp::assimp" for configuration "Debug"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
     set_target_properties(assimp::assimp PROPERTIES
-      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+      IMPORTED_LOCATION_DEBUG "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}" )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}" )
   endif()
 endif()
 
-
-
-
 # Commands beyond this point should not need to know the version.
 set(CMAKE_IMPORT_FILE_VERSION)
 

+ 18 - 13
assimpTargets-release.cmake.in

@@ -7,6 +7,8 @@ set(CMAKE_IMPORT_FILE_VERSION 1)
 
 set(ASSIMP_BUILD_SHARED_LIBS @BUILD_SHARED_LIBS@)
 
+get_property(LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS)
+
 if(MSVC)
   if(MSVC_TOOLSET_VERSION)
     set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
@@ -34,8 +36,6 @@ if(MSVC)
     endif()
   endif()
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
-  	
-  file(TO_NATIVE_PATH ${_IMPORT_PREFIX} _IMPORT_PREFIX)
 
   if(ASSIMP_BUILD_SHARED_LIBS)
     set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@")
@@ -44,22 +44,22 @@ if(MSVC)
     # Import target "assimp::assimp" for configuration "Release"
     set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
     set_target_properties(assimp::assimp PROPERTIES
-      IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/${importLibraryName}"
-      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
+      IMPORTED_IMPLIB_RELEASE "@CMAKE_INSTALL_FULL_LIBDIR@/${importLibraryName}"
+      IMPORTED_LOCATION_RELEASE "@CMAKE_INSTALL_FULL_BINDIR@/${sharedLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${importLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_BINDIR@/${sharedLibraryName}" )
   else()
     set(staticLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_STATIC_LIBRARY_SUFFIX@")
 
     # Import target "assimp::assimp" for configuration "Release"
     set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
     set_target_properties(assimp::assimp PROPERTIES
-      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+      IMPORTED_LOCATION_RELEASE "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}")
   endif()
 
 else()
@@ -73,20 +73,25 @@ else()
     else()
       set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
     endif()
+
+    # Import target "assimp::assimp" for configuration "Release"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
     set_target_properties(assimp::assimp PROPERTIES
       IMPORTED_SONAME_RELEASE "${sharedLibraryName}"
-
-      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
+      IMPORTED_LOCATION_RELEASE "@CMAKE_INSTALL_FULL_LIBDIR@/${sharedLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${sharedLibraryName}" )
   else()
     set(staticLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_STATIC_LIBRARY_SUFFIX@")
+
+    # Import target "assimp::assimp" for configuration "Release"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
     set_target_properties(assimp::assimp PROPERTIES
-      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+      IMPORTED_LOCATION_RELEASE "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
     )
     list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}" )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}" )
   endif()
 endif()
 

+ 1 - 14
assimpTargets.cmake.in

@@ -43,23 +43,13 @@ unset(_targetsDefined)
 unset(_targetsNotDefined)
 unset(_expectedTargets)
 
-
-# Compute the installation prefix relative to this file.
-get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
-get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
-get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
-get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
-if(_IMPORT_PREFIX STREQUAL "/")
-  set(_IMPORT_PREFIX "")
-endif()
-
 # Create imported target assimp::assimp
 add_library(assimp::assimp @BUILD_LIB_TYPE@ IMPORTED)
 
 set_target_properties(assimp::assimp PROPERTIES
   COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION"
   INTERFACE_assimp_MAJOR_VERSION "1"
-  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include;${_IMPORT_PREFIX}/include"
+  INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_FULL_INCLUDEDIR@"
   #INTERFACE_LINK_LIBRARIES "TxtUtils::TxtUtils;MealyMachine::MealyMachine"
 )
 
@@ -74,9 +64,6 @@ foreach(f ${CONFIG_FILES})
   include(${f})
 endforeach()
 
-# Cleanup temporary variables.
-set(_IMPORT_PREFIX)
-
 # Loop over all imported files and verify that they actually exist
 foreach(target ${_IMPORT_CHECK_TARGETS} )
   foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )

+ 0 - 701
code/AMF/AMFImporter.cpp

@@ -1,701 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2020, assimp team
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
-copyright notice, this list of conditions and the
-following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the
-following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-contributors may be used to endorse or promote products
-derived from this software without specific prior
-written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/// \file AMFImporter.cpp
-/// \brief AMF-format files importer for Assimp: main algorithm implementation.
-/// \date 2016
-/// \author [email protected]
-
-#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-
-// Header files, Assimp.
-#include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
-
-#include <assimp/DefaultIOSystem.h>
-#include <assimp/fast_atof.h>
-
-#include <memory>
-
-namespace Assimp {
-
-/// \var aiImporterDesc AMFImporter::Description
-/// Constant which hold importer description
-const aiImporterDesc AMFImporter::Description = {
-	"Additive manufacturing file format(AMF) Importer",
-	"smalcom",
-	"",
-	"See documentation in source code. Chapter: Limitations.",
-	aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
-	0,
-	0,
-	0,
-	0,
-	"amf"
-};
-
-void AMFImporter::Clear() {
-	mNodeElement_Cur = nullptr;
-	mUnit.clear();
-	mMaterial_Converted.clear();
-	mTexture_Converted.clear();
-	// Delete all elements
-	if (!mNodeElement_List.empty()) {
-		for (AMFNodeElementBase *ne : mNodeElement_List) {
-			delete ne;
-		}
-
-		mNodeElement_List.clear();
-	}
-}
-
-AMFImporter::~AMFImporter() {
-	if (mXmlParser != nullptr) {
-		delete mXmlParser;
-		mXmlParser = nullptr;
-	}
-
-	// Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
-	Clear();
-}
-
-void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector<uint8_t> &pOutputData) const {
-	// With help from
-	// René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
-	const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-	uint8_t tidx = 0;
-	uint8_t arr4[4], arr3[3];
-
-	// check input data
-	if (pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
-	// prepare output place
-	pOutputData.clear();
-	pOutputData.reserve(pInputBase64.size() / 4 * 3);
-
-	for (size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--) {
-		if (ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) {
-			arr4[tidx++] = pInputBase64[in_idx++];
-			if (tidx == 4) {
-				for (tidx = 0; tidx < 4; tidx++)
-					arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
-
-				arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
-				arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
-				arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
-				for (tidx = 0; tidx < 3; tidx++)
-					pOutputData.push_back(arr3[tidx]);
-
-				tidx = 0;
-			} // if(tidx == 4)
-		} // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
-		else {
-			in_idx++;
-		} // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
-	}
-
-	if (tidx) {
-		for (uint8_t i = tidx; i < 4; i++)
-			arr4[i] = 0;
-		for (uint8_t i = 0; i < 4; i++)
-			arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
-
-		arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
-		arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
-		arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
-		for (uint8_t i = 0; i < (tidx - 1); i++)
-			pOutputData.push_back(arr3[i]);
-	}
-}
-
-void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) {
-	std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
-
-	// Check whether we can read from the file
-	if (file.get() == nullptr) {
-		throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
-	}
-
-	mXmlParser = new XmlParser;
-	XmlNode *root = mXmlParser->parse(file.get());
-	if (nullptr == root) {
-		throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
-	}
-
-	// start reading
-	// search for root tag <amf>
-
-	if (!root->find_child("amf")) {
-		throw DeadlyImportError("Root node \"amf\" not found.");
-	}
-
-	ParseNode_Root(*root);
-
-	delete mXmlParser;
-	mXmlParser = nullptr;
-}
-
-// <amf
-// unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
-// version="" - Version of file format.
-// >
-// </amf>
-// Root XML element.
-// Multi elements - No.
-void AMFImporter::ParseNode_Root(XmlNode &root) {
-	std::string unit, version;
-	AMFNodeElementBase *ne(nullptr);
-
-	// Read attributes for node <amf>.
-	for (pugi::xml_attribute_iterator ait = root.attributes_begin(); ait != root.attributes_end(); ++ait) {
-		if (ait->name() == "unit") {
-			unit = ait->as_string();
-		} else if (ait->name() == "version") {
-			version = ait->as_string();
-		}
-	}
-
-	// Check attributes
-	if (!mUnit.empty()) {
-		if ((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) {
-			throw DeadlyImportError("Root node does not contain any units.");
-		}
-	}
-
-	// create root node element.
-	ne = new AMFRoot(nullptr);
-
-	// set first "current" element
-	mNodeElement_Cur = ne;
-
-	// and assign attributes values
-	((AMFRoot *)ne)->Unit = unit;
-	((AMFRoot *)ne)->Version = version;
-
-	// Check for child nodes
-	for (pugi::xml_node child : node->children()) {
-		if (child.name() == "object") {
-			ParseNode_Object(child);
-		} else if (child.name() == "material") {
-			ParseNode_Material(child);
-		} else if (child.name() == "texture") {
-			ParseNode_Texture(child);
-		} else if (child.name() == "constellation") {
-			ParseNode_Constellation(child);
-		} else if (child.name() == "metadata") {
-			ParseNode_Metadata(child);
-		}
-	}
-	mNodeElement_List.push_back(ne); // add to node element list because its a new object in graph.
-}
-
-// <constellation
-// id="" - The Object ID of the new constellation being defined.
-// >
-// </constellation>
-// A collection of objects or constellations with specific relative locations.
-// Multi elements - Yes.
-// Parent element - <amf>.
-void AMFImporter::ParseNode_Constellation(XmlNode &root) {
-	std::string id = root.attribute("id").as_string();
-
-	// create and if needed - define new grouping object.
-	AMFNodeElementBase *ne = new AMFConstellation(mNodeElement_Cur);
-
-	AMFConstellation &als = *((AMFConstellation *)ne); // alias for convenience
-
-	for (pugi::xml_node &child : root.children()) {
-		if (child.name() == "instance") {
-			ParseNode_Instance(child);
-		} else if (child.name() == "metadata") {
-			ParseNode_Metadata(child);
-		}
-	}
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-// <instance
-// objectid="" - The Object ID of the new constellation being defined.
-// >
-// </instance>
-// A collection of objects or constellations with specific relative locations.
-// Multi elements - Yes.
-// Parent element - <amf>.
-void AMFImporter::ParseNode_Instance(XmlNode &root) {
-	std::string objectid = root.attribute("objectid").as_string();
-
-        // used object id must be defined, check that.
-	if (objectid.empty()) {
-		throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
-	}
-	// create and define new grouping object.
-	AMFNodeElementBase *ne = new AMFInstance(mNodeElement_Cur);
-
-	AMFInstance &als = *((AMFInstance *)ne); // alias for convenience
-
-	als.ObjectID = objectid;
-	// Check for child nodes
-	if (!mXmlParser->isEmptyElement()) {
-		bool read_flag[6] = { false, false, false, false, false, false };
-
-		als.Delta.Set(0, 0, 0);
-		als.Rotation.Set(0, 0, 0);
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("instance");
-		MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
-		MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
-		MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
-		MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
-		MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
-		MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
-		MACRO_NODECHECK_LOOPEND("instance");
-		ParseHelper_Node_Exit();
-		// also convert degrees to radians.
-		als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
-		als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
-		als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
-	} // if(!mReader->isEmptyElement())
-	else {
-		mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-	} // if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-// <object
-// id="" - A unique ObjectID for the new object being defined.
-// >
-// </object>
-// An object definition.
-// Multi elements - Yes.
-// Parent element - <amf>.
-void AMFImporter::ParseNode_Object(XmlNode &node) {
-
-	std::string id;
-	for (pugi::xml_attribute_iterator ait = node.attributes_begin(); ait != node.attributes_end(); ++ait) {
-		if (ait->name() == "id") {
-			id = ait->as_string();
-		}
-	}
-	// Read attributes for node <object>.
-
-	// create and if needed - define new geometry object.
-	AMFNodeElementBase *ne = new AMFObject(mNodeElement_Cur);
-
-	AMFObject &als = *((AMFObject *)ne); // alias for convenience
-
-	if (!id.empty()) {
-		als.ID = id;
-	}
-
-	// Check for child nodes
-	for (pugi::xml_node_iterator it = node.children().begin(); it != node.children->end(); ++it) {
-		bool col_read = false;
-		if (it->name() == "mesh") {
-			ParseNode_Mesh(*it);
-		} else if (it->name() == "metadata") {
-			ParseNode_Metadata(*it);
-		} else if (it->name() == "color") {
-			ParseNode_Color(*it);
-		}
-	}
-
-	mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-}
-
-// <metadata
-// type="" - The type of the attribute.
-// >
-// </metadata>
-// Specify additional information about an entity.
-// Multi elements - Yes.
-// Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
-//
-// Reserved types are:
-// "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
-// "Description" - A description of the content of the entity
-// "URL" - A link to an external resource relating to the entity
-// "Author" - Specifies the name(s) of the author(s) of the entity
-// "Company" - Specifying the company generating the entity
-// "CAD" - specifies the name of the originating CAD software and version
-// "Revision" - specifies the revision of the entity
-// "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
-// "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
-void AMFImporter::ParseNode_Metadata() {
-	std::string type, value;
-	AMFNodeElementBase *ne(nullptr);
-
-	// read attribute
-	MACRO_ATTRREAD_LOOPBEG;
-	MACRO_ATTRREAD_CHECK_RET("type", type, mXmlParser->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-	// and value of node.
-	value = mXmlParser->getNodeData();
-	// Create node element and assign read data.
-	ne = new AMFMetadata(mNodeElement_Cur);
-	((AMFMetadata *)ne)->Type = type;
-	((AMFMetadata *)ne)->Value = value;
-	mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-/*********************************************************************************************************************************************/
-/******************************************************** Functions: BaseImporter set ********************************************************/
-/*********************************************************************************************************************************************/
-
-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.length() || pCheckSig) {
-		const char *tokens[] = { "<amf" };
-
-		return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
-	}
-
-	return false;
-}
-
-void AMFImporter::GetExtensionList(std::set<std::string> &extensionList) {
-	extensionList.insert("amf");
-}
-
-const aiImporterDesc *AMFImporter::GetInfo() const {
-	return &Description;
-}
-
-void AMFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
-	Clear();
-
-	ParseFile(pFile, pIOHandler);
-
-	Postprocess_BuildScene(pScene);
-}
-
-void AMFImporter::ParseNode_Mesh(XmlNode &node) {
-	AMFNodeElementBase *ne;
-
-	if (node.empty()) {
-		return;
-	}
-
-	for (pugi::xml_node &child : node.children()) {
-		if (child.name() == "vertices") {
-			ParseNode_Vertices(child);
-		}
-	}
-	// create new mesh object.
-	ne = new AMFMesh(mNodeElement_Cur);
-	// Check for child nodes
-	if (!mXmlParser->isEmptyElement()) {
-		bool vert_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("mesh");
-		if (XML_CheckNode_NameEqual("vertices")) {
-			// Check if data already defined.
-			if (vert_read) Throw_MoreThanOnceDefined("vertices", "Only one vertices set can be defined for <mesh>.");
-			// read data and set flag about it
-			vert_read = true;
-
-			continue;
-		}
-
-		if (XML_CheckNode_NameEqual("volume")) {
-			ParseNode_Volume();
-			continue;
-		}
-		MACRO_NODECHECK_LOOPEND("mesh");
-		ParseHelper_Node_Exit();
-	} // if(!mReader->isEmptyElement())
-	else {
-		mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-	} // if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-// <vertices>
-// </vertices>
-// The list of vertices to be used in defining triangles.
-// Multi elements - No.
-// Parent element - <mesh>.
-void AMFImporter::ParseNode_Vertices(XmlNode &node) {
-	AMFNodeElementBase *ne = new AMFVertices(mNodeElement_Cur);
-
-	for (pugi::xml_node &child : node.children()) {
-		if (child.name() == "vertices") {
-			ParseNode_Vertex(child);
-		}
-	}
-	// Check for child nodes
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-// <vertex>
-// </vertex>
-// A vertex to be referenced in triangles.
-// Multi elements - Yes.
-// Parent element - <vertices>.
-void AMFImporter::ParseNode_Vertex() {
-	AMFNodeElementBase *ne;
-
-	// create new mesh object.
-	ne = new AMFVertex(mNodeElement_Cur);
-	// Check for child nodes
-	if (!mXmlParser->isEmptyElement()) {
-		bool col_read = false;
-		bool coord_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("vertex");
-		if (XML_CheckNode_NameEqual("color")) {
-			// Check if data already defined.
-			if (col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <vertex>.");
-			// read data and set flag about it
-			ParseNode_Color();
-			col_read = true;
-
-			continue;
-		}
-
-		if (XML_CheckNode_NameEqual("coordinates")) {
-			// Check if data already defined.
-			if (coord_read) Throw_MoreThanOnceDefined("coordinates", "Only one coordinates set can be defined for <vertex>.");
-			// read data and set flag about it
-			ParseNode_Coordinates();
-			coord_read = true;
-
-			continue;
-		}
-
-		if (XML_CheckNode_NameEqual("metadata")) {
-			ParseNode_Metadata();
-			continue;
-		}
-		MACRO_NODECHECK_LOOPEND("vertex");
-		ParseHelper_Node_Exit();
-	} // if(!mReader->isEmptyElement())
-	else {
-		mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-	} // if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-// <coordinates>
-// </coordinates>
-// Specifies the 3D location of this vertex.
-// Multi elements - No.
-// Parent element - <vertex>.
-//
-// Children elements:
-//   <x>, <y>, <z>
-//   Multi elements - No.
-//   X, Y, or Z coordinate, respectively, of a vertex position in space.
-void AMFImporter::ParseNode_Coordinates() {
-	AMFNodeElementBase *ne;
-
-	// create new color object.
-	ne = new AMFCoordinates(mNodeElement_Cur);
-
-	AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
-
-	// Check for child nodes
-	if (!mXmlParser->isEmptyElement()) {
-		bool read_flag[3] = { false, false, false };
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("coordinates");
-		MACRO_NODECHECK_READCOMP_F("x", read_flag[0], als.Coordinate.x);
-		MACRO_NODECHECK_READCOMP_F("y", read_flag[1], als.Coordinate.y);
-		MACRO_NODECHECK_READCOMP_F("z", read_flag[2], als.Coordinate.z);
-		MACRO_NODECHECK_LOOPEND("coordinates");
-		ParseHelper_Node_Exit();
-		// check that all components was defined
-		if ((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all coordinate's components are defined.");
-
-	} // if(!mReader->isEmptyElement())
-	else {
-		mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-	} // if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-// <volume
-// materialid="" - Which material to use.
-// type=""       - What this volume describes can be “region” or “support”. If none specified, “object” is assumed. If support, then the geometric
-//                 requirements 1-8 listed in section 5 do not need to be maintained.
-// >
-// </volume>
-// Defines a volume from the established vertex list.
-// Multi elements - Yes.
-// Parent element - <mesh>.
-void AMFImporter::ParseNode_Volume() {
-	std::string materialid;
-	std::string type;
-	AMFNodeElementBase *ne;
-
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-	MACRO_ATTRREAD_CHECK_RET("materialid", materialid, mXmlParser->getAttributeValue);
-	MACRO_ATTRREAD_CHECK_RET("type", type, mXmlParser->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new object.
-	ne = new AMFVolume(mNodeElement_Cur);
-	// and assign read data
-	((AMFVolume *)ne)->MaterialID = materialid;
-	((AMFVolume *)ne)->Type = type;
-	// Check for child nodes
-	if (!mXmlParser->isEmptyElement()) {
-		bool col_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("volume");
-		if (XML_CheckNode_NameEqual("color")) {
-			// Check if data already defined.
-			if (col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <volume>.");
-			// read data and set flag about it
-			ParseNode_Color();
-			col_read = true;
-
-			continue;
-		}
-
-		if (XML_CheckNode_NameEqual("triangle")) {
-			ParseNode_Triangle();
-			continue;
-		}
-		if (XML_CheckNode_NameEqual("metadata")) {
-			ParseNode_Metadata();
-			continue;
-		}
-		MACRO_NODECHECK_LOOPEND("volume");
-		ParseHelper_Node_Exit();
-	} // if(!mReader->isEmptyElement())
-	else {
-		mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-	} // if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-// <triangle>
-// </triangle>
-// Defines a 3D triangle from three vertices, according to the right-hand rule (counter-clockwise when looking from the outside).
-// Multi elements - Yes.
-// Parent element - <volume>.
-//
-// Children elements:
-//   <v1>, <v2>, <v3>
-//   Multi elements - No.
-//   Index of the desired vertices in a triangle or edge.
-void AMFImporter::ParseNode_Triangle() {
-	AMFNodeElementBase *ne;
-
-	// create new color object.
-	ne = new AMFTriangle(mNodeElement_Cur);
-
-	AMFTriangle &als = *((AMFTriangle *)ne); // alias for convenience
-
-	// Check for child nodes
-	if (!mXmlParser->isEmptyElement()) {
-		bool col_read = false, tex_read = false;
-		bool read_flag[3] = { false, false, false };
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("triangle");
-		if (XML_CheckNode_NameEqual("color")) {
-			// Check if data already defined.
-			if (col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <triangle>.");
-			// read data and set flag about it
-			ParseNode_Color();
-			col_read = true;
-
-			continue;
-		}
-
-		if (XML_CheckNode_NameEqual("texmap")) // new name of node: "texmap".
-		{
-			// Check if data already defined.
-			if (tex_read) Throw_MoreThanOnceDefined("texmap", "Only one texture coordinate can be defined for <triangle>.");
-			// read data and set flag about it
-			ParseNode_TexMap();
-			tex_read = true;
-
-			continue;
-		} else if (XML_CheckNode_NameEqual("map")) // old name of node: "map".
-		{
-			// Check if data already defined.
-			if (tex_read) Throw_MoreThanOnceDefined("map", "Only one texture coordinate can be defined for <triangle>.");
-			// read data and set flag about it
-			ParseNode_TexMap(true);
-			tex_read = true;
-
-			continue;
-		}
-
-		//		MACRO_NODECHECK_READCOMP_U32("v1", read_flag[0], als.V[0]);
-		//		MACRO_NODECHECK_READCOMP_U32("v2", read_flag[1], als.V[1]);
-		//		MACRO_NODECHECK_READCOMP_U32("v3", read_flag[2], als.V[2]);
-		//		MACRO_NODECHECK_LOOPEND("triangle");
-		ParseHelper_Node_Exit();
-		// check that all components was defined
-		if ((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all vertices of the triangle are defined.");
-
-	} // if(!mReader->isEmptyElement())
-	else {
-		mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-	} // if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
-}
-
-} // namespace Assimp
-
-#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 0 - 847
code/Assbin/AssbinExporter.cpp

@@ -1,847 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2020, assimp team
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the
-following conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-/** @file  AssbinExporter.cpp
- *  ASSBIN exporter main code
- */
-
-#ifndef ASSIMP_BUILD_NO_EXPORT
-#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
-
-#include "Common/assbin_chunks.h"
-#include "PostProcessing/ProcessHelper.h"
-
-#include <assimp/version.h>
-#include <assimp/IOStream.hpp>
-#include <assimp/IOSystem.hpp>
-#include <assimp/Exporter.hpp>
-#include <assimp/Exceptional.h>
-
-#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#   include <zlib.h>
-#else
-#   include "../contrib/zlib/zlib.h"
-#endif
-
-#include <time.h>
-
-namespace Assimp {
-
-template <typename T>
-size_t Write(IOStream * stream, const T& v) {
-    return stream->Write( &v, sizeof(T), 1 );
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an aiString
-template <>
-inline
-size_t Write<aiString>(IOStream * stream, const aiString& s) {
-    const size_t s2 = (uint32_t)s.length;
-    stream->Write(&s,4,1);
-    stream->Write(s.data,s2,1);
-
-    return s2+4;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an unsigned int as uint32_t
-template <>
-inline
-size_t Write<unsigned int>(IOStream * stream, const unsigned int& w) {
-    const uint32_t t = (uint32_t)w;
-    if (w > t) {
-        // this shouldn't happen, integers in Assimp data structures never exceed 2^32
-        throw DeadlyExportError("loss of data due to 64 -> 32 bit integer conversion");
-    }
-
-    stream->Write(&t,4,1);
-
-    return 4;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an unsigned int as uint16_t
-template <>
-inline
-size_t Write<uint16_t>(IOStream * stream, const uint16_t& w) {
-    static_assert(sizeof(uint16_t)==2, "sizeof(uint16_t)==2");
-    stream->Write(&w,2,1);
-
-    return 2;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a float
-template <>
-inline
-size_t Write<float>(IOStream * stream, const float& f) {
-    static_assert(sizeof(float)==4, "sizeof(float)==4");
-    stream->Write(&f,4,1);
-
-    return 4;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a double
-template <>
-inline
-size_t Write<double>(IOStream * stream, const double& f) {
-    static_assert(sizeof(double)==8, "sizeof(double)==8");
-    stream->Write(&f,8,1);
-
-    return 8;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a vec3
-template <>
-inline
-size_t Write<aiVector3D>(IOStream * stream, const aiVector3D& v) {
-    size_t t = Write<float>(stream,v.x);
-    t += Write<float>(stream,v.y);
-    t += Write<float>(stream,v.z);
-
-    return t;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a color value
-template <>
-inline
-size_t Write<aiColor3D>(IOStream * stream, const aiColor3D& v) {
-    size_t t = Write<float>(stream,v.r);
-    t += Write<float>(stream,v.g);
-    t += Write<float>(stream,v.b);
-
-    return t;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a color value
-template <>
-inline
-size_t Write<aiColor4D>(IOStream * stream, const aiColor4D& v) {
-    size_t t = Write<float>(stream,v.r);
-    t += Write<float>(stream,v.g);
-    t += Write<float>(stream,v.b);
-    t += Write<float>(stream,v.a);
-
-    return t;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a quaternion
-template <>
-inline
-size_t Write<aiQuaternion>(IOStream * stream, const aiQuaternion& v) {
-    size_t t = Write<float>(stream,v.w);
-    t += Write<float>(stream,v.x);
-    t += Write<float>(stream,v.y);
-    t += Write<float>(stream,v.z);
-    ai_assert(t == 16);
-
-    return 16;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a vertex weight
-template <>
-inline
-size_t Write<aiVertexWeight>(IOStream * stream, const aiVertexWeight& v) {
-    size_t t = Write<unsigned int>(stream,v.mVertexId);
-
-    return t+Write<float>(stream,v.mWeight);
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a mat4x4
-template <>
-inline
-size_t Write<aiMatrix4x4>(IOStream * stream, const aiMatrix4x4& m) {
-    for (unsigned int i = 0; i < 4;++i) {
-        for (unsigned int i2 = 0; i2 < 4;++i2) {
-            Write<float>(stream,m[i][i2]);
-        }
-    }
-
-    return 64;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an aiVectorKey
-template <>
-inline
-size_t Write<aiVectorKey>(IOStream * stream, const aiVectorKey& v) {
-    const size_t t = Write<double>(stream,v.mTime);
-    return t + Write<aiVector3D>(stream,v.mValue);
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an aiQuatKey
-template <>
-inline
-size_t Write<aiQuatKey>(IOStream * stream, const aiQuatKey& v) {
-    const size_t t = Write<double>(stream,v.mTime);
-    return t + Write<aiQuaternion>(stream,v.mValue);
-}
-
-template <typename T>
-inline
-size_t WriteBounds(IOStream * stream, const T* in, unsigned int size) {
-    T minc, maxc;
-    ArrayBounds(in,size,minc,maxc);
-
-    const size_t t = Write<T>(stream,minc);
-    return t + Write<T>(stream,maxc);
-}
-
-// We use this to write out non-byte arrays so that we write using the specializations.
-// This way we avoid writing out extra bytes that potentially come from struct alignment.
-template <typename T>
-inline
-size_t WriteArray(IOStream * stream, const T* in, unsigned int size) {
-    size_t n = 0;
-    for (unsigned int i=0; i<size; i++) n += Write<T>(stream,in[i]);
-
-    return n;
-}
-
-// ----------------------------------------------------------------------------------
-/** @class  AssbinChunkWriter
- *  @brief  Chunk writer mechanism for the .assbin file structure
- *
- *  This is a standard in-memory IOStream (most of the code is based on BlobIOStream),
- *  the difference being that this takes another IOStream as a "container" in the
- *  constructor, and when it is destroyed, it appends the magic number, the chunk size,
- *  and the chunk contents to the container stream. This allows relatively easy chunk
- *  chunk construction, even recursively.
- */
-class AssbinChunkWriter : public IOStream
-{
-private:
-
-    uint8_t* buffer;
-    uint32_t magic;
-    IOStream * container;
-    size_t cur_size, cursor, initial;
-
-private:
-    // -------------------------------------------------------------------
-    void Grow(size_t need = 0)
-    {
-        size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) ));
-
-        const uint8_t* const old = buffer;
-        buffer = new uint8_t[new_size];
-
-        if (old) {
-            memcpy(buffer,old,cur_size);
-            delete[] old;
-        }
-
-        cur_size = new_size;
-    }
-
-public:
-
-    AssbinChunkWriter( IOStream * container, uint32_t magic, size_t initial = 4096)
-        : buffer(NULL), magic(magic), container(container), cur_size(0), cursor(0), initial(initial)
-    {
-    }
-
-    virtual ~AssbinChunkWriter()
-    {
-        if (container) {
-            container->Write( &magic, sizeof(uint32_t), 1 );
-            container->Write( &cursor, sizeof(uint32_t), 1 );
-            container->Write( buffer, 1, cursor );
-        }
-        if (buffer) delete[] buffer;
-    }
-
-    void * GetBufferPointer() { return buffer; }
-
-    // -------------------------------------------------------------------
-    virtual size_t Read(void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) {
-        return 0;
-    }
-    virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
-        return aiReturn_FAILURE;
-    }
-    virtual size_t Tell() const {
-        return cursor;
-    }
-    virtual void Flush() {
-        // not implemented
-    }
-
-    virtual size_t FileSize() const {
-        return cursor;
-    }
-
-    // -------------------------------------------------------------------
-    virtual size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {
-        pSize *= pCount;
-        if (cursor + pSize > cur_size) {
-            Grow(cursor + pSize);
-        }
-
-        memcpy(buffer+cursor, pvBuffer, pSize);
-        cursor += pSize;
-
-        return pCount;
-    }
-
-};
-
-// ----------------------------------------------------------------------------------
-/** @class  AssbinExport
- *  @brief  Assbin exporter class
- *
- *  This class performs the .assbin exporting, and is responsible for the file layout.
- */
-class AssbinExport
-{
-private:
-    bool shortened;
-    bool compressed;
-
-protected:
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryNode( IOStream * container, const aiNode* node)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODE );
-
-        unsigned int nb_metadata = (node->mMetaData != NULL ? node->mMetaData->mNumProperties : 0);
-
-        Write<aiString>(&chunk,node->mName);
-        Write<aiMatrix4x4>(&chunk,node->mTransformation);
-        Write<unsigned int>(&chunk,node->mNumChildren);
-        Write<unsigned int>(&chunk,node->mNumMeshes);
-        Write<unsigned int>(&chunk,nb_metadata);
-
-        for (unsigned int i = 0; i < node->mNumMeshes;++i) {
-            Write<unsigned int>(&chunk,node->mMeshes[i]);
-        }
-
-        for (unsigned int i = 0; i < node->mNumChildren;++i) {
-            WriteBinaryNode( &chunk, node->mChildren[i] );
-        }
-
-        for (unsigned int i = 0; i < nb_metadata; ++i) {
-            const aiString& key = node->mMetaData->mKeys[i];
-            aiMetadataType type = node->mMetaData->mValues[i].mType;
-            void* value = node->mMetaData->mValues[i].mData;
-
-            Write<aiString>(&chunk, key);
-            Write<uint16_t>(&chunk, type);
-
-            switch (type) {
-                case AI_BOOL:
-                    Write<bool>(&chunk, *((bool*) value));
-                    break;
-                case AI_INT32:
-                    Write<int32_t>(&chunk, *((int32_t*) value));
-                    break;
-                case AI_UINT64:
-                    Write<uint64_t>(&chunk, *((uint64_t*) value));
-                    break;
-                case AI_FLOAT:
-                    Write<float>(&chunk, *((float*) value));
-                    break;
-                case AI_DOUBLE:
-                    Write<double>(&chunk, *((double*) value));
-                    break;
-                case AI_AISTRING:
-                    Write<aiString>(&chunk, *((aiString*) value));
-                    break;
-                case AI_AIVECTOR3D:
-                    Write<aiVector3D>(&chunk, *((aiVector3D*) value));
-                    break;
-#ifdef SWIG
-                case FORCE_32BIT:
-#endif // SWIG
-                default:
-                    break;
-            }
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryTexture(IOStream * container, const aiTexture* tex)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AITEXTURE );
-
-        Write<unsigned int>(&chunk,tex->mWidth);
-        Write<unsigned int>(&chunk,tex->mHeight);
-        // Write the texture format, but don't include the null terminator.
-        chunk.Write( tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1 );
-
-        if(!shortened) {
-            if (!tex->mHeight) {
-                chunk.Write(tex->pcData,1,tex->mWidth);
-            }
-            else {
-                chunk.Write(tex->pcData,1,tex->mWidth*tex->mHeight*4);
-            }
-        }
-
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryBone(IOStream * container, const aiBone* b)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIBONE );
-
-        Write<aiString>(&chunk,b->mName);
-        Write<unsigned int>(&chunk,b->mNumWeights);
-        Write<aiMatrix4x4>(&chunk,b->mOffsetMatrix);
-
-        // for the moment we write dumb min/max values for the bones, too.
-        // maybe I'll add a better, hash-like solution later
-        if (shortened) {
-            WriteBounds(&chunk,b->mWeights,b->mNumWeights);
-        } // else write as usual
-        else WriteArray<aiVertexWeight>(&chunk,b->mWeights,b->mNumWeights);
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryMesh(IOStream * container, const aiMesh* mesh)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMESH );
-
-        Write<unsigned int>(&chunk,mesh->mPrimitiveTypes);
-        Write<unsigned int>(&chunk,mesh->mNumVertices);
-        Write<unsigned int>(&chunk,mesh->mNumFaces);
-        Write<unsigned int>(&chunk,mesh->mNumBones);
-        Write<unsigned int>(&chunk,mesh->mMaterialIndex);
-
-        // first of all, write bits for all existent vertex components
-        unsigned int c = 0;
-        if (mesh->mVertices) {
-            c |= ASSBIN_MESH_HAS_POSITIONS;
-        }
-        if (mesh->mNormals) {
-            c |= ASSBIN_MESH_HAS_NORMALS;
-        }
-        if (mesh->mTangents && mesh->mBitangents) {
-            c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS;
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
-            if (!mesh->mTextureCoords[n]) {
-                break;
-            }
-            c |= ASSBIN_MESH_HAS_TEXCOORD(n);
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
-            if (!mesh->mColors[n]) {
-                break;
-            }
-            c |= ASSBIN_MESH_HAS_COLOR(n);
-        }
-        Write<unsigned int>(&chunk,c);
-
-        aiVector3D minVec, maxVec;
-        if (mesh->mVertices) {
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mVertices,mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiVector3D>(&chunk,mesh->mVertices,mesh->mNumVertices);
-        }
-        if (mesh->mNormals) {
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mNormals,mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiVector3D>(&chunk,mesh->mNormals,mesh->mNumVertices);
-        }
-        if (mesh->mTangents && mesh->mBitangents) {
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mTangents,mesh->mNumVertices);
-                WriteBounds(&chunk,mesh->mBitangents,mesh->mNumVertices);
-            } // else write as usual
-            else {
-                WriteArray<aiVector3D>(&chunk,mesh->mTangents,mesh->mNumVertices);
-                WriteArray<aiVector3D>(&chunk,mesh->mBitangents,mesh->mNumVertices);
-            }
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
-            if (!mesh->mColors[n])
-                break;
-
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mColors[n],mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiColor4D>(&chunk,mesh->mColors[n],mesh->mNumVertices);
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
-            if (!mesh->mTextureCoords[n])
-                break;
-
-            // write number of UV components
-            Write<unsigned int>(&chunk,mesh->mNumUVComponents[n]);
-
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiVector3D>(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices);
-        }
-
-        // write faces. There are no floating-point calculations involved
-        // in these, so we can write a simple hash over the face data
-        // to the dump file. We generate a single 32 Bit hash for 512 faces
-        // using Assimp's standard hashing function.
-        if (shortened) {
-            unsigned int processed = 0;
-            for (unsigned int job;(job = std::min(mesh->mNumFaces-processed,512u));processed += job) {
-
-                uint32_t hash = 0;
-                for (unsigned int a = 0; a < job;++a) {
-
-                    const aiFace& f = mesh->mFaces[processed+a];
-                    uint32_t tmp = f.mNumIndices;
-                    hash = SuperFastHash(reinterpret_cast<const char*>(&tmp),sizeof tmp,hash);
-                    for (unsigned int i = 0; i < f.mNumIndices; ++i) {
-                        static_assert(AI_MAX_VERTICES <= 0xffffffff, "AI_MAX_VERTICES <= 0xffffffff");
-                        tmp = static_cast<uint32_t>( f.mIndices[i] );
-                        hash = SuperFastHash(reinterpret_cast<const char*>(&tmp),sizeof tmp,hash);
-                    }
-                }
-                Write<unsigned int>(&chunk,hash);
-            }
-        }
-        else // else write as usual
-        {
-            // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
-            for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
-                const aiFace& f = mesh->mFaces[i];
-
-                static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
-                Write<uint16_t>(&chunk,f.mNumIndices);
-
-                for (unsigned int a = 0; a < f.mNumIndices;++a) {
-                    if (mesh->mNumVertices < (1u<<16)) {
-                        Write<uint16_t>(&chunk,f.mIndices[a]);
-                    }
-                    else Write<unsigned int>(&chunk,f.mIndices[a]);
-                }
-            }
-        }
-
-        // write bones
-        if (mesh->mNumBones) {
-            for (unsigned int a = 0; a < mesh->mNumBones;++a) {
-                const aiBone* b = mesh->mBones[a];
-                WriteBinaryBone(&chunk,b);
-            }
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryMaterialProperty(IOStream * container, const aiMaterialProperty* prop)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMATERIALPROPERTY );
-
-        Write<aiString>(&chunk,prop->mKey);
-        Write<unsigned int>(&chunk,prop->mSemantic);
-        Write<unsigned int>(&chunk,prop->mIndex);
-
-        Write<unsigned int>(&chunk,prop->mDataLength);
-        Write<unsigned int>(&chunk,(unsigned int)prop->mType);
-        chunk.Write(prop->mData,1,prop->mDataLength);
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryMaterial(IOStream * container, const aiMaterial* mat)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMATERIAL);
-
-        Write<unsigned int>(&chunk,mat->mNumProperties);
-        for (unsigned int i = 0; i < mat->mNumProperties;++i) {
-            WriteBinaryMaterialProperty( &chunk, mat->mProperties[i]);
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryNodeAnim(IOStream * container, const aiNodeAnim* nd)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODEANIM );
-
-        Write<aiString>(&chunk,nd->mNodeName);
-        Write<unsigned int>(&chunk,nd->mNumPositionKeys);
-        Write<unsigned int>(&chunk,nd->mNumRotationKeys);
-        Write<unsigned int>(&chunk,nd->mNumScalingKeys);
-        Write<unsigned int>(&chunk,nd->mPreState);
-        Write<unsigned int>(&chunk,nd->mPostState);
-
-        if (nd->mPositionKeys) {
-            if (shortened) {
-                WriteBounds(&chunk,nd->mPositionKeys,nd->mNumPositionKeys);
-
-            } // else write as usual
-            else WriteArray<aiVectorKey>(&chunk,nd->mPositionKeys,nd->mNumPositionKeys);
-        }
-        if (nd->mRotationKeys) {
-            if (shortened) {
-                WriteBounds(&chunk,nd->mRotationKeys,nd->mNumRotationKeys);
-
-            } // else write as usual
-            else WriteArray<aiQuatKey>(&chunk,nd->mRotationKeys,nd->mNumRotationKeys);
-        }
-        if (nd->mScalingKeys) {
-            if (shortened) {
-                WriteBounds(&chunk,nd->mScalingKeys,nd->mNumScalingKeys);
-
-            } // else write as usual
-            else WriteArray<aiVectorKey>(&chunk,nd->mScalingKeys,nd->mNumScalingKeys);
-        }
-    }
-
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryAnim( IOStream * container, const aiAnimation* anim )
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIANIMATION );
-
-        Write<aiString>(&chunk,anim->mName);
-        Write<double>(&chunk,anim->mDuration);
-        Write<double>(&chunk,anim->mTicksPerSecond);
-        Write<unsigned int>(&chunk,anim->mNumChannels);
-
-        for (unsigned int a = 0; a < anim->mNumChannels;++a) {
-            const aiNodeAnim* nd = anim->mChannels[a];
-            WriteBinaryNodeAnim(&chunk,nd);
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryLight( IOStream * container, const aiLight* l )
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AILIGHT );
-
-        Write<aiString>(&chunk,l->mName);
-        Write<unsigned int>(&chunk,l->mType);
-
-        if (l->mType != aiLightSource_DIRECTIONAL) {
-            Write<float>(&chunk,l->mAttenuationConstant);
-            Write<float>(&chunk,l->mAttenuationLinear);
-            Write<float>(&chunk,l->mAttenuationQuadratic);
-        }
-
-        Write<aiColor3D>(&chunk,l->mColorDiffuse);
-        Write<aiColor3D>(&chunk,l->mColorSpecular);
-        Write<aiColor3D>(&chunk,l->mColorAmbient);
-
-        if (l->mType == aiLightSource_SPOT) {
-            Write<float>(&chunk,l->mAngleInnerCone);
-            Write<float>(&chunk,l->mAngleOuterCone);
-        }
-
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryCamera( IOStream * container, const aiCamera* cam )
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AICAMERA );
-
-        Write<aiString>(&chunk,cam->mName);
-        Write<aiVector3D>(&chunk,cam->mPosition);
-        Write<aiVector3D>(&chunk,cam->mLookAt);
-        Write<aiVector3D>(&chunk,cam->mUp);
-        Write<float>(&chunk,cam->mHorizontalFOV);
-        Write<float>(&chunk,cam->mClipPlaneNear);
-        Write<float>(&chunk,cam->mClipPlaneFar);
-        Write<float>(&chunk,cam->mAspect);
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryScene( IOStream * container, const aiScene* scene)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AISCENE );
-
-        // basic scene information
-        Write<unsigned int>(&chunk,scene->mFlags);
-        Write<unsigned int>(&chunk,scene->mNumMeshes);
-        Write<unsigned int>(&chunk,scene->mNumMaterials);
-        Write<unsigned int>(&chunk,scene->mNumAnimations);
-        Write<unsigned int>(&chunk,scene->mNumTextures);
-        Write<unsigned int>(&chunk,scene->mNumLights);
-        Write<unsigned int>(&chunk,scene->mNumCameras);
-
-        // write node graph
-        WriteBinaryNode( &chunk, scene->mRootNode );
-
-        // write all meshes
-        for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
-            const aiMesh* mesh = scene->mMeshes[i];
-            WriteBinaryMesh( &chunk,mesh);
-        }
-
-        // write materials
-        for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
-            const aiMaterial* mat = scene->mMaterials[i];
-            WriteBinaryMaterial(&chunk,mat);
-        }
-
-        // write all animations
-        for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
-            const aiAnimation* anim = scene->mAnimations[i];
-            WriteBinaryAnim(&chunk,anim);
-        }
-
-
-        // write all textures
-        for (unsigned int i = 0; i < scene->mNumTextures;++i) {
-            const aiTexture* mesh = scene->mTextures[i];
-            WriteBinaryTexture(&chunk,mesh);
-        }
-
-        // write lights
-        for (unsigned int i = 0; i < scene->mNumLights;++i) {
-            const aiLight* l = scene->mLights[i];
-            WriteBinaryLight(&chunk,l);
-        }
-
-        // write cameras
-        for (unsigned int i = 0; i < scene->mNumCameras;++i) {
-            const aiCamera* cam = scene->mCameras[i];
-            WriteBinaryCamera(&chunk,cam);
-        }
-
-    }
-
-public:
-    AssbinExport()
-        : shortened(false), compressed(false) // temporary settings until properties are introduced for exporters
-    {
-    }
-
-    // -----------------------------------------------------------------------------------
-    // Write a binary model dump
-    void WriteBinaryDump(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
-    {
-        IOStream * out = pIOSystem->Open( pFile, "wb" );
-        if (!out) return;
-
-        time_t tt = time(NULL);
-#if _WIN32
-        tm* p     = gmtime(&tt);
-#else
-        struct tm now;
-        tm* p = gmtime_r(&tt, &now);
-#endif
-
-        // header
-        char s[64];
-        memset( s, 0, 64 );
-#if _MSC_VER >= 1400
-        sprintf_s(s,"ASSIMP.binary-dump.%s",asctime(p));
-#else
-        ai_snprintf(s,64,"ASSIMP.binary-dump.%s",asctime(p));
-#endif
-        out->Write( s, 44, 1 );
-        // == 44 bytes
-
-        Write<unsigned int>( out, ASSBIN_VERSION_MAJOR );
-        Write<unsigned int>( out, ASSBIN_VERSION_MINOR );
-        Write<unsigned int>( out, aiGetVersionRevision() );
-        Write<unsigned int>( out, aiGetCompileFlags() );
-        Write<uint16_t>( out, shortened );
-        Write<uint16_t>( out, compressed );
-        // ==  20 bytes
-
-        char buff[256];
-        strncpy(buff,pFile,256);
-        out->Write(buff,sizeof(char),256);
-
-        char cmd[] = "\0";
-        strncpy(buff,cmd,128);
-        out->Write(buff,sizeof(char),128);
-
-        // leave 64 bytes free for future extensions
-        memset(buff,0xcd,64);
-        out->Write(buff,sizeof(char),64);
-        // == 435 bytes
-
-        // ==== total header size: 512 bytes
-        ai_assert( out->Tell() == ASSBIN_HEADER_LENGTH );
-
-        // Up to here the data is uncompressed. For compressed files, the rest
-        // is compressed using standard DEFLATE from zlib.
-        if (compressed)
-        {
-            AssbinChunkWriter uncompressedStream( NULL, 0 );
-            WriteBinaryScene( &uncompressedStream, pScene );
-
-            uLongf uncompressedSize = static_cast<uLongf>(uncompressedStream.Tell());
-            uLongf compressedSize = (uLongf)compressBound(uncompressedSize);
-            uint8_t* compressedBuffer = new uint8_t[ compressedSize ];
-
-            int res = compress2( compressedBuffer, &compressedSize, (const Bytef*)uncompressedStream.GetBufferPointer(), uncompressedSize, 9 );
-            if(res != Z_OK)
-            {
-                delete [] compressedBuffer;
-                pIOSystem->Close(out);
-                throw DeadlyExportError("Compression failed.");
-            }
-
-            out->Write( &uncompressedSize, sizeof(uint32_t), 1 );
-            out->Write( compressedBuffer, sizeof(char), compressedSize );
-
-            delete[] compressedBuffer;
-        }
-        else
-        {
-            WriteBinaryScene( out, pScene );
-        }
-
-        pIOSystem->Close( out );
-    }
-};
-
-void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
-    AssbinExport exporter;
-    exporter.WriteBinaryDump( pFile, pIOSystem, pScene );
-}
-} // end of namespace Assimp
-
-#endif // ASSIMP_BUILD_NO_ASSBIN_EXPORTER
-#endif // ASSIMP_BUILD_NO_EXPORT

文件差异内容过多而无法显示
+ 249 - 308
code/AssetLib/3DS/3DSConverter.cpp


+ 129 - 144
code/3DS/3DSExporter.cpp → code/AssetLib/3DS/3DSExporter.cpp

@@ -43,120 +43,117 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
 
-#include "3DS/3DSExporter.h"
-#include "3DS/3DSLoader.h"
-#include "3DS/3DSHelper.h"
+#include "AssetLib/3DS/3DSExporter.h"
+#include "AssetLib/3DS/3DSHelper.h"
+#include "AssetLib/3DS/3DSLoader.h"
 #include "PostProcessing/SplitLargeMeshes.h"
 
 #include <assimp/SceneCombiner.h>
 #include <assimp/StringComparison.h>
-#include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
 
 #include <memory>
 
 using namespace Assimp;
-namespace Assimp    {
+namespace Assimp {
 using namespace D3DS;
 
 namespace {
 
-    //////////////////////////////////////////////////////////////////////////////////////
-    // Scope utility to write a 3DS file chunk.
-    //
-    // Upon construction, the chunk header is written with the chunk type (flags)
-    // filled out, but the chunk size left empty. Upon destruction, the correct chunk
-    // size based on the then-position of the output stream cursor is filled in.
-    class ChunkWriter {
-        enum {
-              CHUNK_SIZE_NOT_SET = 0xdeadbeef
-            , SIZE_OFFSET        = 2
-        };
-    public:
-
-        ChunkWriter(StreamWriterLE& writer, uint16_t chunk_type)
-            : writer(writer)
-        {
-            chunk_start_pos = writer.GetCurrentPos();
-            writer.PutU2(chunk_type);
-            writer.PutU4(CHUNK_SIZE_NOT_SET);
-        }
-
-        ~ChunkWriter() {
-            std::size_t head_pos = writer.GetCurrentPos();
+//////////////////////////////////////////////////////////////////////////////////////
+// Scope utility to write a 3DS file chunk.
+//
+// Upon construction, the chunk header is written with the chunk type (flags)
+// filled out, but the chunk size left empty. Upon destruction, the correct chunk
+// size based on the then-position of the output stream cursor is filled in.
+class ChunkWriter {
+    enum {
+        CHUNK_SIZE_NOT_SET = 0xdeadbeef,
+        SIZE_OFFSET = 2
+    };
 
-            ai_assert(head_pos > chunk_start_pos);
-            const std::size_t chunk_size = head_pos - chunk_start_pos;
+public:
+    ChunkWriter(StreamWriterLE &writer, uint16_t chunk_type) :
+            writer(writer) {
+        chunk_start_pos = writer.GetCurrentPos();
+        writer.PutU2(chunk_type);
+        writer.PutU4((uint32_t)CHUNK_SIZE_NOT_SET);
+    }
 
-            writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
-            writer.PutU4(static_cast<uint32_t>(chunk_size));
-            writer.SetCurrentPos(head_pos);
-        }
+    ~ChunkWriter() {
+        std::size_t head_pos = writer.GetCurrentPos();
 
-    private:
-        StreamWriterLE& writer;
-        std::size_t chunk_start_pos;
-    };
+        ai_assert(head_pos > chunk_start_pos);
+        const std::size_t chunk_size = head_pos - chunk_start_pos;
 
+        writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
+        writer.PutU4(static_cast<uint32_t>(chunk_size));
+        writer.SetCurrentPos(head_pos);
+    }
 
-    // Return an unique name for a given |mesh| attached to |node| that
-    // preserves the mesh's given name if it has one. |index| is the index
-    // of the mesh in |aiScene::mMeshes|.
-    std::string GetMeshName(const aiMesh& mesh, unsigned int index, const aiNode& node) {
-        static const std::string underscore = "_";
-        char postfix[10] = {0};
-        ASSIMP_itoa10(postfix, index);
+private:
+    StreamWriterLE &writer;
+    std::size_t chunk_start_pos;
+};
+
+// Return an unique name for a given |mesh| attached to |node| that
+// preserves the mesh's given name if it has one. |index| is the index
+// of the mesh in |aiScene::mMeshes|.
+std::string GetMeshName(const aiMesh &mesh, unsigned int index, const aiNode &node) {
+    static const std::string underscore = "_";
+    char postfix[10] = { 0 };
+    ASSIMP_itoa10(postfix, index);
+
+    std::string result = node.mName.C_Str();
+    if (mesh.mName.length > 0) {
+        result += underscore + mesh.mName.C_Str();
+    }
+    return result + underscore + postfix;
+}
 
-        std::string result = node.mName.C_Str();
-        if (mesh.mName.length > 0) {
-            result += underscore + mesh.mName.C_Str();
-        }
-        return result + underscore + postfix;
+// Return an unique name for a given |mat| with original position |index|
+// in |aiScene::mMaterials|. The name preserves the original material
+// name if possible.
+std::string GetMaterialName(const aiMaterial &mat, unsigned int index) {
+    static const std::string underscore = "_";
+    char postfix[10] = { 0 };
+    ASSIMP_itoa10(postfix, index);
+
+    aiString mat_name;
+    if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
+        return mat_name.C_Str() + underscore + postfix;
     }
 
-    // Return an unique name for a given |mat| with original position |index|
-    // in |aiScene::mMaterials|. The name preserves the original material
-    // name if possible.
-    std::string GetMaterialName(const aiMaterial& mat, unsigned int index) {
-        static const std::string underscore = "_";
-        char postfix[10] = {0};
-        ASSIMP_itoa10(postfix, index);
-
-        aiString mat_name;
-        if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
-            return mat_name.C_Str() + underscore + postfix;
-        }
+    return "Material" + underscore + postfix;
+}
 
-        return "Material" + underscore + postfix;
+// Collect world transformations for each node
+void CollectTrafos(const aiNode *node, std::map<const aiNode *, aiMatrix4x4> &trafos) {
+    const aiMatrix4x4 &parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
+    trafos[node] = parent * node->mTransformation;
+    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+        CollectTrafos(node->mChildren[i], trafos);
     }
+}
 
-    // Collect world transformations for each node
-    void CollectTrafos(const aiNode* node, std::map<const aiNode*, aiMatrix4x4>& trafos) {
-        const aiMatrix4x4& parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
-        trafos[node] = parent * node->mTransformation;
-        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
-            CollectTrafos(node->mChildren[i], trafos);
-        }
+// Generate a flat list of the meshes (by index) assigned to each node
+void CollectMeshes(const aiNode *node, std::multimap<const aiNode *, unsigned int> &meshes) {
+    for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+        meshes.insert(std::make_pair(node, node->mMeshes[i]));
     }
-
-    // Generate a flat list of the meshes (by index) assigned to each node
-    void CollectMeshes(const aiNode* node, std::multimap<const aiNode*, unsigned int>& meshes) {
-        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
-            meshes.insert(std::make_pair(node, node->mMeshes[i]));
-        }
-        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
-            CollectMeshes(node->mChildren[i], meshes);
-        }
+    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+        CollectMeshes(node->mChildren[i], meshes);
     }
 }
+} // namespace
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
-void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
-{
-    std::shared_ptr<IOStream> outfile (pIOSystem->Open(pFile, "wb"));
-    if(!outfile) {
+void ExportScene3DS(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    std::shared_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wb"));
+    if (!outfile) {
         throw DeadlyExportError("Could not open output .3ds file: " + std::string(pFile));
     }
 
@@ -167,8 +164,8 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen
     // SplitLargeMeshes can do this, but it requires the correct limit to be set
     // which is not possible with the current way of specifying preprocess steps
     // in |Exporter::ExportFormatEntry|.
-    aiScene* scenecopy_tmp;
-    SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
+    aiScene *scenecopy_tmp;
+    SceneCombiner::CopyScene(&scenecopy_tmp, pScene);
     std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
 
     SplitLargeMeshesProcess_Triangle tri_splitter;
@@ -186,28 +183,26 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen
 } // end of namespace Assimp
 
 // ------------------------------------------------------------------------------------------------
-Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* scene)
-: scene(scene)
-, writer(outfile)
-{
+Discreet3DSExporter::Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene *scene) :
+        scene(scene), writer(outfile) {
     CollectTrafos(scene->mRootNode, trafos);
     CollectMeshes(scene->mRootNode, meshes);
 
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAIN);
+    ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAIN);
 
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJMESH);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_OBJMESH);
         WriteMaterials();
         WriteMeshes();
 
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MASTER_SCALE);
+            ChunkWriter curChunk1(writer, Discreet3DS::CHUNK_MASTER_SCALE);
             writer.PutF4(1.0f);
         }
     }
 
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_KEYFRAMER);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_KEYFRAMER);
         WriteHierarchy(*scene->mRootNode, -1, -1);
     }
 }
@@ -217,15 +212,13 @@ Discreet3DSExporter::~Discreet3DSExporter() {
     // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
-int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling_level)
-{
+int Discreet3DSExporter::WriteHierarchy(const aiNode &node, int seq, int sibling_level) {
     // 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+        ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
 
             // Assimp node names are unique and distinct from all mesh-node
             // names we generate; thus we can use them as-is
@@ -237,7 +230,7 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
 
             int16_t hierarchy_pos = static_cast<int16_t>(seq);
             if (sibling_level != -1) {
-                hierarchy_pos = sibling_level;
+                hierarchy_pos = (uint16_t)sibling_level;
             }
 
             // Write the hierarchy position
@@ -260,9 +253,9 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
         const bool first_child = node.mNumChildren == 0 && i == 0;
 
         const unsigned int mesh_idx = node.mMeshes[i];
-        const aiMesh& mesh = *scene->mMeshes[mesh_idx];
+        const aiMesh &mesh = *scene->mMeshes[mesh_idx];
 
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
         {
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
             WriteString(GetMeshName(mesh, mesh_idx, node));
@@ -276,36 +269,35 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteMaterials()
-{
+void Discreet3DSExporter::WriteMaterials() {
     for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
-        const aiMaterial& mat = *scene->mMaterials[i];
+        ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
+        const aiMaterial &mat = *scene->mMaterials[i];
 
         {
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATNAME);
-            const std::string& name = GetMaterialName(mat, i);
+            const std::string &name = GetMaterialName(mat, i);
             WriteString(name);
         }
 
         aiColor3D color;
         if (mat.Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_DIFFUSE);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_DIFFUSE);
             WriteColor(color);
         }
 
         if (mat.Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
             WriteColor(color);
         }
 
         if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
             WriteColor(color);
         }
 
         if (mat.Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
             WriteColor(color);
         }
 
@@ -314,7 +306,7 @@ void Discreet3DSExporter::WriteMaterials()
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
 
             Discreet3DS::shadetype3ds shading_mode_out;
-            switch(shading_mode) {
+            switch (shading_mode) {
             case aiShadingMode_Flat:
             case aiShadingMode_NoShading:
                 shading_mode_out = Discreet3DS::Flat;
@@ -341,7 +333,6 @@ void Discreet3DSExporter::WriteMaterials()
             writer.PutU2(static_cast<uint16_t>(shading_mode_out));
         }
 
-
         float f;
         if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
@@ -370,8 +361,7 @@ void Discreet3DSExporter::WriteMaterials()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags)
-{
+void Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type, uint16_t chunk_flags) {
     aiString path;
     aiTextureMapMode map_mode[2] = {
         aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
@@ -389,19 +379,18 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
 
     ChunkWriter chunk(writer, chunk_flags);
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPFILE);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAPFILE);
         WriteString(path);
     }
 
     WritePercentChunk(blend);
 
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MAP_TILING);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_MAP_TILING);
         uint16_t val = 0; // WRAP
         if (map_mode[0] == aiTextureMapMode_Mirror) {
             val = 0x2;
-        }
-        else if (map_mode[0] == aiTextureMapMode_Decal) {
+        } else if (map_mode[0] == aiTextureMapMode_Decal) {
             val = 0x10;
         }
         writer.PutU2(val);
@@ -410,8 +399,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteMeshes()
-{
+void Discreet3DSExporter::WriteMeshes() {
     // NOTE: 3DS allows for instances. However:
     //   i)  not all importers support reading them
     //   ii) instances are not as flexible as they are in assimp, in particular,
@@ -423,36 +411,35 @@ void Discreet3DSExporter::WriteMeshes()
     // Furthermore, the TRIMESH is transformed into world space so that it will
     // appear correctly if importers don't read the scene hierarchy at all.
     for (MeshesByNodeMap::const_iterator it = meshes.begin(); it != meshes.end(); ++it) {
-        const aiNode& node = *(*it).first;
+        const aiNode &node = *(*it).first;
         const unsigned int mesh_idx = (*it).second;
 
-        const aiMesh& mesh = *scene->mMeshes[mesh_idx];
+        const aiMesh &mesh = *scene->mMeshes[mesh_idx];
 
         // This should not happen if the SLM step is correctly executed
         // before the scene is handed to the exporter
         ai_assert(mesh.mNumVertices <= 0xffff);
         ai_assert(mesh.mNumFaces <= 0xffff);
 
-        const aiMatrix4x4& trafo = trafos[&node];
+        const aiMatrix4x4 &trafo = trafos[&node];
 
         ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK);
 
         // Mesh name is tied to the node it is attached to so it can later be referenced
-        const std::string& name = GetMeshName(mesh, mesh_idx, node);
+        const std::string &name = GetMeshName(mesh, mesh_idx, node);
         WriteString(name);
 
-
         // TRIMESH chunk
         ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH);
 
         // Vertices in world space
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_VERTLIST);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_VERTLIST);
 
             const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
             writer.PutU2(count);
             for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
-                const aiVector3D& v = trafo * mesh.mVertices[i];
+                const aiVector3D &v = trafo * mesh.mVertices[i];
                 writer.PutF4(v.x);
                 writer.PutF4(v.y);
                 writer.PutF4(v.z);
@@ -461,12 +448,12 @@ void Discreet3DSExporter::WriteMeshes()
 
         // UV coordinates
         if (mesh.HasTextureCoords(0)) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPLIST);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAPLIST);
             const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
             writer.PutU2(count);
 
             for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
-                const aiVector3D& v = mesh.mTextureCoords[0][i];
+                const aiVector3D &v = mesh.mTextureCoords[0][i];
                 writer.PutF4(v.x);
                 writer.PutF4(v.y);
             }
@@ -474,14 +461,14 @@ void Discreet3DSExporter::WriteMeshes()
 
         // Faces (indices)
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_FACELIST);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACELIST);
 
             ai_assert(mesh.mNumFaces <= 0xffff);
 
             // Count triangles, discard lines and points
             uint16_t count = 0;
             for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
-                const aiFace& f = mesh.mFaces[i];
+                const aiFace &f = mesh.mFaces[i];
                 if (f.mNumIndices < 3) {
                     continue;
                 }
@@ -492,7 +479,7 @@ void Discreet3DSExporter::WriteMeshes()
 
             writer.PutU2(count);
             for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
-                const aiFace& f = mesh.mFaces[i];
+                const aiFace &f = mesh.mFaces[i];
                 if (f.mNumIndices < 3) {
                     continue;
                 }
@@ -513,7 +500,7 @@ void Discreet3DSExporter::WriteMeshes()
 
         // Transformation matrix by which the mesh vertices have been pre-transformed with.
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRMATRIX);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRMATRIX);
             for (unsigned int r = 0; r < 4; ++r) {
                 for (unsigned int c = 0; c < 3; ++c) {
                     writer.PutF4(trafo[r][c]);
@@ -524,10 +511,9 @@ void Discreet3DSExporter::WriteMeshes()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
-{
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_FACEMAT);
-    const std::string& name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
+void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh &mesh) {
+    ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACEMAT);
+    const std::string &name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
     WriteString(name);
 
     // Because assimp splits meshes by material, only a single
@@ -542,7 +528,7 @@ void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteString(const std::string& s) {
+void Discreet3DSExporter::WriteString(const std::string &s) {
     for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
         writer.PutI1(*it);
     }
@@ -550,7 +536,7 @@ void Discreet3DSExporter::WriteString(const std::string& s) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteString(const aiString& s) {
+void Discreet3DSExporter::WriteString(const aiString &s) {
     for (std::size_t i = 0; i < s.length; ++i) {
         writer.PutI1(s.data[i]);
     }
@@ -558,8 +544,8 @@ void Discreet3DSExporter::WriteString(const aiString& s) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteColor(const aiColor3D& color) {
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_RGBF);
+void Discreet3DSExporter::WriteColor(const aiColor3D &color) {
+    ChunkWriter curChunk(writer, Discreet3DS::CHUNK_RGBF);
     writer.PutF4(color.r);
     writer.PutF4(color.g);
     writer.PutF4(color.b);
@@ -567,16 +553,15 @@ void Discreet3DSExporter::WriteColor(const aiColor3D& color) {
 
 // ------------------------------------------------------------------------------------------------
 void Discreet3DSExporter::WritePercentChunk(float f) {
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_PERCENTF);
+    ChunkWriter curChunk(writer, Discreet3DS::CHUNK_PERCENTF);
     writer.PutF4(f);
 }
 
 // ------------------------------------------------------------------------------------------------
 void Discreet3DSExporter::WritePercentChunk(double f) {
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_PERCENTD);
+    ChunkWriter ccurChunkhunk(writer, Discreet3DS::CHUNK_PERCENTD);
     writer.PutF8(f);
 }
 
-
 #endif // ASSIMP_BUILD_NO_3DS_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 0 - 0
code/3DS/3DSExporter.h → code/AssetLib/3DS/3DSExporter.h


+ 287 - 235
code/3DS/3DSHelper.h → code/AssetLib/3DS/3DSHelper.h

@@ -45,18 +45,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_3DSFILEHELPER_H_INC
 #define AI_3DSFILEHELPER_H_INC
 
-#include <assimp/SpatialSort.h>
 #include <assimp/SmoothingGroups.h>
+#include <assimp/SpatialSort.h>
 #include <assimp/StringUtils.h>
-#include <assimp/qnan.h>
-#include <assimp/material.h>
+#include <assimp/anim.h>
 #include <assimp/camera.h>
 #include <assimp/light.h>
-#include <assimp/anim.h>
+#include <assimp/material.h>
+#include <assimp/qnan.h>
 #include <stdio.h> //sprintf
 
-namespace Assimp    {
-namespace D3DS  {
+namespace Assimp {
+namespace D3DS {
 
 #include <assimp/Compiler/pushpack1.h>
 
@@ -77,15 +77,13 @@ private:
 public:
     //! data structure for a single chunk in a .3ds file
     struct Chunk {
-        uint16_t    Flag;
-        uint32_t    Size;
+        uint16_t Flag;
+        uint32_t Size;
     } PACK_STRUCT;
 
-
     //! Used for shading field in material3ds structure
     //! From AutoDesk 3ds SDK
-    typedef enum
-    {
+    typedef enum {
         // translated to gouraud shading with wireframe active
         Wire = 0x0,
 
@@ -109,59 +107,57 @@ public:
     } shadetype3ds;
 
     // Flags for animated keys
-    enum
-    {
-        KEY_USE_TENS         = 0x1,
-        KEY_USE_CONT         = 0x2,
-        KEY_USE_BIAS         = 0x4,
-        KEY_USE_EASE_TO      = 0x8,
-        KEY_USE_EASE_FROM    = 0x10
-    } ;
-
-    enum
-    {
+    enum {
+        KEY_USE_TENS = 0x1,
+        KEY_USE_CONT = 0x2,
+        KEY_USE_BIAS = 0x4,
+        KEY_USE_EASE_TO = 0x8,
+        KEY_USE_EASE_FROM = 0x10
+    };
+
+    enum {
 
         // ********************************************************************
         // Basic chunks which can be found everywhere in the file
-        CHUNK_VERSION   = 0x0002,
-        CHUNK_RGBF      = 0x0010,       // float4 R; float4 G; float4 B
-        CHUNK_RGBB      = 0x0011,       // int1 R; int1 G; int B
+        CHUNK_VERSION = 0x0002,
+        CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B
+        CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B
 
         // Linear color values (gamma = 2.2?)
-        CHUNK_LINRGBF      = 0x0013,    // float4 R; float4 G; float4 B
-        CHUNK_LINRGBB      = 0x0012,    // int1 R; int1 G; int B
+        CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B
+        CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B
 
-        CHUNK_PERCENTW  = 0x0030,       // int2   percentage
-        CHUNK_PERCENTF  = 0x0031,       // float4  percentage
-        CHUNK_PERCENTD  = 0x0032,       // float8  percentage
+        CHUNK_PERCENTW = 0x0030, // int2   percentage
+        CHUNK_PERCENTF = 0x0031, // float4  percentage
+        CHUNK_PERCENTD = 0x0032, // float8  percentage
         // ********************************************************************
 
         // Prj master chunk
-        CHUNK_PRJ       = 0xC23D,
+        CHUNK_PRJ = 0xC23D,
 
         // MDLI master chunk
-        CHUNK_MLI       = 0x3DAA,
+        CHUNK_MLI = 0x3DAA,
 
         // Primary main chunk of the .3ds file
-        CHUNK_MAIN      = 0x4D4D,
+        CHUNK_MAIN = 0x4D4D,
 
         // Mesh main chunk
-        CHUNK_OBJMESH   = 0x3D3D,
+        CHUNK_OBJMESH = 0x3D3D,
 
         // Specifies the background color of the .3ds file
         // This is passed through the material system for
         // viewing purposes.
-        CHUNK_BKGCOLOR  = 0x1200,
+        CHUNK_BKGCOLOR = 0x1200,
 
         // Specifies the ambient base color of the scene.
         // This is added to all materials in the file
-        CHUNK_AMBCOLOR  = 0x2100,
+        CHUNK_AMBCOLOR = 0x2100,
 
         // Specifies the background image for the whole scene
         // This value is passed through the material system
         // to the viewer
-        CHUNK_BIT_MAP   = 0x1100,
-        CHUNK_BIT_MAP_EXISTS  = 0x1101,
+        CHUNK_BIT_MAP = 0x1100,
+        CHUNK_BIT_MAP_EXISTS = 0x1101,
 
         // ********************************************************************
         // Viewport related stuff. Ignored
@@ -177,171 +173,222 @@ public:
         // ********************************************************************
 
         // Mesh chunks
-        CHUNK_OBJBLOCK  = 0x4000,
-        CHUNK_TRIMESH   = 0x4100,
-        CHUNK_VERTLIST  = 0x4110,
+        CHUNK_OBJBLOCK = 0x4000,
+        CHUNK_TRIMESH = 0x4100,
+        CHUNK_VERTLIST = 0x4110,
         CHUNK_VERTFLAGS = 0x4111,
-        CHUNK_FACELIST  = 0x4120,
-        CHUNK_FACEMAT   = 0x4130,
-        CHUNK_MAPLIST   = 0x4140,
-        CHUNK_SMOOLIST  = 0x4150,
-        CHUNK_TRMATRIX  = 0x4160,
+        CHUNK_FACELIST = 0x4120,
+        CHUNK_FACEMAT = 0x4130,
+        CHUNK_MAPLIST = 0x4140,
+        CHUNK_SMOOLIST = 0x4150,
+        CHUNK_TRMATRIX = 0x4160,
         CHUNK_MESHCOLOR = 0x4165,
-        CHUNK_TXTINFO   = 0x4170,
-        CHUNK_LIGHT     = 0x4600,
-        CHUNK_CAMERA    = 0x4700,
+        CHUNK_TXTINFO = 0x4170,
+        CHUNK_LIGHT = 0x4600,
+        CHUNK_CAMERA = 0x4700,
         CHUNK_HIERARCHY = 0x4F00,
 
         // Specifies the global scaling factor. This is applied
         // to the root node's transformation matrix
-        CHUNK_MASTER_SCALE    = 0x0100,
+        CHUNK_MASTER_SCALE = 0x0100,
 
         // ********************************************************************
         // Material chunks
-        CHUNK_MAT_MATERIAL  = 0xAFFF,
+        CHUNK_MAT_MATERIAL = 0xAFFF,
 
-            // asciiz containing the name of the material
-            CHUNK_MAT_MATNAME   = 0xA000,
-            CHUNK_MAT_AMBIENT   = 0xA010, // followed by color chunk
-            CHUNK_MAT_DIFFUSE   = 0xA020, // followed by color chunk
-            CHUNK_MAT_SPECULAR  = 0xA030, // followed by color chunk
+        // asciiz containing the name of the material
+        CHUNK_MAT_MATNAME = 0xA000,
+        CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk
+        CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk
+        CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk
 
-            // Specifies the shininess of the material
-            // followed by percentage chunk
-            CHUNK_MAT_SHININESS  = 0xA040,
-            CHUNK_MAT_SHININESS_PERCENT  = 0xA041 ,
+        // Specifies the shininess of the material
+        // followed by percentage chunk
+        CHUNK_MAT_SHININESS = 0xA040,
+        CHUNK_MAT_SHININESS_PERCENT = 0xA041,
 
-            // Specifies the shading mode to be used
-            // followed by a short
-            CHUNK_MAT_SHADING  = 0xA100,
+        // Specifies the shading mode to be used
+        // followed by a short
+        CHUNK_MAT_SHADING = 0xA100,
 
-            // NOTE: Emissive color (self illumination) seems not
-            // to be a color but a single value, type is unknown.
-            // Make the parser accept both of them.
-            // followed by percentage chunk (?)
-            CHUNK_MAT_SELF_ILLUM = 0xA080,
+        // NOTE: Emissive color (self illumination) seems not
+        // to be a color but a single value, type is unknown.
+        // Make the parser accept both of them.
+        // followed by percentage chunk (?)
+        CHUNK_MAT_SELF_ILLUM = 0xA080,
 
-            // Always followed by percentage chunk  (?)
-            CHUNK_MAT_SELF_ILPCT = 0xA084,
+        // Always followed by percentage chunk  (?)
+        CHUNK_MAT_SELF_ILPCT = 0xA084,
 
-            // Always followed by percentage chunk
-            CHUNK_MAT_TRANSPARENCY = 0xA050,
+        // Always followed by percentage chunk
+        CHUNK_MAT_TRANSPARENCY = 0xA050,
 
-            // Diffuse texture channel 0
-            CHUNK_MAT_TEXTURE   = 0xA200,
+        // Diffuse texture channel 0
+        CHUNK_MAT_TEXTURE = 0xA200,
 
-            // Contains opacity information for each texel
-            CHUNK_MAT_OPACMAP = 0xA210,
+        // Contains opacity information for each texel
+        CHUNK_MAT_OPACMAP = 0xA210,
 
-            // Contains a reflection map to be used to reflect
-            // the environment. This is partially supported.
-            CHUNK_MAT_REFLMAP = 0xA220,
+        // Contains a reflection map to be used to reflect
+        // the environment. This is partially supported.
+        CHUNK_MAT_REFLMAP = 0xA220,
 
-            // Self Illumination map (emissive colors)
-            CHUNK_MAT_SELFIMAP = 0xA33d,
+        // Self Illumination map (emissive colors)
+        CHUNK_MAT_SELFIMAP = 0xA33d,
 
-            // Bumpmap. Not specified whether it is a heightmap
-            // or a normal map. Assme it is a heightmap since
-            // artist normally prefer this format.
-            CHUNK_MAT_BUMPMAP = 0xA230,
+        // Bumpmap. Not specified whether it is a heightmap
+        // or a normal map. Assme it is a heightmap since
+        // artist normally prefer this format.
+        CHUNK_MAT_BUMPMAP = 0xA230,
 
-            // Specular map. Seems to influence the specular color
-            CHUNK_MAT_SPECMAP = 0xA204,
+        // Specular map. Seems to influence the specular color
+        CHUNK_MAT_SPECMAP = 0xA204,
 
-            // Holds shininess data.
-            CHUNK_MAT_MAT_SHINMAP = 0xA33C,
+        // Holds shininess data.
+        CHUNK_MAT_MAT_SHINMAP = 0xA33C,
 
-            // Scaling in U/V direction.
-            // (need to gen separate UV coordinate set
-            // and do this by hand)
-            CHUNK_MAT_MAP_USCALE      = 0xA354,
-            CHUNK_MAT_MAP_VSCALE      = 0xA356,
+        // Scaling in U/V direction.
+        // (need to gen separate UV coordinate set
+        // and do this by hand)
+        CHUNK_MAT_MAP_USCALE = 0xA354,
+        CHUNK_MAT_MAP_VSCALE = 0xA356,
 
-            // Translation in U/V direction.
-            // (need to gen separate UV coordinate set
-            // and do this by hand)
-            CHUNK_MAT_MAP_UOFFSET     = 0xA358,
-            CHUNK_MAT_MAP_VOFFSET     = 0xA35a,
+        // Translation in U/V direction.
+        // (need to gen separate UV coordinate set
+        // and do this by hand)
+        CHUNK_MAT_MAP_UOFFSET = 0xA358,
+        CHUNK_MAT_MAP_VOFFSET = 0xA35a,
 
-            // UV-coordinates rotation around the z-axis
-            // Assumed to be in radians.
-            CHUNK_MAT_MAP_ANG = 0xA35C,
+        // UV-coordinates rotation around the z-axis
+        // Assumed to be in radians.
+        CHUNK_MAT_MAP_ANG = 0xA35C,
 
-            // Tiling flags for 3DS files
-            CHUNK_MAT_MAP_TILING = 0xa351,
+        // Tiling flags for 3DS files
+        CHUNK_MAT_MAP_TILING = 0xa351,
 
-            // Specifies the file name of a texture
-            CHUNK_MAPFILE   = 0xA300,
+        // Specifies the file name of a texture
+        CHUNK_MAPFILE = 0xA300,
 
-            // Specifies whether a materail requires two-sided rendering
-            CHUNK_MAT_TWO_SIDE = 0xA081,
+        // Specifies whether a materail requires two-sided rendering
+        CHUNK_MAT_TWO_SIDE = 0xA081,
         // ********************************************************************
 
         // Main keyframer chunk. Contains translation/rotation/scaling data
-        CHUNK_KEYFRAMER     = 0xB000,
+        CHUNK_KEYFRAMER = 0xB000,
 
         // Supported sub chunks
-        CHUNK_TRACKINFO     = 0xB002,
-        CHUNK_TRACKOBJNAME  = 0xB010,
-        CHUNK_TRACKDUMMYOBJNAME  = 0xB011,
-        CHUNK_TRACKPIVOT    = 0xB013,
-        CHUNK_TRACKPOS      = 0xB020,
-        CHUNK_TRACKROTATE   = 0xB021,
-        CHUNK_TRACKSCALE    = 0xB022,
+        CHUNK_TRACKINFO = 0xB002,
+        CHUNK_TRACKOBJNAME = 0xB010,
+        CHUNK_TRACKDUMMYOBJNAME = 0xB011,
+        CHUNK_TRACKPIVOT = 0xB013,
+        CHUNK_TRACKPOS = 0xB020,
+        CHUNK_TRACKROTATE = 0xB021,
+        CHUNK_TRACKSCALE = 0xB022,
 
         // ********************************************************************
         // Keyframes for various other stuff in the file
         // Partially ignored
-        CHUNK_AMBIENTKEY    = 0xB001,
-        CHUNK_TRACKMORPH    = 0xB026,
-        CHUNK_TRACKHIDE     = 0xB029,
-        CHUNK_OBJNUMBER     = 0xB030,
-        CHUNK_TRACKCAMERA   = 0xB003,
-        CHUNK_TRACKFOV      = 0xB023,
-        CHUNK_TRACKROLL     = 0xB024,
-        CHUNK_TRACKCAMTGT   = 0xB004,
-        CHUNK_TRACKLIGHT    = 0xB005,
-        CHUNK_TRACKLIGTGT   = 0xB006,
-        CHUNK_TRACKSPOTL    = 0xB007,
-        CHUNK_FRAMES        = 0xB008,
+        CHUNK_AMBIENTKEY = 0xB001,
+        CHUNK_TRACKMORPH = 0xB026,
+        CHUNK_TRACKHIDE = 0xB029,
+        CHUNK_OBJNUMBER = 0xB030,
+        CHUNK_TRACKCAMERA = 0xB003,
+        CHUNK_TRACKFOV = 0xB023,
+        CHUNK_TRACKROLL = 0xB024,
+        CHUNK_TRACKCAMTGT = 0xB004,
+        CHUNK_TRACKLIGHT = 0xB005,
+        CHUNK_TRACKLIGTGT = 0xB006,
+        CHUNK_TRACKSPOTL = 0xB007,
+        CHUNK_FRAMES = 0xB008,
         // ********************************************************************
 
         // light sub-chunks
-        CHUNK_DL_OFF                 = 0x4620,
-        CHUNK_DL_OUTER_RANGE         = 0x465A,
-        CHUNK_DL_INNER_RANGE         = 0x4659,
-        CHUNK_DL_MULTIPLIER          = 0x465B,
-        CHUNK_DL_EXCLUDE             = 0x4654,
-        CHUNK_DL_ATTENUATE           = 0x4625,
-        CHUNK_DL_SPOTLIGHT           = 0x4610,
+        CHUNK_DL_OFF = 0x4620,
+        CHUNK_DL_OUTER_RANGE = 0x465A,
+        CHUNK_DL_INNER_RANGE = 0x4659,
+        CHUNK_DL_MULTIPLIER = 0x465B,
+        CHUNK_DL_EXCLUDE = 0x4654,
+        CHUNK_DL_ATTENUATE = 0x4625,
+        CHUNK_DL_SPOTLIGHT = 0x4610,
 
         // camera sub-chunks
-        CHUNK_CAM_RANGES             = 0x4720
+        CHUNK_CAM_RANGES = 0x4720
     };
 };
 
 // ---------------------------------------------------------------------------
 /** Helper structure representing a 3ds mesh face */
-struct Face : public FaceWithSmoothingGroup
-{
+struct Face : public FaceWithSmoothingGroup {
 };
 
+#ifdef _WIN32
+#pragma warning(disable : 4315)
+#endif
+
 // ---------------------------------------------------------------------------
 /** Helper structure representing a texture */
 struct Texture {
     //! Default constructor
     Texture() AI_NO_EXCEPT
-    : mOffsetU  (0.0)
-    , mOffsetV  (0.0)
-    , mScaleU   (1.0)
-    , mScaleV   (1.0)
-    , mRotation (0.0)
-    , mMapMode  (aiTextureMapMode_Wrap)
-    , bPrivate()
-    , iUVSrc    (0) {
+            : mTextureBlend(0.0f),
+              mMapName(),
+              mOffsetU(0.0),
+              mOffsetV(0.0),
+              mScaleU(1.0),
+              mScaleV(1.0),
+              mRotation(0.0),
+              mMapMode(aiTextureMapMode_Wrap),
+              bPrivate(),
+              iUVSrc(0) {
         mTextureBlend = get_qnan();
     }
 
+    Texture(const Texture &other) :
+            mTextureBlend(other.mTextureBlend),
+            mMapName(other.mMapName),
+            mOffsetU(other.mOffsetU),
+            mOffsetV(other.mOffsetV),
+            mScaleU(other.mScaleU),
+            mScaleV(other.mScaleV),
+            mRotation(other.mRotation),
+            mMapMode(other.mMapMode),
+            bPrivate(other.bPrivate),
+            iUVSrc(other.iUVSrc) {
+        // empty
+    }
+
+    Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(std::move(other.mTextureBlend)),
+                                            mMapName(std::move(other.mMapName)),
+                                            mOffsetU(std::move(other.mOffsetU)),
+                                            mOffsetV(std::move(other.mOffsetV)),
+                                            mScaleU(std::move(other.mScaleU)),
+                                            mScaleV(std::move(other.mScaleV)),
+                                            mRotation(std::move(other.mRotation)),
+                                            mMapMode(std::move(other.mMapMode)),
+                                            bPrivate(std::move(other.bPrivate)),
+                                            iUVSrc(std::move(other.iUVSrc)) {
+        // empty
+    }
+
+    Texture &operator=(Texture &&other) AI_NO_EXCEPT {
+        if (this == &other) {
+            return *this;
+        }
+
+        mTextureBlend = std::move(other.mTextureBlend);
+        mMapName = std::move(other.mMapName);
+        mOffsetU = std::move(other.mOffsetU);
+        mOffsetV = std::move(other.mOffsetV);
+        mScaleU = std::move(other.mScaleU);
+        mScaleV = std::move(other.mScaleV);
+        mRotation = std::move(other.mRotation);
+        mMapMode = std::move(other.mMapMode);
+        bPrivate = std::move(other.bPrivate);
+        iUVSrc = std::move(other.iUVSrc);
+
+        return *this;
+    }
+
     //! Specifies the blend factor for the texture
     ai_real mTextureBlend;
 
@@ -367,55 +414,81 @@ struct Texture {
 
 // ---------------------------------------------------------------------------
 /** Helper structure representing a 3ds material */
-struct Material
-{
+struct Material {
     //! Default constructor has been deleted
-    Material() = delete;
-
+    Material() :
+            mName(),
+            mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+            mSpecularExponent(ai_real(0.0)),
+            mShininessStrength(ai_real(1.0)),
+            mShading(Discreet3DS::Gouraud),
+            mTransparency(ai_real(1.0)),
+            mBumpHeight(ai_real(1.0)),
+            mTwoSided(false) {
+        // empty
+    }
 
     //! Constructor with explicit name
-    explicit Material(const std::string &name)
-    : mName(name)
-    , mDiffuse            ( ai_real( 0.6 ), ai_real( 0.6 ), ai_real( 0.6 ) ) // FIX ... we won't want object to be black
-    , mSpecularExponent   ( ai_real( 0.0 ) )
-    , mShininessStrength  ( ai_real( 1.0 ) )
-    , mShading(Discreet3DS::Gouraud)
-    , mTransparency       ( ai_real( 1.0 ) )
-    , mBumpHeight         ( ai_real( 1.0 ) )
-    , mTwoSided           (false)
-    {
+    explicit Material(const std::string &name) :
+            mName(name),
+            mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+            mSpecularExponent(ai_real(0.0)),
+            mShininessStrength(ai_real(1.0)),
+            mShading(Discreet3DS::Gouraud),
+            mTransparency(ai_real(1.0)),
+            mBumpHeight(ai_real(1.0)),
+            mTwoSided(false) {
+        // empty
     }
 
+    Material(const Material &other) :
+            mName(other.mName),
+            mDiffuse(other.mDiffuse),
+            mSpecularExponent(other.mSpecularExponent),
+            mShininessStrength(other.mShininessStrength),
+            mSpecular(other.mSpecular),
+            mAmbient(other.mAmbient),
+            mShading(other.mShading),
+            mTransparency(other.mTransparency),
+            sTexDiffuse(other.sTexDiffuse),
+            sTexOpacity(other.sTexOpacity),
+            sTexSpecular(other.sTexSpecular),
+            sTexReflective(other.sTexReflective),
+            sTexBump(other.sTexBump),
+            sTexEmissive(other.sTexEmissive),
+            sTexShininess(other.sTexShininess),
+            mBumpHeight(other.mBumpHeight),
+            mEmissive(other.mEmissive),
+            sTexAmbient(other.sTexAmbient),
+            mTwoSided(other.mTwoSided) {
+        // empty
 
-    Material(const Material &other)            = default;
-    Material &operator=(const Material &other) = default;
-
+    }
 
     //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
-    Material(Material &&other) AI_NO_EXCEPT
-    : mName(std::move(other.mName))
-    , mDiffuse(std::move(other.mDiffuse))
-    , mSpecularExponent(std::move(other.mSpecularExponent))
-    , mShininessStrength(std::move(other.mShininessStrength))
-    , mSpecular(std::move(other.mSpecular))
-    , mAmbient(std::move(other.mAmbient))
-    , mShading(std::move(other.mShading))
-    , mTransparency(std::move(other.mTransparency))
-    , sTexDiffuse(std::move(other.sTexDiffuse))
-    , sTexOpacity(std::move(other.sTexOpacity))
-    , sTexSpecular(std::move(other.sTexSpecular))
-    , sTexReflective(std::move(other.sTexReflective))
-    , sTexBump(std::move(other.sTexBump))
-    , sTexEmissive(std::move(other.sTexEmissive))
-    , sTexShininess(std::move(other.sTexShininess))
-    , mBumpHeight(std::move(other.mBumpHeight))
-    , mEmissive(std::move(other.mEmissive))
-    , sTexAmbient(std::move(other.sTexAmbient))
-    , mTwoSided(std::move(other.mTwoSided))
-    {
+    Material(Material &&other) AI_NO_EXCEPT :
+            mName(std::move(other.mName)),
+            mDiffuse(std::move(other.mDiffuse)),
+            mSpecularExponent(std::move(other.mSpecularExponent)),
+            mShininessStrength(std::move(other.mShininessStrength)),
+            mSpecular(std::move(other.mSpecular)),
+            mAmbient(std::move(other.mAmbient)),
+            mShading(std::move(other.mShading)),
+            mTransparency(std::move(other.mTransparency)),
+            sTexDiffuse(std::move(other.sTexDiffuse)),
+            sTexOpacity(std::move(other.sTexOpacity)),
+            sTexSpecular(std::move(other.sTexSpecular)),
+            sTexReflective(std::move(other.sTexReflective)),
+            sTexBump(std::move(other.sTexBump)),
+            sTexEmissive(std::move(other.sTexEmissive)),
+            sTexShininess(std::move(other.sTexShininess)),
+            mBumpHeight(std::move(other.mBumpHeight)),
+            mEmissive(std::move(other.mEmissive)),
+            sTexAmbient(std::move(other.sTexAmbient)),
+            mTwoSided(std::move(other.mTwoSided)) {
+        // empty
     }
 
-
     Material &operator=(Material &&other) AI_NO_EXCEPT {
         if (this == &other) {
             return *this;
@@ -444,9 +517,9 @@ struct Material
         return *this;
     }
 
-
-    virtual ~Material() {}
-
+    virtual ~Material() {
+        // empty
+    }
 
     //! Name of the material
     std::string mName;
@@ -491,18 +564,15 @@ struct Material
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent a 3ds file mesh */
-struct Mesh : public MeshWithSmoothingGroups<D3DS::Face>
-{
+struct Mesh : public MeshWithSmoothingGroups<D3DS::Face> {
     //! Default constructor has been deleted
     Mesh() = delete;
 
     //! Constructor with explicit name
-    explicit Mesh(const std::string &name)
-    : mName(name)
-    {
+    explicit Mesh(const std::string &name) :
+            mName(name) {
     }
 
-
     //! Name of the mesh
     std::string mName;
 
@@ -519,62 +589,48 @@ struct Mesh : public MeshWithSmoothingGroups<D3DS::Face>
 // ---------------------------------------------------------------------------
 /** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the
     C-API, so it would be difficult to make them a template. */
-struct aiFloatKey
-{
-    double mTime;      ///< The time of this key
-    ai_real mValue;   ///< The value of this key
+struct aiFloatKey {
+    double mTime; ///< The time of this key
+    ai_real mValue; ///< The value of this key
 
 #ifdef __cplusplus
 
     // time is not compared
-    bool operator == (const aiFloatKey& o) const
-        {return o.mValue == this->mValue;}
+    bool operator==(const aiFloatKey &o) const { return o.mValue == this->mValue; }
 
-    bool operator != (const aiFloatKey& o) const
-        {return o.mValue != this->mValue;}
+    bool operator!=(const aiFloatKey &o) const { return o.mValue != this->mValue; }
 
     // Only time is compared. This operator is defined
     // for use with std::sort
-    bool operator < (const aiFloatKey& o) const
-        {return mTime < o.mTime;}
+    bool operator<(const aiFloatKey &o) const { return mTime < o.mTime; }
 
-    bool operator > (const aiFloatKey& o) const
-        {return mTime > o.mTime;}
+    bool operator>(const aiFloatKey &o) const { return mTime > o.mTime; }
 
 #endif
 };
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent a 3ds file node */
-struct Node
-{
+struct Node {
     Node() = delete;
 
-    explicit Node(const std::string &name)
-    : mParent(NULL)
-    , mName(name)
-    , mInstanceNumber(0)
-    , mHierarchyPos       (0)
-    , mHierarchyIndex     (0)
-    , mInstanceCount      (1)
-    {
-        aRotationKeys.reserve (20);
-        aPositionKeys.reserve (20);
-        aScalingKeys.reserve  (20);
+    explicit Node(const std::string &name) :
+            mParent(NULL), mName(name), mInstanceNumber(0), mHierarchyPos(0), mHierarchyIndex(0), mInstanceCount(1) {
+        aRotationKeys.reserve(20);
+        aPositionKeys.reserve(20);
+        aScalingKeys.reserve(20);
     }
 
-
-    ~Node()
-    {
-        for (unsigned int i = 0; i < mChildren.size();++i)
+    ~Node() {
+        for (unsigned int i = 0; i < mChildren.size(); ++i)
             delete mChildren[i];
     }
 
     //! Pointer to the parent node
-    Node* mParent;
+    Node *mParent;
 
     //! Holds all child nodes
-    std::vector<Node*> mChildren;
+    std::vector<Node *> mChildren;
 
     //! Name of the node
     std::string mName;
@@ -600,13 +656,12 @@ struct Node
     //! Scaling keys loaded from the file
     std::vector<aiVectorKey> aScalingKeys;
 
-
     // For target lights (spot lights and directional lights):
     // The position of the target
-    std::vector< aiVectorKey > aTargetPositionKeys;
+    std::vector<aiVectorKey> aTargetPositionKeys;
 
     // For cameras: the camera roll angle
-    std::vector< aiFloatKey > aCameraRollKeys;
+    std::vector<aiFloatKey> aCameraRollKeys;
 
     //! Pivot position loaded from the file
     aiVector3D vPivot;
@@ -616,8 +671,7 @@ struct Node
 
     //! Add a child node, setup the right parent node for it
     //! \param pc Node to be 'adopted'
-    inline Node& push_back(Node* pc)
-    {
+    inline Node &push_back(Node *pc) {
         mChildren.push_back(pc);
         pc->mParent = this;
         return *this;
@@ -625,8 +679,7 @@ struct Node
 };
 // ---------------------------------------------------------------------------
 /** Helper structure analogue to aiScene */
-struct Scene
-{
+struct Scene {
     //! List of all materials loaded
     //! NOTE: 3ds references materials globally
     std::vector<Material> mMaterials;
@@ -635,17 +688,16 @@ struct Scene
     std::vector<Mesh> mMeshes;
 
     //! List of all cameras loaded
-    std::vector<aiCamera*> mCameras;
+    std::vector<aiCamera *> mCameras;
 
     //! List of all lights loaded
-    std::vector<aiLight*> mLights;
+    std::vector<aiLight *> mLights;
 
     //! Pointer to the root node of the scene
     // --- moved to main class
     // Node* pcRootNode;
 };
 
-
 } // end of namespace D3DS
 } // end of namespace Assimp
 

文件差异内容过多而无法显示
+ 257 - 317
code/AssetLib/3DS/3DSLoader.cpp


+ 1 - 5
code/3DS/3DSLoader.h → code/AssetLib/3DS/3DSLoader.h

@@ -65,15 +65,11 @@ using namespace D3DS;
 // ---------------------------------------------------------------------------------
 /** Importer class for 3D Studio r3 and r4 3DS files
  */
-class Discreet3DSImporter : public BaseImporter
-{
+class Discreet3DSImporter : public BaseImporter {
 public:
-
     Discreet3DSImporter();
     ~Discreet3DSImporter();
 
-public:
-
     // -------------------------------------------------------------------
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details.

+ 0 - 0
code/3MF/3MFXmlTags.h → code/AssetLib/3MF/3MFXmlTags.h


+ 124 - 122
code/3MF/D3MFExporter.cpp → code/AssetLib/3MF/D3MFExporter.cpp

@@ -44,81 +44,74 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "D3MFExporter.h"
 
+#include <assimp/Exceptional.h>
+#include <assimp/StringUtils.h>
 #include <assimp/scene.h>
-#include <assimp/IOSystem.hpp>
-#include <assimp/IOStream.hpp>
-#include <assimp/Exporter.hpp>
 #include <assimp/DefaultLogger.hpp>
-#include <assimp/StringUtils.h>
-#include <assimp/Exceptional.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
 
 #include "3MFXmlTags.h"
 #include "D3MFOpcPackage.h"
 
 #ifdef ASSIMP_USE_HUNTER
-#  include <zip/zip.h>
+#include <zip/zip.h>
 #else
-#  include <contrib/zip/src/zip.h>
+#include <contrib/zip/src/zip.h>
 #endif
 
 namespace Assimp {
 
-void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) {
-    if ( nullptr == pIOSystem ) {
-        throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
+void ExportScene3MF(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    if (nullptr == pIOSystem) {
+        throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
     }
-    D3MF::D3MFExporter myExporter( pFile, pScene );
-    if ( myExporter.validate() ) {
-        if ( pIOSystem->Exists( pFile ) ) {
-            if ( !pIOSystem->DeleteFile( pFile ) ) {
-                throw DeadlyExportError( "File exists, cannot override : " + std::string( pFile ) );
+    D3MF::D3MFExporter myExporter(pFile, pScene);
+    if (myExporter.validate()) {
+        if (pIOSystem->Exists(pFile)) {
+            if (!pIOSystem->DeleteFile(pFile)) {
+                throw DeadlyExportError("File exists, cannot override : " + std::string(pFile));
             }
         }
         bool ok = myExporter.exportArchive(pFile);
-        if ( !ok ) {
-            throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
+        if (!ok) {
+            throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
         }
     }
 }
 
 namespace D3MF {
 
-D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene )
-: mArchiveName( pFile )
-, m_zipArchive( nullptr )
-, mScene( pScene )
-, mModelOutput()
-, mRelOutput()
-, mContentOutput()
-, mBuildItems()
-, mRelations() {
+D3MFExporter::D3MFExporter(const char *pFile, const aiScene *pScene) :
+        mArchiveName(pFile), m_zipArchive(nullptr), mScene(pScene), mModelOutput(), mRelOutput(), mContentOutput(), mBuildItems(), mRelations() {
     // empty
 }
 
 D3MFExporter::~D3MFExporter() {
-    for ( size_t i = 0; i < mRelations.size(); ++i ) {
-        delete mRelations[ i ];
+    for (size_t i = 0; i < mRelations.size(); ++i) {
+        delete mRelations[i];
     }
     mRelations.clear();
 }
 
 bool D3MFExporter::validate() {
-    if ( mArchiveName.empty() ) {
+    if (mArchiveName.empty()) {
         return false;
     }
 
-    if ( nullptr == mScene ) {
+    if (nullptr == mScene) {
         return false;
     }
 
     return true;
 }
 
-bool D3MFExporter::exportArchive( const char *file ) {
-    bool ok( true );
+bool D3MFExporter::exportArchive(const char *file) {
+    bool ok(true);
 
-    m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' );
-    if ( nullptr == m_zipArchive ) {
+    m_zipArchive = zip_open(file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
+    if (nullptr == m_zipArchive) {
         return false;
     }
 
@@ -126,7 +119,7 @@ bool D3MFExporter::exportArchive( const char *file ) {
     ok |= export3DModel();
     ok |= exportRelations();
 
-    zip_close( m_zipArchive );
+    zip_close(m_zipArchive);
     m_zipArchive = nullptr;
 
     return ok;
@@ -145,7 +138,7 @@ bool D3MFExporter::exportContentTypes() {
     mContentOutput << std::endl;
     mContentOutput << "</Types>";
     mContentOutput << std::endl;
-    exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE );
+    exportContentTyp(XmlTag::CONTENT_TYPES_ARCHIVE);
 
     return true;
 }
@@ -157,20 +150,20 @@ bool D3MFExporter::exportRelations() {
     mRelOutput << std::endl;
     mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
 
-    for ( size_t i = 0; i < mRelations.size(); ++i ) {
-        if ( mRelations[ i ]->target[ 0 ] == '/' ) {
-            mRelOutput << "<Relationship Target=\"" << mRelations[ i ]->target << "\" ";
+    for (size_t i = 0; i < mRelations.size(); ++i) {
+        if (mRelations[i]->target[0] == '/') {
+            mRelOutput << "<Relationship Target=\"" << mRelations[i]->target << "\" ";
         } else {
-            mRelOutput << "<Relationship Target=\"/" << mRelations[ i ]->target << "\" ";
+            mRelOutput << "<Relationship Target=\"/" << mRelations[i]->target << "\" ";
         }
         mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
-        mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />";
+        mRelOutput << "Type=\"" << mRelations[i]->type << "\" />";
         mRelOutput << std::endl;
     }
     mRelOutput << "</Relationships>";
     mRelOutput << std::endl;
 
-    writeRelInfoToFile( "_rels", ".rels" );
+    writeRelInfoToFile("_rels", ".rels");
     mRelOutput.flush();
 
     return true;
@@ -181,8 +174,8 @@ bool D3MFExporter::export3DModel() {
 
     writeHeader();
     mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\""
-            << "xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
-            << std::endl;
+                 << " xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
+                 << std::endl;
     mModelOutput << "<" << XmlTag::resources << ">";
     mModelOutput << std::endl;
 
@@ -192,7 +185,6 @@ bool D3MFExporter::export3DModel() {
 
     writeObjects();
 
-
     mModelOutput << "</" << XmlTag::resources << ">";
     mModelOutput << std::endl;
     writeBuild();
@@ -203,36 +195,36 @@ bool D3MFExporter::export3DModel() {
     info->id = "rel0";
     info->target = "/3D/3DModel.model";
     info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
-    mRelations.push_back( info );
+    mRelations.push_back(info);
 
-    writeModelToArchive( "3D", "3DModel.model" );
+    writeModelToArchive("3D", "3DModel.model");
     mModelOutput.flush();
 
     return true;
 }
 
 void D3MFExporter::writeHeader() {
-    mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF - 8\"?>";
+    mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
     mModelOutput << std::endl;
 }
 
 void D3MFExporter::writeMetaData() {
-    if ( nullptr == mScene->mMetaData ) {
+    if (nullptr == mScene->mMetaData) {
         return;
     }
 
-    const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties );
-    if ( 0 == numMetaEntries ) {
+    const unsigned int numMetaEntries(mScene->mMetaData->mNumProperties);
+    if (0 == numMetaEntries) {
         return;
     }
 
-	const aiString *key = nullptr;
+    const aiString *key = nullptr;
     const aiMetadataEntry *entry(nullptr);
-    for ( size_t i = 0; i < numMetaEntries; ++i ) {
-        mScene->mMetaData->Get( i, key, entry );
-        std::string k( key->C_Str() );
+    for (size_t i = 0; i < numMetaEntries; ++i) {
+        mScene->mMetaData->Get(i, key, entry);
+        std::string k(key->C_Str());
         aiString value;
-        mScene->mMetaData->Get(  k, value );
+        mScene->mMetaData->Get(k, value);
         mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
         mModelOutput << value.C_Str();
         mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
@@ -241,103 +233,114 @@ void D3MFExporter::writeMetaData() {
 
 void D3MFExporter::writeBaseMaterials() {
     mModelOutput << "<basematerials id=\"1\">\n";
-    std::string strName, hexDiffuseColor , tmp;
-    for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) {
-        aiMaterial *mat = mScene->mMaterials[ i ];
+    std::string strName, hexDiffuseColor, tmp;
+    for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
+        aiMaterial *mat = mScene->mMaterials[i];
         aiString name;
-        if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) {
-            strName = "basemat_" + to_string( i );
+        if (mat->Get(AI_MATKEY_NAME, name) != aiReturn_SUCCESS) {
+            strName = "basemat_" + to_string(i);
         } else {
             strName = name.C_Str();
         }
         aiColor4D color;
-        if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) {
+        if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, color) == aiReturn_SUCCESS) {
             hexDiffuseColor.clear();
             tmp.clear();
-            hexDiffuseColor = "#";
-            
-            tmp = DecimalToHexa( color.r );
-            hexDiffuseColor += tmp;
-            tmp = DecimalToHexa( color.g );
-            hexDiffuseColor += tmp;
-            tmp = DecimalToHexa( color.b );
-            hexDiffuseColor += tmp;
-            tmp = DecimalToHexa( color.a );
-            hexDiffuseColor += tmp;
+            // rgbs %
+            if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) {
+
+                hexDiffuseColor = Rgba2Hex(
+                        (int)((ai_real)color.r) * 255,
+                        (int)((ai_real)color.g) * 255,
+                        (int)((ai_real)color.b) * 255,
+                        (int)((ai_real)color.a) * 255,
+                        true);
+
+            } else {
+                hexDiffuseColor = "#";
+                tmp = DecimalToHexa((ai_real)color.r);
+                hexDiffuseColor += tmp;
+                tmp = DecimalToHexa((ai_real)color.g);
+                hexDiffuseColor += tmp;
+                tmp = DecimalToHexa((ai_real)color.b);
+                hexDiffuseColor += tmp;
+                tmp = DecimalToHexa((ai_real)color.a);
+                hexDiffuseColor += tmp;
+            }
         } else {
             hexDiffuseColor = "#FFFFFFFF";
         }
 
-        mModelOutput << "<base name=\""+strName+"\" "+" displaycolor=\""+hexDiffuseColor+"\" />\n";
+        mModelOutput << "<base name=\"" + strName + "\" " + " displaycolor=\"" + hexDiffuseColor + "\" />\n";
     }
     mModelOutput << "</basematerials>\n";
 }
 
 void D3MFExporter::writeObjects() {
-    if ( nullptr == mScene->mRootNode ) {
+    if (nullptr == mScene->mRootNode) {
         return;
     }
 
     aiNode *root = mScene->mRootNode;
-    for ( unsigned int i = 0; i < root->mNumChildren; ++i ) {
-        aiNode *currentNode( root->mChildren[ i ] );
-        if ( nullptr == currentNode ) {
+    for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+        aiNode *currentNode(root->mChildren[i]);
+        if (nullptr == currentNode) {
             continue;
         }
-        mModelOutput << "<" << XmlTag::object << " id=\"" << currentNode->mName.C_Str() << "\" type=\"model\">";
+        mModelOutput << "<" << XmlTag::object << " id=\"" << i + 2 << "\" type=\"model\">";
         mModelOutput << std::endl;
-        for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) {
-            aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ];
-            if ( nullptr == currentMesh ) {
+        for (unsigned int j = 0; j < currentNode->mNumMeshes; ++j) {
+            aiMesh *currentMesh = mScene->mMeshes[currentNode->mMeshes[j]];
+            if (nullptr == currentMesh) {
                 continue;
             }
-            writeMesh( currentMesh );
+            writeMesh(currentMesh);
         }
-        mBuildItems.push_back( i );
+        mBuildItems.push_back(i);
 
         mModelOutput << "</" << XmlTag::object << ">";
         mModelOutput << std::endl;
     }
 }
 
-void D3MFExporter::writeMesh( aiMesh *mesh ) {
-    if ( nullptr == mesh ) {
+void D3MFExporter::writeMesh(aiMesh *mesh) {
+    if (nullptr == mesh) {
         return;
     }
 
     mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
     mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
-    for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) {
-        writeVertex( mesh->mVertices[ i ] );
+    for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+        writeVertex(mesh->mVertices[i]);
     }
     mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
 
-    const unsigned int matIdx( mesh->mMaterialIndex );
+    const unsigned int matIdx(mesh->mMaterialIndex);
 
-    writeFaces( mesh, matIdx );
+    writeFaces(mesh, matIdx);
 
     mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
 }
 
-void D3MFExporter::writeVertex( const aiVector3D &pos ) {
+void D3MFExporter::writeVertex(const aiVector3D &pos) {
     mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
     mModelOutput << std::endl;
 }
 
-void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
-    if ( nullptr == mesh ) {
+void D3MFExporter::writeFaces(aiMesh *mesh, unsigned int matIdx) {
+    if (nullptr == mesh) {
         return;
     }
 
-    if ( !mesh->HasFaces() ) {
+    if (!mesh->HasFaces()) {
         return;
     }
     mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
-    for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) {
-        aiFace &currentFace = mesh->mFaces[ i ];
-        mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\""
-                << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ]
-                << "\" pid=\"1\" p1=\""+to_string(matIdx)+"\" />";
+    for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+        aiFace &currentFace = mesh->mFaces[i];
+        mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[0] << "\" v2=\""
+                     << currentFace.mIndices[1] << "\" v3=\"" << currentFace.mIndices[2]
+                     << "\" pid=\"1\" p1=\"" + to_string(matIdx) + "\" />";
         mModelOutput << std::endl;
     }
     mModelOutput << "</" << XmlTag::triangles << ">";
@@ -347,54 +350,53 @@ void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
 void D3MFExporter::writeBuild() {
     mModelOutput << "<" << XmlTag::build << ">" << std::endl;
 
-    for ( size_t i = 0; i < mBuildItems.size(); ++i ) {
-        mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 1 << "\"/>";
+    for (size_t i = 0; i < mBuildItems.size(); ++i) {
+        mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 2 << "\"/>";
         mModelOutput << std::endl;
     }
     mModelOutput << "</" << XmlTag::build << ">";
     mModelOutput << std::endl;
 }
 
-void D3MFExporter::exportContentTyp( const std::string &filename ) {
-    if ( nullptr == m_zipArchive ) {
-        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+void D3MFExporter::exportContentTyp(const std::string &filename) {
+    if (nullptr == m_zipArchive) {
+        throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
     }
     const std::string entry = filename;
-    zip_entry_open( m_zipArchive, entry.c_str() );
+    zip_entry_open(m_zipArchive, entry.c_str());
 
-    const std::string &exportTxt( mContentOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+    const std::string &exportTxt(mContentOutput.str());
+    zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
 
-    zip_entry_close( m_zipArchive );
+    zip_entry_close(m_zipArchive);
 }
 
-void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) {
-    if ( nullptr == m_zipArchive ) {
-        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+void D3MFExporter::writeModelToArchive(const std::string &folder, const std::string &modelName) {
+    if (nullptr == m_zipArchive) {
+        throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
     }
     const std::string entry = folder + "/" + modelName;
-    zip_entry_open( m_zipArchive, entry.c_str() );
+    zip_entry_open(m_zipArchive, entry.c_str());
 
-    const std::string &exportTxt( mModelOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+    const std::string &exportTxt(mModelOutput.str());
+    zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
 
-    zip_entry_close( m_zipArchive );
+    zip_entry_close(m_zipArchive);
 }
 
-void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) {
-    if ( nullptr == m_zipArchive ) {
-        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+void D3MFExporter::writeRelInfoToFile(const std::string &folder, const std::string &relName) {
+    if (nullptr == m_zipArchive) {
+        throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
     }
     const std::string entry = folder + "/" + relName;
-    zip_entry_open( m_zipArchive, entry.c_str() );
+    zip_entry_open(m_zipArchive, entry.c_str());
 
-    const std::string &exportTxt( mRelOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+    const std::string &exportTxt(mRelOutput.str());
+    zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
 
-    zip_entry_close( m_zipArchive );
+    zip_entry_close(m_zipArchive);
 }
 
-
 } // Namespace D3MF
 } // Namespace Assimp
 

+ 0 - 0
code/3MF/D3MFExporter.h → code/AssetLib/3MF/D3MFExporter.h


+ 138 - 140
code/3MF/D3MFImporter.cpp → code/AssetLib/3MF/D3MFImporter.cpp

@@ -44,24 +44,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "D3MFImporter.h"
 
-#include <assimp/scene.h>
-#include <assimp/IOSystem.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/importerdesc.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
 #include <assimp/ZipArchiveIOSystem.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
 
-#include <string>
-#include <vector>
-#include <map>
 #include <cassert>
+#include <map>
 #include <memory>
+#include <string>
+#include <vector>
 
-#include "D3MFOpcPackage.h"
-#include <assimp/irrXMLWrapper.h>
 #include "3MFXmlTags.h"
+#include "D3MFOpcPackage.h"
 #include <assimp/fast_atof.h>
+#include <assimp/irrXMLWrapper.h>
 
 #include <iomanip>
 
@@ -70,90 +70,90 @@ namespace D3MF {
 
 class XmlSerializer {
 public:
-    using MatArray = std::vector<aiMaterial*>;
+    using MatArray = std::vector<aiMaterial *>;
     using MatId2MatArray = std::map<unsigned int, std::vector<unsigned int>>;
 
-    XmlSerializer(XmlReader* xmlReader)
-    : mMeshes()
-    , mMatArray()
-    , mActiveMatGroup( 99999999 )
-    , mMatId2MatArray()
-    , xmlReader(xmlReader){
-		// empty
+    XmlSerializer(XmlReader *xmlReader) :
+            mMeshes(),
+            mMatArray(),
+            mActiveMatGroup(99999999),
+            mMatId2MatArray(),
+            xmlReader(xmlReader) {
+        // empty
     }
 
     ~XmlSerializer() {
         // empty
     }
 
-    void ImportXml(aiScene* scene) {
-        if ( nullptr == scene ) {
+    void ImportXml(aiScene *scene) {
+        if (nullptr == scene) {
             return;
         }
 
         scene->mRootNode = new aiNode();
-        std::vector<aiNode*> children;
+        std::vector<aiNode *> children;
 
         std::string nodeName;
-        while(ReadToEndElement(D3MF::XmlTag::model)) {
+        while (ReadToEndElement(D3MF::XmlTag::model)) {
             nodeName = xmlReader->getNodeName();
-            if( nodeName == D3MF::XmlTag::object) {
+            if (nodeName == D3MF::XmlTag::object) {
                 children.push_back(ReadObject(scene));
-            } else if( nodeName == D3MF::XmlTag::build) {
-                // 
-            } else if ( nodeName == D3MF::XmlTag::basematerials ) {
+            } else if (nodeName == D3MF::XmlTag::build) {
+                //
+            } else if (nodeName == D3MF::XmlTag::basematerials) {
                 ReadBaseMaterials();
-            } else if ( nodeName == D3MF::XmlTag::meta ) {
+            } else if (nodeName == D3MF::XmlTag::meta) {
                 ReadMetadata();
             }
         }
 
-        if ( scene->mRootNode->mName.length == 0 ) {
-            scene->mRootNode->mName.Set( "3MF" );
+        if (scene->mRootNode->mName.length == 0) {
+            scene->mRootNode->mName.Set("3MF");
         }
 
         // 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 );
+        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>( mMeshes.size());
-        scene->mMeshes = new aiMesh*[scene->mNumMeshes]();
-        std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes);
+        scene->mNumMeshes = static_cast<unsigned int>(mMeshes.size());
+        scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
+        std::copy(mMeshes.begin(), mMeshes.end(), scene->mMeshes);
 
         // import the materials
-        scene->mNumMaterials = static_cast<unsigned int>( mMatArray.size() );
-        if ( 0 != scene->mNumMaterials ) {
-            scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ];
-            std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials );
+        scene->mNumMaterials = static_cast<unsigned int>(mMatArray.size());
+        if (0 != scene->mNumMaterials) {
+            scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
+            std::copy(mMatArray.begin(), mMatArray.end(), scene->mMaterials);
         }
 
         // create the scenegraph
         scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
-        scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren]();
+        scene->mRootNode->mChildren = new aiNode *[scene->mRootNode->mNumChildren]();
         std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
     }
 
 private:
-    aiNode* ReadObject(aiScene* scene) {
+    aiNode *ReadObject(aiScene *scene) {
         std::unique_ptr<aiNode> node(new aiNode());
 
         std::vector<unsigned long> meshIds;
 
-        const char *attrib( nullptr );
+        const char *attrib(nullptr);
         std::string name, type;
-        attrib = xmlReader->getAttributeValue( D3MF::XmlTag::id.c_str() );
-        if ( nullptr != attrib ) {
+        attrib = xmlReader->getAttributeValue(D3MF::XmlTag::id.c_str());
+        if (nullptr != attrib) {
             name = attrib;
         }
-        attrib = xmlReader->getAttributeValue( D3MF::XmlTag::type.c_str() );
-        if ( nullptr != attrib ) {
+        attrib = xmlReader->getAttributeValue(D3MF::XmlTag::type.c_str());
+        if (nullptr != attrib) {
             type = attrib;
         }
 
@@ -162,8 +162,8 @@ private:
 
         size_t meshIdx = mMeshes.size();
 
-        while(ReadToEndElement(D3MF::XmlTag::object)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
+        while (ReadToEndElement(D3MF::XmlTag::object)) {
+            if (xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
                 auto mesh = ReadMesh();
 
                 mesh->mName.Set(name);
@@ -183,11 +183,11 @@ private:
     }
 
     aiMesh *ReadMesh() {
-        aiMesh* mesh = new aiMesh();
-        while(ReadToEndElement(D3MF::XmlTag::mesh)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
+        aiMesh *mesh = new aiMesh();
+        while (ReadToEndElement(D3MF::XmlTag::mesh)) {
+            if (xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
                 ImportVertices(mesh);
-            } else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
+            } else if (xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
                 ImportTriangles(mesh);
             }
         }
@@ -196,24 +196,24 @@ private:
     }
 
     void ReadMetadata() {
-        const std::string name = xmlReader->getAttributeValue( D3MF::XmlTag::meta_name.c_str() );
+        const std::string name = xmlReader->getAttributeValue(D3MF::XmlTag::meta_name.c_str());
         xmlReader->read();
         const std::string value = xmlReader->getNodeData();
 
-        if ( name.empty() ) {
+        if (name.empty()) {
             return;
         }
 
         MetaEntry entry;
         entry.name = name;
         entry.value = value;
-        mMetaData.push_back( entry );
+        mMetaData.push_back(entry);
     }
 
-    void ImportVertices(aiMesh* mesh) {
+    void ImportVertices(aiMesh *mesh) {
         std::vector<aiVector3D> vertices;
-        while(ReadToEndElement(D3MF::XmlTag::vertices)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
+        while (ReadToEndElement(D3MF::XmlTag::vertices)) {
+            if (xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
                 vertices.push_back(ReadVertex());
             }
         }
@@ -233,20 +233,20 @@ private:
         return vertex;
     }
 
-    void ImportTriangles(aiMesh* mesh) {
-         std::vector<aiFace> faces;
-
-         while(ReadToEndElement(D3MF::XmlTag::triangles)) {
-             const std::string nodeName( xmlReader->getNodeName() );
-             if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
-                 faces.push_back(ReadTriangle());
-                 const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::p1.c_str() ) );
-                 if ( nullptr != pidToken ) {
-                     int matIdx( std::atoi( pidToken ) );
-                     mesh->mMaterialIndex = matIdx;
-                 }
-             }
-         }
+    void ImportTriangles(aiMesh *mesh) {
+        std::vector<aiFace> faces;
+
+        while (ReadToEndElement(D3MF::XmlTag::triangles)) {
+            const std::string nodeName(xmlReader->getNodeName());
+            if (xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
+                faces.push_back(ReadTriangle());
+                const char *pidToken(xmlReader->getAttributeValue(D3MF::XmlTag::p1.c_str()));
+                if (nullptr != pidToken) {
+                    int matIdx(std::atoi(pidToken));
+                    mesh->mMaterialIndex = matIdx;
+                }
+            }
+        }
 
         mesh->mNumFaces = static_cast<unsigned int>(faces.size());
         mesh->mFaces = new aiFace[mesh->mNumFaces];
@@ -269,117 +269,115 @@ private:
 
     void ReadBaseMaterials() {
         std::vector<unsigned int> MatIdArray;
-        const char *baseMaterialId( xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_id.c_str() ) );
-        if ( nullptr != baseMaterialId ) {
-            unsigned int id = std::atoi( baseMaterialId );
-            const size_t newMatIdx( mMatArray.size() );
-            if ( id != mActiveMatGroup ) {
+        const char *baseMaterialId(xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_id.c_str()));
+        if (nullptr != baseMaterialId) {
+            unsigned int id = std::atoi(baseMaterialId);
+            const size_t newMatIdx(mMatArray.size());
+            if (id != mActiveMatGroup) {
                 mActiveMatGroup = id;
-                MatId2MatArray::const_iterator it( mMatId2MatArray.find( id ) );
-                if ( mMatId2MatArray.end() == it ) {
+                MatId2MatArray::const_iterator it(mMatId2MatArray.find(id));
+                if (mMatId2MatArray.end() == it) {
                     MatIdArray.clear();
-                    mMatId2MatArray[ id ] = MatIdArray;
+                    mMatId2MatArray[id] = MatIdArray;
                 } else {
                     MatIdArray = it->second;
                 }
             }
-            MatIdArray.push_back( static_cast<unsigned int>( newMatIdx ) );
-            mMatId2MatArray[ mActiveMatGroup ] = MatIdArray;
+            MatIdArray.push_back(static_cast<unsigned int>(newMatIdx));
+            mMatId2MatArray[mActiveMatGroup] = MatIdArray;
         }
 
-        while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) {
-            mMatArray.push_back( readMaterialDef() );
+        while (ReadToEndElement(D3MF::XmlTag::basematerials)) {
+            mMatArray.push_back(readMaterialDef());
         }
     }
 
-    bool parseColor( const char *color, aiColor4D &diffuse ) {
-        if ( nullptr == color ) {
+    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) {
+        const size_t len(strlen(color));
+        if (9 != len && 7 != len) {
             return false;
         }
 
-        const char *buf( color );
-        if ( '#' != *buf ) {
+        const char *buf(color);
+        if ('#' != *buf) {
             return false;
         }
         ++buf;
-        char comp[ 3 ] = { 0,0,'\0' };
+        char comp[3] = { 0, 0, '\0' };
 
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.r = static_cast<ai_real>( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
-
+        diffuse.r = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
+        diffuse.g = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
+        diffuse.b = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
-        if(7 == len)
+        if (7 == len)
             return true;
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
+        diffuse.a = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
         return true;
     }
 
-    void assignDiffuseColor( aiMaterial *mat ) {
-        const char *color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() );
+    void assignDiffuseColor(aiMaterial *mat) {
+        const char *color = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_displaycolor.c_str());
         aiColor4D diffuse;
-        if ( parseColor( color, diffuse ) ) {
-            mat->AddProperty<aiColor4D>( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
+        if (parseColor(color, diffuse)) {
+            mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
         }
-
     }
     aiMaterial *readMaterialDef() {
-        aiMaterial *mat( nullptr );
-        const char *name( nullptr );
-        const std::string nodeName( xmlReader->getNodeName() );
-        if ( nodeName == D3MF::XmlTag::basematerials_base ) {
-            name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() );
+        aiMaterial *mat(nullptr);
+        const char *name(nullptr);
+        const std::string nodeName(xmlReader->getNodeName());
+        if (nodeName == D3MF::XmlTag::basematerials_base) {
+            name = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_name.c_str());
             std::string stdMatName;
             aiString matName;
-            std::string strId( to_string( mActiveMatGroup ) );
+            std::string strId(to_string(mActiveMatGroup));
             stdMatName += "id";
             stdMatName += strId;
             stdMatName += "_";
-            if ( nullptr != name ) {
-                stdMatName += std::string( name );
+            if (nullptr != name) {
+                stdMatName += std::string(name);
             } else {
                 stdMatName += "basemat";
             }
-            matName.Set( stdMatName );
+            matName.Set(stdMatName);
 
             mat = new aiMaterial;
-            mat->AddProperty( &matName, AI_MATKEY_NAME );
+            mat->AddProperty(&matName, AI_MATKEY_NAME);
 
-            assignDiffuseColor( mat );
+            assignDiffuseColor(mat);
         }
 
         return mat;
     }
 
 private:
-    bool ReadToStartElement(const std::string& startTag) {
-        while(xmlReader->read()) {
-            const std::string &nodeName( xmlReader->getNodeName() );
+    bool ReadToStartElement(const std::string &startTag) {
+        while (xmlReader->read()) {
+            const std::string &nodeName(xmlReader->getNodeName());
             if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && nodeName == startTag) {
                 return true;
             } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == startTag) {
@@ -390,9 +388,9 @@ private:
         return false;
     }
 
-    bool ReadToEndElement(const std::string& closeTag) {
-        while(xmlReader->read()) {
-            const std::string &nodeName( xmlReader->getNodeName() );
+    bool ReadToEndElement(const std::string &closeTag) {
+        while (xmlReader->read()) {
+            const std::string &nodeName(xmlReader->getNodeName());
             if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) {
                 return true;
             } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == closeTag) {
@@ -410,11 +408,11 @@ private:
         std::string value;
     };
     std::vector<MetaEntry> mMetaData;
-    std::vector<aiMesh*> mMeshes;
+    std::vector<aiMesh *> mMeshes;
     MatArray mMatArray;
     unsigned int mActiveMatGroup;
     MatId2MatArray mMatId2MatArray;
-    XmlReader* xmlReader;
+    XmlReader *xmlReader;
 };
 
 } //namespace D3MF
@@ -432,8 +430,8 @@ static const aiImporterDesc desc = {
     "3mf"
 };
 
-D3MFImporter::D3MFImporter()
-: BaseImporter() {
+D3MFImporter::D3MFImporter() :
+        BaseImporter() {
     // empty
 }
 
@@ -442,17 +440,17 @@ D3MFImporter::~D3MFImporter() {
 }
 
 bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
-    const std::string extension( GetExtension( filename ) );
-    if(extension == desc.mFileExtensions ) {
+    const std::string extension(GetExtension(filename));
+    if (extension == desc.mFileExtensions) {
         return true;
-    } else if ( !extension.length() || checkSig ) {
-        if ( nullptr == pIOHandler ) {
+    } else if (!extension.length() || checkSig) {
+        if (nullptr == pIOHandler) {
             return false;
         }
-        if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
+        if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
             return false;
         }
-        D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );
+        D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
         return opcPackage.validate();
     }
 
@@ -467,7 +465,7 @@ const aiImporterDesc *D3MFImporter::GetInfo() const {
     return &desc;
 }
 
-void D3MFImporter::InternReadFile( const std::string &filename, aiScene *pScene, IOSystem *pIOHandler ) {
+void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, IOSystem *pIOHandler) {
     D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
 
     std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream()));

+ 0 - 0
code/3MF/D3MFImporter.h → code/AssetLib/3MF/D3MFImporter.h


+ 1 - 1
code/3MF/D3MFOpcPackage.cpp → code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -147,7 +147,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
                 }
             }
 
-            ASSIMP_LOG_DEBUG(rootFile);
+            ASSIMP_LOG_VERBOSE_DEBUG(rootFile);
 
             mZipArchive->Close(fileStream);
 

+ 0 - 0
code/3MF/D3MFOpcPackage.h → code/AssetLib/3MF/D3MFOpcPackage.h


+ 286 - 387
code/AC/ACLoader.cpp → code/AssetLib/AC/ACLoader.cpp

@@ -1,4 +1,3 @@
-
 /*
 ---------------------------------------------------------------------------
 Open Asset Import Library (assimp)
@@ -6,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -44,25 +41,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /** @file Implementation of the AC3D importer class */
 
-
-
 #ifndef ASSIMP_BUILD_NO_AC_IMPORTER
 
 // internal headers
 #include "ACLoader.h"
-#include <assimp/ParsingUtils.h>
-#include <assimp/fast_atof.h>
-#include <assimp/Subdivision.h>
 #include "Common/Importer.h"
 #include <assimp/BaseImporter.h>
-#include <assimp/Importer.hpp>
+#include <assimp/ParsingUtils.h>
+#include <assimp/Subdivision.h>
+#include <assimp/config.h>
+#include <assimp/fast_atof.h>
+#include <assimp/importerdesc.h>
 #include <assimp/light.h>
-#include <assimp/DefaultLogger.hpp>
 #include <assimp/material.h>
 #include <assimp/scene.h>
-#include <assimp/config.h>
+#include <assimp/DefaultLogger.hpp>
 #include <assimp/IOSystem.hpp>
-#include <assimp/importerdesc.h>
+#include <assimp/Importer.hpp>
 #include <memory>
 
 using namespace Assimp;
@@ -82,116 +77,112 @@ static const aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 // skip to the next token
-#define AI_AC_SKIP_TO_NEXT_TOKEN() \
-    if (!SkipSpaces(&buffer)) \
-    { \
-        ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL"); \
-        continue; \
+inline const char *AcSkipToNextToken(const char *buffer) {
+    if (!SkipSpaces(&buffer)) {
+        ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL");
     }
+    return buffer;
+}
 
 // ------------------------------------------------------------------------------------------------
 // read a string (may be enclosed in double quotation marks). buffer must point to "
-#define AI_AC_GET_STRING(out) \
-    if (*buffer == '\0') { \
-        throw DeadlyImportError("AC3D: Unexpected EOF in string"); \
-    } \
-    ++buffer; \
-    const char* sz = buffer; \
-    while ('\"' != *buffer) \
-    { \
-        if (IsLineEnd( *buffer )) \
-        { \
-            ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string"); \
-            out = "ERROR"; \
-            break; \
-        } \
-        ++buffer; \
-    } \
-    if (IsLineEnd( *buffer ))continue; \
-    out = std::string(sz,(unsigned int)(buffer-sz)); \
+inline const char *AcGetString(const char *buffer, std::string &out) {
+    if (*buffer == '\0') {
+        throw DeadlyImportError("AC3D: Unexpected EOF in string");
+    }
+    ++buffer;
+    const char *sz = buffer;
+    while ('\"' != *buffer) {
+        if (IsLineEnd(*buffer)) {
+            ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string");
+            out = "ERROR";
+            break;
+        }
+        ++buffer;
+    }
+    if (IsLineEnd(*buffer)) {
+        return buffer;
+    }
+    out = std::string(sz, (unsigned int)(buffer - sz));
     ++buffer;
 
+    return buffer;
+}
 
 // ------------------------------------------------------------------------------------------------
 // read 1 to n floats prefixed with an optional predefined identifier
-#define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \
-    AI_AC_SKIP_TO_NEXT_TOKEN(); \
-    if (name_length) \
-    { \
-        if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \
-        { \
-            ASSIMP_LOG_ERROR("AC3D: Unexpexted token. " name " was expected."); \
-            continue; \
-        } \
-        buffer += name_length+1; \
-    } \
-    for (unsigned int i = 0; i < num;++i) \
-    { \
-        AI_AC_SKIP_TO_NEXT_TOKEN(); \
-        buffer = fast_atoreal_move<float>(buffer,((float*)out)[i]); \
+template <class T>
+inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name, size_t name_length, size_t num, T *out) {
+    buffer = AcSkipToNextToken(buffer);
+    if (0 != name_length) {
+        if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) {
+            ASSIMP_LOG_ERROR("AC3D: Unexpexted token. " + std::string(name) + " was expected.");
+            return buffer;
+        }
+        buffer += name_length + 1;
+    }
+    for (unsigned int _i = 0; _i < num; ++_i) {
+        buffer = AcSkipToNextToken(buffer);
+        buffer = fast_atoreal_move<float>(buffer, ((float *)out)[_i]);
     }
 
+    return buffer;
+}
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-AC3DImporter::AC3DImporter()
-    : buffer(),
-    configSplitBFCull(),
-    configEvalSubdivision(),
-    mNumMeshes(),
-    mLights(),
-    lights(),
-    groups(),
-    polys(),
-    worlds()
-{
+AC3DImporter::AC3DImporter() :
+        buffer(),
+        configSplitBFCull(),
+        configEvalSubdivision(),
+        mNumMeshes(),
+        mLights(),
+        mLightsCounter(0),
+        mGroupsCounter(0),
+        mPolysCounter(0),
+        mWorldsCounter(0) {
     // nothing to be done here
 }
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-AC3DImporter::~AC3DImporter()
-{
+AC3DImporter::~AC3DImporter() {
     // nothing to be done here
 }
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
+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") {
+    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 CheckMagicToken(pIOHandler, pFile, &token, 1, 0);
     }
     return false;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Loader meta information
-const aiImporterDesc* AC3DImporter::GetInfo () const
-{
+const aiImporterDesc *AC3DImporter::GetInfo() const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get a pointer to the next line from the file
-bool AC3DImporter::GetNextLine( )
-{
+bool AC3DImporter::GetNextLine() {
     SkipLine(&buffer);
     return SkipSpaces(&buffer);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Parse an object section in an AC file
-void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
-{
-    if (!TokenMatch(buffer,"OBJECT",6))
+void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
+    if (!TokenMatch(buffer, "OBJECT", 6))
         return;
 
     SkipSpaces(&buffer);
@@ -199,147 +190,111 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
     ++mNumMeshes;
 
     objects.push_back(Object());
-    Object& obj = objects.back();
+    Object &obj = objects.back();
 
-    aiLight* light = NULL;
-    if (!ASSIMP_strincmp(buffer,"light",5))
-    {
+    aiLight *light = nullptr;
+    if (!ASSIMP_strincmp(buffer, "light", 5)) {
         // This is a light source. Add it to the list
         mLights->push_back(light = new aiLight());
 
         // Return a point light with no attenuation
         light->mType = aiLightSource_POINT;
-        light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f);
+        light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f, 1.f, 1.f);
         light->mAttenuationConstant = 1.f;
 
         // Generate a default name for both the light source and the node
         // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version.
-        light->mName.length = ::ai_snprintf(light->mName.data, MAXLEN, "ACLight_%i",static_cast<unsigned int>(mLights->size())-1);
-        obj.name = std::string( light->mName.data );
+        light->mName.length = ::ai_snprintf(light->mName.data, MAXLEN, "ACLight_%i", static_cast<unsigned int>(mLights->size()) - 1);
+        obj.name = std::string(light->mName.data);
 
-        ASSIMP_LOG_DEBUG("AC3D: Light source encountered");
+        ASSIMP_LOG_VERBOSE_DEBUG("AC3D: Light source encountered");
         obj.type = Object::Light;
-    }
-    else if (!ASSIMP_strincmp(buffer,"group",5))
-    {
+    } else if (!ASSIMP_strincmp(buffer, "group", 5)) {
         obj.type = Object::Group;
-    }
-    else if (!ASSIMP_strincmp(buffer,"world",5))
-    {
+    } else if (!ASSIMP_strincmp(buffer, "world", 5)) {
         obj.type = Object::World;
-    }
-    else obj.type = Object::Poly;
-    while (GetNextLine())
-    {
-        if (TokenMatch(buffer,"kids",4))
-        {
+    } else
+        obj.type = Object::Poly;
+    while (GetNextLine()) {
+        if (TokenMatch(buffer, "kids", 4)) {
             SkipSpaces(&buffer);
-            unsigned int num = strtoul10(buffer,&buffer);
+            unsigned int num = strtoul10(buffer, &buffer);
             GetNextLine();
-            if (num)
-            {
+            if (num) {
                 // load the children of this object recursively
                 obj.children.reserve(num);
                 for (unsigned int i = 0; i < num; ++i)
                     LoadObjectSection(obj.children);
             }
             return;
-        }
-        else if (TokenMatch(buffer,"name",4))
-        {
+        } else if (TokenMatch(buffer, "name", 4)) {
             SkipSpaces(&buffer);
-            AI_AC_GET_STRING(obj.name);
+            buffer = AcGetString(buffer, obj.name);
 
             // If this is a light source, we'll also need to store
             // the name of the node in it.
-            if (light)
-            {
+            if (light) {
                 light->mName.Set(obj.name);
             }
-        }
-        else if (TokenMatch(buffer,"texture",7))
-        {
+        } else if (TokenMatch(buffer, "texture", 7)) {
             SkipSpaces(&buffer);
-            AI_AC_GET_STRING(obj.texture);
-        }
-        else if (TokenMatch(buffer,"texrep",6))
-        {
+            buffer = AcGetString(buffer, obj.texture);
+        } else if (TokenMatch(buffer, "texrep", 6)) {
             SkipSpaces(&buffer);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat);
+            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texRepeat);
             if (!obj.texRepeat.x || !obj.texRepeat.y)
-                obj.texRepeat = aiVector2D (1.f,1.f);
-        }
-        else if (TokenMatch(buffer,"texoff",6))
-        {
+                obj.texRepeat = aiVector2D(1.f, 1.f);
+        } else if (TokenMatch(buffer, "texoff", 6)) {
             SkipSpaces(&buffer);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset);
-        }
-        else if (TokenMatch(buffer,"rot",3))
-        {
+            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &obj.texOffset);
+        } else if (TokenMatch(buffer, "rot", 3)) {
             SkipSpaces(&buffer);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation);
-        }
-        else if (TokenMatch(buffer,"loc",3))
-        {
+            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 9, &obj.rotation);
+        } else if (TokenMatch(buffer, "loc", 3)) {
             SkipSpaces(&buffer);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation);
-        }
-        else if (TokenMatch(buffer,"subdiv",6))
-        {
+            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &obj.translation);
+        } else if (TokenMatch(buffer, "subdiv", 6)) {
             SkipSpaces(&buffer);
-            obj.subDiv = strtoul10(buffer,&buffer);
-        }
-        else if (TokenMatch(buffer,"crease",6))
-        {
+            obj.subDiv = strtoul10(buffer, &buffer);
+        } else if (TokenMatch(buffer, "crease", 6)) {
             SkipSpaces(&buffer);
             obj.crease = fast_atof(buffer);
-        }
-        else if (TokenMatch(buffer,"numvert",7))
-        {
+        } else if (TokenMatch(buffer, "numvert", 7)) {
             SkipSpaces(&buffer);
 
-            unsigned int t = strtoul10(buffer,&buffer);
+            unsigned int t = strtoul10(buffer, &buffer);
             if (t >= AI_MAX_ALLOC(aiVector3D)) {
                 throw DeadlyImportError("AC3D: Too many vertices, would run out of memory");
             }
             obj.vertices.reserve(t);
-            for (unsigned int i = 0; i < t;++i)
-            {
-                if (!GetNextLine())
-                {
+            for (unsigned int i = 0; i < t; ++i) {
+                if (!GetNextLine()) {
                     ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet");
                     break;
-                }
-                else if (!IsNumeric(*buffer))
-                {
+                } else if (!IsNumeric(*buffer)) {
                     ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet");
                     --buffer; // make sure the line is processed a second time
                     break;
                 }
                 obj.vertices.push_back(aiVector3D());
-                aiVector3D& v = obj.vertices.back();
-                AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x);
+                aiVector3D &v = obj.vertices.back();
+                buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 3, &v.x);
             }
-        }
-        else if (TokenMatch(buffer,"numsurf",7))
-        {
+        } else if (TokenMatch(buffer, "numsurf", 7)) {
             SkipSpaces(&buffer);
 
             bool Q3DWorkAround = false;
 
-            const unsigned int t = strtoul10(buffer,&buffer);
+            const unsigned int t = strtoul10(buffer, &buffer);
             obj.surfaces.reserve(t);
-            for (unsigned int i = 0; i < t;++i)
-            {
+            for (unsigned int i = 0; i < t; ++i) {
                 GetNextLine();
-                if (!TokenMatch(buffer,"SURF",4))
-                {
+                if (!TokenMatch(buffer, "SURF", 4)) {
                     // FIX: this can occur for some files - Quick 3D for
                     // example writes no surf chunks
-                    if (!Q3DWorkAround)
-                    {
+                    if (!Q3DWorkAround) {
                         ASSIMP_LOG_WARN("AC3D: SURF token was expected");
-                        ASSIMP_LOG_DEBUG("Continuing with Quick3D Workaround enabled");
+                        ASSIMP_LOG_VERBOSE_DEBUG("Continuing with Quick3D Workaround enabled");
                     }
                     --buffer; // make sure the line is processed a second time
                     // break; --- see fix notes above
@@ -348,27 +303,20 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
                 }
                 SkipSpaces(&buffer);
                 obj.surfaces.push_back(Surface());
-                Surface& surf = obj.surfaces.back();
+                Surface &surf = obj.surfaces.back();
                 surf.flags = strtoul_cppstyle(buffer);
 
-                while (1)
-                {
-                    if(!GetNextLine())
-                    {
+                while (1) {
+                    if (!GetNextLine()) {
                         throw DeadlyImportError("AC3D: Unexpected EOF: surface is incomplete");
                     }
-                    if (TokenMatch(buffer,"mat",3))
-                    {
+                    if (TokenMatch(buffer, "mat", 3)) {
                         SkipSpaces(&buffer);
                         surf.mat = strtoul10(buffer);
-                    }
-                    else if (TokenMatch(buffer,"refs",4))
-                    {
+                    } else if (TokenMatch(buffer, "refs", 4)) {
                         // --- see fix notes above
-                        if (Q3DWorkAround)
-                        {
-                            if (!surf.entries.empty())
-                            {
+                        if (Q3DWorkAround) {
+                            if (!surf.entries.empty()) {
                                 buffer -= 6;
                                 break;
                             }
@@ -380,24 +328,19 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
 
                         obj.numRefs += m;
 
-                        for (unsigned int k = 0; k < m; ++k)
-                        {
-                            if(!GetNextLine())
-                            {
+                        for (unsigned int k = 0; k < m; ++k) {
+                            if (!GetNextLine()) {
                                 ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: surface references are incomplete");
                                 break;
                             }
                             surf.entries.push_back(Surface::SurfaceEntry());
-                            Surface::SurfaceEntry& entry = surf.entries.back();
+                            Surface::SurfaceEntry &entry = surf.entries.back();
 
-                            entry.first = strtoul10(buffer,&buffer);
+                            entry.first = strtoul10(buffer, &buffer);
                             SkipSpaces(&buffer);
-                            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second);
+                            buffer = TAcCheckedLoadFloatArray(buffer, "", 0, 2, &entry.second);
                         }
-                    }
-                    else
-                    {
-
+                    } else {
                         --buffer; // make sure the line is processed a second time
                         break;
                     }
@@ -410,65 +353,58 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
 
 // ------------------------------------------------------------------------------------------------
 // Convert a material from AC3DImporter::Material to aiMaterial
-void AC3DImporter::ConvertMaterial(const Object& object,
-    const Material& matSrc,
-    aiMaterial& matDest)
-{
+void AC3DImporter::ConvertMaterial(const Object &object,
+        const Material &matSrc,
+        aiMaterial &matDest) {
     aiString s;
 
-    if (matSrc.name.length())
-    {
+    if (matSrc.name.length()) {
         s.Set(matSrc.name);
-        matDest.AddProperty(&s,AI_MATKEY_NAME);
+        matDest.AddProperty(&s, AI_MATKEY_NAME);
     }
-    if (object.texture.length())
-    {
+    if (object.texture.length()) {
         s.Set(object.texture);
-        matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+        matDest.AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
 
         // UV transformation
         if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y ||
-            object.texOffset.x        || object.texOffset.y)
-        {
+                object.texOffset.x || object.texOffset.y) {
             aiUVTransform transform;
             transform.mScaling = object.texRepeat;
             transform.mTranslation = object.texOffset;
-            matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
+            matDest.AddProperty(&transform, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
         }
     }
 
-    matDest.AddProperty<aiColor3D>(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE);
-    matDest.AddProperty<aiColor3D>(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT);
-    matDest.AddProperty<aiColor3D>(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE);
-    matDest.AddProperty<aiColor3D>(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR);
+    matDest.AddProperty<aiColor3D>(&matSrc.rgb, 1, AI_MATKEY_COLOR_DIFFUSE);
+    matDest.AddProperty<aiColor3D>(&matSrc.amb, 1, AI_MATKEY_COLOR_AMBIENT);
+    matDest.AddProperty<aiColor3D>(&matSrc.emis, 1, AI_MATKEY_COLOR_EMISSIVE);
+    matDest.AddProperty<aiColor3D>(&matSrc.spec, 1, AI_MATKEY_COLOR_SPECULAR);
 
-    int n;
-    if (matSrc.shin)
-    {
+    int n = -1;
+    if (matSrc.shin) {
         n = aiShadingMode_Phong;
-        matDest.AddProperty<float>(&matSrc.shin,1,AI_MATKEY_SHININESS);
+        matDest.AddProperty<float>(&matSrc.shin, 1, AI_MATKEY_SHININESS);
+    } else {
+        n = aiShadingMode_Gouraud;
     }
-    else n = aiShadingMode_Gouraud;
-    matDest.AddProperty<int>(&n,1,AI_MATKEY_SHADING_MODEL);
+    matDest.AddProperty<int>(&n, 1, AI_MATKEY_SHADING_MODEL);
 
     float f = 1.f - matSrc.trans;
-    matDest.AddProperty<float>(&f,1,AI_MATKEY_OPACITY);
+    matDest.AddProperty<float>(&f, 1, AI_MATKEY_OPACITY);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Converts the loaded data to the internal verbose representation
-aiNode* AC3DImporter::ConvertObjectSection(Object& object,
-    std::vector<aiMesh*>& meshes,
-    std::vector<aiMaterial*>& outMaterials,
-    const std::vector<Material>& materials,
-    aiNode* parent)
-{
-    aiNode* node = new aiNode();
+aiNode *AC3DImporter::ConvertObjectSection(Object &object,
+        std::vector<aiMesh *> &meshes,
+        std::vector<aiMaterial *> &outMaterials,
+        const std::vector<Material> &materials,
+        aiNode *parent) {
+    aiNode *node = new aiNode();
     node->mParent = parent;
-    if (object.vertices.size())
-    {
-        if (!object.surfaces.size() || !object.numRefs)
-        {
+    if (object.vertices.size()) {
+        if (!object.surfaces.size() || !object.numRefs) {
             /* " An object with 7 vertices (no surfaces, no materials defined).
                  This is a good way of getting point data into AC3D.
                  The Vertex->create convex-surface/object can be used on these
@@ -479,17 +415,16 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
              */
 
             ASSIMP_LOG_INFO("AC3D: No surfaces defined in object definition, "
-                "a point list is returned");
+                            "a point list is returned");
 
             meshes.push_back(new aiMesh());
-            aiMesh* mesh = meshes.back();
+            aiMesh *mesh = meshes.back();
 
             mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size();
-            aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
-            aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+            aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+            aiVector3D *verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
 
-            for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts)
-            {
+            for (unsigned int i = 0; i < mesh->mNumVertices; ++i, ++faces, ++verts) {
                 *verts = object.vertices[i];
                 faces->mNumIndices = 1;
                 faces->mIndices = new unsigned int[1];
@@ -502,65 +437,56 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
             mesh->mMaterialIndex = 0;
             outMaterials.push_back(new aiMaterial());
             ConvertMaterial(object, materials[0], *outMaterials.back());
-        }
-        else
-        {
+        } else {
             // need to generate one or more meshes for this object.
             // find out how many different materials we have
-            typedef std::pair< unsigned int, unsigned int > IntPair;
-            typedef std::vector< IntPair > MatTable;
-            MatTable needMat(materials.size(),IntPair(0,0));
+            typedef std::pair<unsigned int, unsigned int> IntPair;
+            typedef std::vector<IntPair> MatTable;
+            MatTable needMat(materials.size(), IntPair(0, 0));
 
-            std::vector<Surface>::iterator it,end = object.surfaces.end();
-            std::vector<Surface::SurfaceEntry>::iterator it2,end2;
+            std::vector<Surface>::iterator it, end = object.surfaces.end();
+            std::vector<Surface::SurfaceEntry>::iterator it2, end2;
 
-            for (it = object.surfaces.begin(); it != end; ++it)
-            {
+            for (it = object.surfaces.begin(); it != end; ++it) {
                 unsigned int idx = (*it).mat;
-                if (idx >= needMat.size())
-                {
+                if (idx >= needMat.size()) {
                     ASSIMP_LOG_ERROR("AC3D: material index is out of range");
                     idx = 0;
                 }
-                if ((*it).entries.empty())
-                {
+                if ((*it).entries.empty()) {
                     ASSIMP_LOG_WARN("AC3D: surface her zero vertex references");
                 }
 
                 // validate all vertex indices to make sure we won't crash here
-                for (it2  = (*it).entries.begin(),
-                     end2 = (*it).entries.end(); it2 != end2; ++it2)
-                {
-                    if ((*it2).first >= object.vertices.size())
-                    {
+                for (it2 = (*it).entries.begin(),
+                    end2 = (*it).entries.end();
+                        it2 != end2; ++it2) {
+                    if ((*it2).first >= object.vertices.size()) {
                         ASSIMP_LOG_WARN("AC3D: Invalid vertex reference");
                         (*it2).first = 0;
                     }
                 }
 
-                if (!needMat[idx].first)++node->mNumMeshes;
+                if (!needMat[idx].first) {
+                    ++node->mNumMeshes;
+                }
 
-                switch ((*it).flags & 0xf)
-                {
+                switch ((*it).flags & 0xf) {
                     // closed line
                 case 0x1:
-
-                    needMat[idx].first  += (unsigned int)(*it).entries.size();
-                    needMat[idx].second += (unsigned int)(*it).entries.size()<<1u;
+                    needMat[idx].first += (unsigned int)(*it).entries.size();
+                    needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
                     break;
 
                     // unclosed line
                 case 0x2:
-
-                    needMat[idx].first  += (unsigned int)(*it).entries.size()-1;
-                    needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u;
+                    needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
+                    needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
                     break;
 
                     // 0 == polygon, else unknown
                 default:
-
-                    if ((*it).flags & 0xf)
-                    {
+                    if ((*it).flags & 0xf) {
                         ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown");
                         (*it).flags &= ~(0xf);
                     }
@@ -571,20 +497,21 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                     needMat[idx].second += (unsigned int)(*it).entries.size();
                 };
             }
-            unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes];
+            unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes];
             unsigned int mat = 0;
             const size_t oldm = meshes.size();
             for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end();
-                cit != cend; ++cit, ++mat)
-            {
-                if (!(*cit).first)continue;
+                    cit != cend; ++cit, ++mat) {
+                if (!(*cit).first) {
+                    continue;
+                }
 
                 // allocate a new aiMesh object
                 *pip++ = (unsigned int)meshes.size();
-                aiMesh* mesh = new aiMesh();
+                aiMesh *mesh = new aiMesh();
                 meshes.push_back(mesh);
 
-                mesh->mMaterialIndex = (unsigned int)outMaterials.size();
+                mesh->mMaterialIndex = static_cast<unsigned int>(outMaterials.size());
                 outMaterials.push_back(new aiMaterial());
                 ConvertMaterial(object, materials[mat], *outMaterials.back());
 
@@ -595,7 +522,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                 } else if (mesh->mNumFaces > AI_MAX_ALLOC(aiFace)) {
                     throw DeadlyImportError("AC3D: Too many faces, would run out of memory");
                 }
-                aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+                aiFace *faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
 
                 mesh->mNumVertices = (*cit).second;
                 if (mesh->mNumVertices == 0) {
@@ -603,35 +530,30 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                 } else if (mesh->mNumVertices > AI_MAX_ALLOC(aiVector3D)) {
                     throw DeadlyImportError("AC3D: Too many vertices, would run out of memory");
                 }
-                aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+                aiVector3D *vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
                 unsigned int cur = 0;
 
                 // allocate UV coordinates, but only if the texture name for the
                 // surface is not empty
-                aiVector3D* uv = NULL;
-                if(object.texture.length())
-                {
+                aiVector3D *uv = nullptr;
+                if (object.texture.length()) {
                     uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
                     mesh->mNumUVComponents[0] = 2;
                 }
 
-                for (it = object.surfaces.begin(); it != end; ++it)
-                {
-                    if (mat == (*it).mat)
-                    {
-                        const Surface& src = *it;
+                for (it = object.surfaces.begin(); it != end; ++it) {
+                    if (mat == (*it).mat) {
+                        const Surface &src = *it;
 
                         // closed polygon
                         unsigned int type = (*it).flags & 0xf;
-                        if (!type)
-                        {
-                            aiFace& face = *faces++;
-                            if((face.mNumIndices = (unsigned int)src.entries.size()))
-                            {
+                        if (!type) {
+                            aiFace &face = *faces++;
+                            face.mNumIndices = (unsigned int)src.entries.size();
+                            if (0 != face.mNumIndices) {
                                 face.mIndices = new unsigned int[face.mNumIndices];
-                                for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices)
-                                {
-                                    const Surface::SurfaceEntry& entry = src.entries[i];
+                                for (unsigned int i = 0; i < face.mNumIndices; ++i, ++vertices) {
+                                    const Surface::SurfaceEntry &entry = src.entries[i];
                                     face.mIndices[i] = cur++;
 
                                     // copy vertex positions
@@ -640,28 +562,23 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                                     }
                                     *vertices = object.vertices[entry.first] + object.translation;
 
-
                                     // copy texture coordinates
-                                    if (uv)
-                                    {
-                                        uv->x =  entry.second.x;
-                                        uv->y =  entry.second.y;
+                                    if (uv) {
+                                        uv->x = entry.second.x;
+                                        uv->y = entry.second.y;
                                         ++uv;
                                     }
                                 }
                             }
-                        }
-                        else
-                        {
+                        } else {
 
-                            it2  = (*it).entries.begin();
+                            it2 = (*it).entries.begin();
 
                             // either a closed or an unclosed line
                             unsigned int tmp = (unsigned int)(*it).entries.size();
-                            if (0x2 == type)--tmp;
-                            for (unsigned int m = 0; m < tmp;++m)
-                            {
-                                aiFace& face = *faces++;
+                            if (0x2 == type) --tmp;
+                            for (unsigned int m = 0; m < tmp; ++m) {
+                                aiFace &face = *faces++;
 
                                 face.mNumIndices = 2;
                                 face.mIndices = new unsigned int[2];
@@ -669,35 +586,31 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                                 face.mIndices[1] = cur++;
 
                                 // copy vertex positions
-                                if (it2 == (*it).entries.end() ) {
+                                if (it2 == (*it).entries.end()) {
                                     throw DeadlyImportError("AC3D: Bad line");
                                 }
                                 ai_assert((*it2).first < object.vertices.size());
                                 *vertices++ = object.vertices[(*it2).first];
 
                                 // copy texture coordinates
-                                if (uv)
-                                {
-                                    uv->x =  (*it2).second.x;
-                                    uv->y =  (*it2).second.y;
+                                if (uv) {
+                                    uv->x = (*it2).second.x;
+                                    uv->y = (*it2).second.y;
                                     ++uv;
                                 }
 
-
-                                if (0x1 == type && tmp-1 == m)
-                                {
+                                if (0x1 == type && tmp - 1 == m) {
                                     // if this is a closed line repeat its beginning now
-                                    it2  = (*it).entries.begin();
-                                }
-                                else ++it2;
+                                    it2 = (*it).entries.begin();
+                                } else
+                                    ++it2;
 
                                 // second point
                                 *vertices++ = object.vertices[(*it2).first];
 
-                                if (uv)
-                                {
-                                    uv->x =  (*it2).second.x;
-                                    uv->y =  (*it2).second.y;
+                                if (uv) {
+                                    uv->x = (*it2).second.x;
+                                    uv->y = (*it2).second.y;
                                     ++uv;
                                 }
                             }
@@ -709,20 +622,18 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
             // Now apply catmull clark subdivision if necessary. We split meshes into
             // materials which is not done by AC3D during smoothing, so we need to
             // collect all meshes using the same material group.
-            if (object.subDiv)  {
+            if (object.subDiv) {
                 if (configEvalSubdivision) {
                     std::unique_ptr<Subdivider> div(Subdivider::Create(Subdivider::CATMULL_CLARKE));
-                    ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: "+object.name);
+                    ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: " + object.name);
 
-                    std::vector<aiMesh*> cpy(meshes.size()-oldm,NULL);
-                    div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true);
-                    std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm);
+                    std::vector<aiMesh *> cpy(meshes.size() - oldm, NULL);
+                    div->Subdivide(&meshes[oldm], cpy.size(), &cpy.front(), object.subDiv, true);
+                    std::copy(cpy.begin(), cpy.end(), meshes.begin() + oldm);
 
                     // previous meshes are deleted vy Subdivide().
-                }
-                else {
-                    ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: "
-                        +object.name);
+                } else {
+                    ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: " + object.name);
                 }
             }
         }
@@ -730,48 +641,42 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
 
     if (object.name.length())
         node->mName.Set(object.name);
-    else
-    {
+    else {
         // generate a name depending on the type of the node
-        switch (object.type)
-        {
+        switch (object.type) {
         case Object::Group:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i",groups++);
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++);
             break;
         case Object::Poly:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i",polys++);
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++);
             break;
         case Object::Light:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i",lights++);
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++);
             break;
 
             // there shouldn't be more than one world, but we don't care
         case Object::World:
-            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i",worlds++);
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++);
             break;
         }
     }
 
-
     // setup the local transformation matrix of the object
     // compute the transformation offset to the parent node
-    node->mTransformation = aiMatrix4x4 ( object.rotation );
+    node->mTransformation = aiMatrix4x4(object.rotation);
 
-    if (object.type == Object::Group || !object.numRefs)
-    {
+    if (object.type == Object::Group || !object.numRefs) {
         node->mTransformation.a4 = object.translation.x;
         node->mTransformation.b4 = object.translation.y;
         node->mTransformation.c4 = object.translation.z;
     }
 
     // add children to the object
-    if (object.children.size())
-    {
+    if (object.children.size()) {
         node->mNumChildren = (unsigned int)object.children.size();
-        node->mChildren = new aiNode*[node->mNumChildren];
-        for (unsigned int i = 0; i < node->mNumChildren;++i)
-        {
-            node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node);
+        node->mChildren = new aiNode *[node->mNumChildren];
+        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+            node->mChildren[i] = ConvertObjectSection(object.children[i], meshes, outMaterials, materials, node);
         }
     }
 
@@ -779,40 +684,39 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
 }
 
 // ------------------------------------------------------------------------------------------------
-void AC3DImporter::SetupProperties(const Importer* pImp)
-{
-    configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false;
-    configEvalSubdivision =  pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false;
+void AC3DImporter::SetupProperties(const Importer *pImp) {
+    configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL, 1) ? true : false;
+    configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION, 1) ? true : false;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void AC3DImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
-    std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+void AC3DImporter::InternReadFile(const std::string &pFile,
+        aiScene *pScene, IOSystem *pIOHandler) {
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
 
     // Check whether we can read from the file
-    if( file.get() == NULL)
-        throw DeadlyImportError( "Failed to open AC3D file " + pFile + ".");
+    if (file.get() == nullptr) {
+        throw DeadlyImportError("Failed to open AC3D file " + pFile + ".");
+    }
 
     // allocate storage and copy the contents of the file to a memory buffer
     std::vector<char> mBuffer2;
-    TextFileToBuffer(file.get(),mBuffer2);
+    TextFileToBuffer(file.get(), mBuffer2);
 
     buffer = &mBuffer2[0];
     mNumMeshes = 0;
 
-    lights = polys = worlds = groups = 0;
+    mLightsCounter = mPolysCounter = mWorldsCounter = mGroupsCounter = 0;
 
-    if (::strncmp(buffer,"AC3D",4)) {
+    if (::strncmp(buffer, "AC3D", 4)) {
         throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
     }
 
     // print the file format version to the console
-    unsigned int version = HexDigitToDecimal( buffer[4] );
+    unsigned int version = HexDigitToDecimal(buffer[4]);
     char msg[3];
-    ASSIMP_itoa10(msg,3,version);
+    ASSIMP_itoa10(msg, 3, version);
     ASSIMP_LOG_INFO_F("AC3D file format version: ", msg);
 
     std::vector<Material> materials;
@@ -821,89 +725,84 @@ void AC3DImporter::InternReadFile( const std::string& pFile,
     std::vector<Object> rootObjects;
     rootObjects.reserve(5);
 
-    std::vector<aiLight*> lights;
-    mLights = & lights;
+    std::vector<aiLight *> lights;
+    mLights = &lights;
 
-    while (GetNextLine())
-    {
-        if (TokenMatch(buffer,"MATERIAL",8))
-        {
+    while (GetNextLine()) {
+        if (TokenMatch(buffer, "MATERIAL", 8)) {
             materials.push_back(Material());
-            Material& mat = materials.back();
+            Material &mat = materials.back();
 
             // manually parse the material ... sscanf would use the buldin atof ...
             // Format: (name) rgb %f %f %f  amb %f %f %f  emis %f %f %f  spec %f %f %f  shi %d  trans %f
 
-            AI_AC_SKIP_TO_NEXT_TOKEN();
-            if ('\"' == *buffer)
-            {
-                AI_AC_GET_STRING(mat.name);
-                AI_AC_SKIP_TO_NEXT_TOKEN();
+            buffer = AcSkipToNextToken(buffer);
+            if ('\"' == *buffer) {
+                buffer = AcGetString(buffer, mat.name);
+                buffer = AcSkipToNextToken(buffer);
             }
 
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin);
-            AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans);
+            buffer = TAcCheckedLoadFloatArray(buffer, "rgb", 3, 3, &mat.rgb);
+            buffer = TAcCheckedLoadFloatArray(buffer, "amb", 3, 3, &mat.amb);
+            buffer = TAcCheckedLoadFloatArray(buffer, "emis", 4, 3, &mat.emis);
+            buffer = TAcCheckedLoadFloatArray(buffer, "spec", 4, 3, &mat.spec);
+            buffer = TAcCheckedLoadFloatArray(buffer, "shi", 3, 1, &mat.shin);
+            buffer = TAcCheckedLoadFloatArray(buffer, "trans", 5, 1, &mat.trans);
         }
         LoadObjectSection(rootObjects);
     }
 
-    if (rootObjects.empty() || !mNumMeshes)
-    {
+    if (rootObjects.empty() || !mNumMeshes) {
         throw DeadlyImportError("AC3D: No meshes have been loaded");
     }
-    if (materials.empty())
-    {
+    if (materials.empty()) {
         ASSIMP_LOG_WARN("AC3D: No material has been found");
         materials.push_back(Material());
     }
 
-    mNumMeshes += (mNumMeshes>>2u) + 1;
-    std::vector<aiMesh*> meshes;
+    mNumMeshes += (mNumMeshes >> 2u) + 1;
+    std::vector<aiMesh *> meshes;
     meshes.reserve(mNumMeshes);
 
-    std::vector<aiMaterial*> omaterials;
+    std::vector<aiMaterial *> omaterials;
     materials.reserve(mNumMeshes);
 
     // generate a dummy root if there are multiple objects on the top layer
-    Object* root;
+    Object *root;
     if (1 == rootObjects.size())
         root = &rootObjects[0];
-    else
-    {
+    else {
         root = new Object();
     }
 
     // now convert the imported stuff to our output data structure
-    pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials);
-    if (1 != rootObjects.size())delete root;
+    pScene->mRootNode = ConvertObjectSection(*root, meshes, omaterials, materials);
+    if (1 != rootObjects.size()) {
+        delete root;
+    }
 
-    if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4))
+    if (!::strncmp(pScene->mRootNode->mName.data, "Node", 4)) {
         pScene->mRootNode->mName.Set("<AC3DWorld>");
+    }
 
     // copy meshes
-    if (meshes.empty())
-    {
+    if (meshes.empty()) {
         throw DeadlyImportError("An unknown error occurred during converting");
     }
     pScene->mNumMeshes = (unsigned int)meshes.size();
-    pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
-    ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*));
+    pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
+    ::memcpy(pScene->mMeshes, &meshes[0], pScene->mNumMeshes * sizeof(void *));
 
     // copy materials
     pScene->mNumMaterials = (unsigned int)omaterials.size();
-    pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
-    ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*));
+    pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
+    ::memcpy(pScene->mMaterials, &omaterials[0], pScene->mNumMaterials * sizeof(void *));
 
     // copy lights
     pScene->mNumLights = (unsigned int)lights.size();
-    if (lights.size())
-    {
-        pScene->mLights = new aiLight*[lights.size()];
-        ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*));
+    if (lights.size()) {
+        pScene->mLights = new aiLight *[lights.size()];
+        ::memcpy(pScene->mLights, &lights[0], lights.size() * sizeof(void *));
     }
 }
 

+ 34 - 67
code/AC/ACLoader.h → code/AssetLib/AC/ACLoader.h

@@ -56,29 +56,20 @@ struct aiMesh;
 struct aiMaterial;
 struct aiLight;
 
-
-namespace Assimp    {
+namespace Assimp {
 
 // ---------------------------------------------------------------------------
 /** AC3D (*.ac) importer class
 */
-class AC3DImporter : public BaseImporter
-{
+class AC3DImporter : public BaseImporter {
 public:
     AC3DImporter();
     ~AC3DImporter();
 
-
-
     // Represents an AC3D material
-    struct Material
-    {
-        Material()
-            :   rgb     (0.6f,0.6f,0.6f)
-            ,   spec    (1.f,1.f,1.f)
-            ,   shin    (0.f)
-            ,   trans   (0.f)
-        {}
+    struct Material {
+        Material() :
+                rgb(0.6f, 0.6f, 0.6f), spec(1.f, 1.f, 1.f), shin(0.f), trans(0.f) {}
 
         // base color of the material
         aiColor3D rgb;
@@ -103,43 +94,25 @@ public:
     };
 
     // Represents an AC3D surface
-    struct Surface
-    {
-        Surface()
-            :   mat     (0)
-            ,   flags   (0)
-        {}
+    struct Surface {
+        Surface() :
+                mat(0), flags(0) {}
 
-        unsigned int mat,flags;
+        unsigned int mat, flags;
 
-        typedef std::pair<unsigned int, aiVector2D > SurfaceEntry;
-        std::vector< SurfaceEntry > entries;
+        typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
+        std::vector<SurfaceEntry> entries;
     };
 
     // Represents an AC3D object
-    struct Object
-    {
-        Object()
-            :   type    (World)
-            ,   name( "" )
-            ,   children()
-            ,   texture( "" )
-            ,   texRepeat( 1.f, 1.f )
-            ,   texOffset( 0.0f, 0.0f )
-            ,   rotation()
-            ,   translation()
-            ,   vertices()
-            ,   surfaces()
-            ,   numRefs (0)
-            ,   subDiv  (0)
-            ,   crease()
-        {}
+    struct Object {
+        Object() :
+                type(World), name(""), children(), texture(""), texRepeat(1.f, 1.f), texOffset(0.0f, 0.0f), rotation(), translation(), vertices(), surfaces(), numRefs(0), subDiv(0), crease() {}
 
         // Type description
-        enum Type
-        {
+        enum Type {
             World = 0x0,
-            Poly  = 0x1,
+            Poly = 0x1,
             Group = 0x2,
             Light = 0x4
         } type;
@@ -179,37 +152,33 @@ public:
         float crease;
     };
 
-
 public:
-
     // -------------------------------------------------------------------
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details.
      */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+            bool checkSig) const;
 
 protected:
-
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc *GetInfo() const;
 
     // -------------------------------------------------------------------
     /** 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);
 
     // -------------------------------------------------------------------
     /** 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);
 
 private:
-
     // -------------------------------------------------------------------
     /** Get the next line from the file.
      *  @return false if the end of the file was reached*/
@@ -220,7 +189,7 @@ private:
      *  load subobjects, the method returns after a 'kids 0' was
      *  encountered.
      *  @objects List of output objects*/
-    void LoadObjectSection(std::vector<Object>& objects);
+    void LoadObjectSection(std::vector<Object> &objects);
 
     // -------------------------------------------------------------------
     /** Convert all objects into meshes and nodes.
@@ -229,26 +198,24 @@ private:
      *  @param outMaterials List of output materials
      *  @param materials Material list
      *  @param Scenegraph node for the object */
-    aiNode* ConvertObjectSection(Object& object,
-        std::vector<aiMesh*>& meshes,
-        std::vector<aiMaterial*>& outMaterials,
-        const std::vector<Material>& materials,
-        aiNode* parent = NULL);
+    aiNode *ConvertObjectSection(Object &object,
+            std::vector<aiMesh *> &meshes,
+            std::vector<aiMaterial *> &outMaterials,
+            const std::vector<Material> &materials,
+            aiNode *parent = nullptr);
 
     // -------------------------------------------------------------------
     /** Convert a material
      *  @param object Current object
      *  @param matSrc Source material description
      *  @param matDest Destination material to be filled */
-    void ConvertMaterial(const Object& object,
-        const Material& matSrc,
-        aiMaterial& matDest);
+    void ConvertMaterial(const Object &object,
+            const Material &matSrc,
+            aiMaterial &matDest);
 
 private:
-
-
     // points to the next data line
-    const char* buffer;
+    const char *buffer;
 
     // Configuration option: if enabled, up to two meshes
     // are generated per material: those faces who have
@@ -265,10 +232,10 @@ private:
     unsigned int mNumMeshes;
 
     // current list of light sources
-    std::vector<aiLight*>* mLights;
+    std::vector<aiLight *> *mLights;
 
     // name counters
-    unsigned int lights, groups, polys, worlds;
+    unsigned int mLightsCounter, mGroupsCounter, mPolysCounter, mWorldsCounter;
 };
 
 } // end of namespace Assimp

+ 672 - 0
code/AssetLib/AMF/AMFImporter.cpp

@@ -0,0 +1,672 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/// \file AMFImporter.cpp
+/// \brief AMF-format files importer for Assimp: main algorithm implementation.
+/// \date 2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+
+// Header files, Assimp.
+#include "AMFImporter.hpp"
+#include "AMFImporter_Macro.hpp"
+
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/fast_atof.h>
+
+// Header files, stdlib.
+#include <memory>
+
+namespace Assimp {
+
+/// \var aiImporterDesc AMFImporter::Description
+/// Conastant which hold importer description
+const aiImporterDesc AMFImporter::Description = {
+    "Additive manufacturing file format(AMF) Importer",
+    "smalcom",
+    "",
+    "See documentation in source code. Chapter: Limitations.",
+    aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+    0,
+    0,
+    0,
+    0,
+    "amf"
+};
+
+void AMFImporter::Clear() {
+    mNodeElement_Cur = nullptr;
+    mUnit.clear();
+    mMaterial_Converted.clear();
+    mTexture_Converted.clear();
+    // Delete all elements
+    if (!mNodeElement_List.empty()) {
+        for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
+            delete ne;
+        }
+
+        mNodeElement_List.clear();
+    }
+}
+
+AMFImporter::~AMFImporter() {
+    if (mReader != nullptr) delete mReader;
+    // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
+    Clear();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: find set ************************************************************/
+/*********************************************************************************************************************************************/
+
+bool AMFImporter::Find_NodeElement(const std::string &pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement **pNodeElement) const {
+    for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
+        if ((ne->ID == pID) && (ne->Type == pType)) {
+            if (pNodeElement != nullptr) *pNodeElement = ne;
+
+            return true;
+        }
+    } // for(CAMFImporter_NodeElement* ne: mNodeElement_List)
+
+    return false;
+}
+
+bool AMFImporter::Find_ConvertedNode(const std::string &pID, std::list<aiNode *> &pNodeList, aiNode **pNode) const {
+    aiString node_name(pID.c_str());
+
+    for (aiNode *node : pNodeList) {
+        if (node->mName == node_name) {
+            if (pNode != nullptr) *pNode = node;
+
+            return true;
+        }
+    } // for(aiNode* node: pNodeList)
+
+    return false;
+}
+
+bool AMFImporter::Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const {
+    for (const SPP_Material &mat : mMaterial_Converted) {
+        if (mat.ID == pID) {
+            if (pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
+
+            return true;
+        }
+    } // for(const SPP_Material& mat: mMaterial_Converted)
+
+    return false;
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: throw set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::Throw_CloseNotFound(const std::string &pNode) {
+    throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
+}
+
+void AMFImporter::Throw_IncorrectAttr(const std::string &pAttrName) {
+    throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
+}
+
+void AMFImporter::Throw_IncorrectAttrValue(const std::string &pAttrName) {
+    throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
+}
+
+void AMFImporter::Throw_MoreThanOnceDefined(const std::string &pNodeType, const std::string &pDescription) {
+    throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
+}
+
+void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
+    throw DeadlyImportError("Not found node with name \"" + pID + "\".");
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************* Functions: XML set ************************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::XML_CheckNode_MustHaveChildren() {
+    if (mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
+}
+
+void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string &pParentNodeName) {
+    static const size_t Uns_Skip_Len = 3;
+    const char *Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
+
+    static bool skipped_before[Uns_Skip_Len] = { false, false, false };
+
+    std::string nn(mReader->getNodeName());
+    bool found = false;
+    bool close_found = false;
+    size_t sk_idx;
+
+    for (sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++) {
+        if (nn != Uns_Skip[sk_idx]) continue;
+
+        found = true;
+        if (mReader->isEmptyElement()) {
+            close_found = true;
+
+            goto casu_cres;
+        }
+
+        while (mReader->read()) {
+            if ((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName())) {
+                close_found = true;
+
+                goto casu_cres;
+            }
+        }
+    } // for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
+
+casu_cres:
+
+    if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
+    if (!close_found) Throw_CloseNotFound(nn);
+
+    if (!skipped_before[sk_idx]) {
+        skipped_before[sk_idx] = true;
+        ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
+    }
+}
+
+bool AMFImporter::XML_SearchNode(const std::string &pNodeName) {
+    while (mReader->read()) {
+        if ((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
+    }
+
+    return false;
+}
+
+bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx) {
+    std::string val(mReader->getAttributeValue(pAttrIdx));
+
+    if ((val == "false") || (val == "0"))
+        return false;
+    else if ((val == "true") || (val == "1"))
+        return true;
+    else
+        throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
+}
+
+float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx) {
+    std::string val;
+    float tvalf;
+
+    ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
+    fast_atoreal_move(val.c_str(), tvalf, false);
+
+    return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx) {
+    return strtoul10(mReader->getAttributeValue(pAttrIdx));
+}
+
+float AMFImporter::XML_ReadNode_GetVal_AsFloat() {
+    std::string val;
+    float tvalf;
+
+    if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
+    if (mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
+
+    ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
+    fast_atoreal_move(val.c_str(), tvalf, false);
+
+    return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32() {
+    if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
+    if (mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
+
+    return strtoul10(mReader->getNodeData());
+}
+
+void AMFImporter::XML_ReadNode_GetVal_AsString(std::string &pValue) {
+    if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
+    if (mReader->getNodeType() != irr::io::EXN_TEXT)
+        throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
+
+    pValue = mReader->getNodeData();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: parse set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement *pNode) {
+    mNodeElement_Cur->Child.push_back(pNode); // add new element to current element child list.
+    mNodeElement_Cur = pNode; // switch current element to new one.
+}
+
+void AMFImporter::ParseHelper_Node_Exit() {
+    // check if we can walk up.
+    if (mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
+}
+
+void AMFImporter::ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString) {
+    size_t instr_len;
+
+    pOutString.clear();
+    instr_len = strlen(pInStr);
+    if (!instr_len) return;
+
+    pOutString.reserve(instr_len * 3 / 2);
+    // check and correct floats in format ".x". Must be "x.y".
+    if (pInStr[0] == '.') pOutString.push_back('0');
+
+    pOutString.push_back(pInStr[0]);
+    for (size_t ci = 1; ci < instr_len; ci++) {
+        if ((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t'))) {
+            pOutString.push_back('0');
+            pOutString.push_back('.');
+        } else {
+            pOutString.push_back(pInStr[ci]);
+        }
+    }
+}
+
+static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) {
+    return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
+}
+
+void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector<uint8_t> &pOutputData) const {
+    // With help from
+    // René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
+    const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    uint8_t tidx = 0;
+    uint8_t arr4[4], arr3[3];
+
+    // check input data
+    if (pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
+    // prepare output place
+    pOutputData.clear();
+    pOutputData.reserve(pInputBase64.size() / 4 * 3);
+
+    for (size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--) {
+        if (ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) {
+            arr4[tidx++] = pInputBase64[in_idx++];
+            if (tidx == 4) {
+                for (tidx = 0; tidx < 4; tidx++)
+                    arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
+
+                arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+                arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+                arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+                for (tidx = 0; tidx < 3; tidx++)
+                    pOutputData.push_back(arr3[tidx]);
+
+                tidx = 0;
+            } // if(tidx == 4)
+        } // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
+        else {
+            in_idx++;
+        } // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
+    }
+
+    if (tidx) {
+        for (uint8_t i = tidx; i < 4; i++)
+            arr4[i] = 0;
+        for (uint8_t i = 0; i < 4; i++)
+            arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
+
+        arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+        arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+        arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+        for (uint8_t i = 0; i < (tidx - 1); i++)
+            pOutputData.push_back(arr3[i]);
+    }
+}
+
+void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) {
+    irr::io::IrrXMLReader *OldReader = mReader; // store current XMLreader.
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
+
+    // Check whether we can read from the file
+    if (file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
+
+    // generate a XML reader for it
+    std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
+    mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+    if (!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
+    //
+    // start reading
+    // search for root tag <amf>
+    if (XML_SearchNode("amf"))
+        ParseNode_Root();
+    else
+        throw DeadlyImportError("Root node \"amf\" not found.");
+
+    delete mReader;
+    // restore old XMLreader
+    mReader = OldReader;
+}
+
+// <amf
+// unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
+// version="" - Version of file format.
+// >
+// </amf>
+// Root XML element.
+// Multi elements - No.
+void AMFImporter::ParseNode_Root() {
+    std::string unit, version;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <amf>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
+    MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND_WSKIP;
+
+    // Check attributes
+    if (!mUnit.empty()) {
+        if ((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
+    }
+
+    // create root node element.
+    ne = new CAMFImporter_NodeElement_Root(nullptr);
+    mNodeElement_Cur = ne; // set first "current" element
+    // and assign attribute's values
+    ((CAMFImporter_NodeElement_Root *)ne)->Unit = unit;
+    ((CAMFImporter_NodeElement_Root *)ne)->Version = version;
+
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        MACRO_NODECHECK_LOOPBEGIN("amf");
+        if (XML_CheckNode_NameEqual("object")) {
+            ParseNode_Object();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("material")) {
+            ParseNode_Material();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("texture")) {
+            ParseNode_Texture();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("constellation")) {
+            ParseNode_Constellation();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("metadata")) {
+            ParseNode_Metadata();
+            continue;
+        }
+        MACRO_NODECHECK_LOOPEND("amf");
+        mNodeElement_Cur = ne; // force restore "current" element
+    } // if(!mReader->isEmptyElement())
+
+    mNodeElement_List.push_back(ne); // add to node element list because its a new object in graph.
+}
+
+// <constellation
+// id="" - The Object ID of the new constellation being defined.
+// >
+// </constellation>
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - <amf>.
+void AMFImporter::ParseNode_Constellation() {
+    std::string id;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <constellation>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+
+    // create and if needed - define new grouping object.
+    ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
+
+    CAMFImporter_NodeElement_Constellation &als = *((CAMFImporter_NodeElement_Constellation *)ne); // alias for convenience
+
+    if (!id.empty()) als.ID = id;
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        ParseHelper_Node_Enter(ne);
+        MACRO_NODECHECK_LOOPBEGIN("constellation");
+        if (XML_CheckNode_NameEqual("instance")) {
+            ParseNode_Instance();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("metadata")) {
+            ParseNode_Metadata();
+            continue;
+        }
+        MACRO_NODECHECK_LOOPEND("constellation");
+        ParseHelper_Node_Exit();
+    } // if(!mReader->isEmptyElement())
+    else {
+        mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    } // if(!mReader->isEmptyElement()) else
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <instance
+// objectid="" - The Object ID of the new constellation being defined.
+// >
+// </instance>
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - <amf>.
+void AMFImporter::ParseNode_Instance() {
+    std::string objectid;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <constellation>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+
+    // used object id must be defined, check that.
+    if (objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
+    // create and define new grouping object.
+    ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
+
+    CAMFImporter_NodeElement_Instance &als = *((CAMFImporter_NodeElement_Instance *)ne); // alias for convenience
+
+    als.ObjectID = objectid;
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        bool read_flag[6] = { false, false, false, false, false, false };
+
+        als.Delta.Set(0, 0, 0);
+        als.Rotation.Set(0, 0, 0);
+        ParseHelper_Node_Enter(ne);
+        MACRO_NODECHECK_LOOPBEGIN("instance");
+        MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
+        MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
+        MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
+        MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
+        MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
+        MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
+        MACRO_NODECHECK_LOOPEND("instance");
+        ParseHelper_Node_Exit();
+        // also convert degrees to radians.
+        als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
+        als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
+        als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
+    } // if(!mReader->isEmptyElement())
+    else {
+        mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    } // if(!mReader->isEmptyElement()) else
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <object
+// id="" - A unique ObjectID for the new object being defined.
+// >
+// </object>
+// An object definition.
+// Multi elements - Yes.
+// Parent element - <amf>.
+void AMFImporter::ParseNode_Object() {
+    std::string id;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <object>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+
+    // create and if needed - define new geometry object.
+    ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
+
+    CAMFImporter_NodeElement_Object &als = *((CAMFImporter_NodeElement_Object *)ne); // alias for convenience
+
+    if (!id.empty()) als.ID = id;
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        bool col_read = false;
+
+        ParseHelper_Node_Enter(ne);
+        MACRO_NODECHECK_LOOPBEGIN("object");
+        if (XML_CheckNode_NameEqual("color")) {
+            // Check if color already defined for object.
+            if (col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <object>.");
+            // read data and set flag about it
+            ParseNode_Color();
+            col_read = true;
+
+            continue;
+        }
+
+        if (XML_CheckNode_NameEqual("mesh")) {
+            ParseNode_Mesh();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("metadata")) {
+            ParseNode_Metadata();
+            continue;
+        }
+        MACRO_NODECHECK_LOOPEND("object");
+        ParseHelper_Node_Exit();
+    } // if(!mReader->isEmptyElement())
+    else {
+        mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    } // if(!mReader->isEmptyElement()) else
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <metadata
+// type="" - The type of the attribute.
+// >
+// </metadata>
+// Specify additional information about an entity.
+// Multi elements - Yes.
+// Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
+//
+// Reserved types are:
+// "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
+// "Description" - A description of the content of the entity
+// "URL" - A link to an external resource relating to the entity
+// "Author" - Specifies the name(s) of the author(s) of the entity
+// "Company" - Specifying the company generating the entity
+// "CAD" - specifies the name of the originating CAD software and version
+// "Revision" - specifies the revision of the entity
+// "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
+// "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
+void AMFImporter::ParseNode_Metadata() {
+    std::string type, value;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // read attribute
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+    // and value of node.
+    value = mReader->getNodeData();
+    // Create node element and assign read data.
+    ne = new CAMFImporter_NodeElement_Metadata(mNodeElement_Cur);
+    ((CAMFImporter_NodeElement_Metadata *)ne)->Type = type;
+    ((CAMFImporter_NodeElement_Metadata *)ne)->Value = value;
+    mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+/*********************************************************************************************************************************************/
+/******************************************************** Functions: BaseImporter set ********************************************************/
+/*********************************************************************************************************************************************/
+
+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.length() || pCheckSig) {
+        const char *tokens[] = { "<amf" };
+
+        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+    }
+
+    return false;
+}
+
+void AMFImporter::GetExtensionList(std::set<std::string> &pExtensionList) {
+    pExtensionList.insert("amf");
+}
+
+const aiImporterDesc *AMFImporter::GetInfo() const {
+    return &Description;
+}
+
+void AMFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    Clear(); // delete old graph.
+    ParseFile(pFile, pIOHandler);
+    Postprocess_BuildScene(pScene);
+    // scene graph is ready, exit.
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 0 - 0
code/AMF/AMFImporter.hpp → code/AssetLib/AMF/AMFImporter.hpp


+ 357 - 0
code/AssetLib/AMF/AMFImporter_Geometry.cpp

@@ -0,0 +1,357 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/// \file AMFImporter_Geometry.cpp
+/// \brief Parsing data from geometry nodes.
+/// \date 2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+
+#include "AMFImporter.hpp"
+#include "AMFImporter_Macro.hpp"
+
+namespace Assimp
+{
+
+// <mesh>
+// </mesh>
+// A 3D mesh hull.
+// Multi elements - Yes.
+// Parent element - <object>.
+void AMFImporter::ParseNode_Mesh()
+{
+CAMFImporter_NodeElement* ne;
+
+	// create new mesh object.
+	ne = new CAMFImporter_NodeElement_Mesh(mNodeElement_Cur);
+	// Check for child nodes
+	if(!mReader->isEmptyElement())
+	{
+		bool vert_read = false;
+
+		ParseHelper_Node_Enter(ne);
+		MACRO_NODECHECK_LOOPBEGIN("mesh");
+			if(XML_CheckNode_NameEqual("vertices"))
+			{
+				// Check if data already defined.
+				if(vert_read) Throw_MoreThanOnceDefined("vertices", "Only one vertices set can be defined for <mesh>.");
+				// read data and set flag about it
+				ParseNode_Vertices();
+				vert_read = true;
+
+				continue;
+			}
+
+			if(XML_CheckNode_NameEqual("volume")) { ParseNode_Volume(); continue; }
+		MACRO_NODECHECK_LOOPEND("mesh");
+		ParseHelper_Node_Exit();
+	}// if(!mReader->isEmptyElement())
+	else
+	{
+		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+	}// if(!mReader->isEmptyElement()) else
+
+	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+// <vertices>
+// </vertices>
+// The list of vertices to be used in defining triangles.
+// Multi elements - No.
+// Parent element - <mesh>.
+void AMFImporter::ParseNode_Vertices()
+{
+CAMFImporter_NodeElement* ne;
+
+	// create new mesh object.
+	ne = new CAMFImporter_NodeElement_Vertices(mNodeElement_Cur);
+	// Check for child nodes
+	if(!mReader->isEmptyElement())
+	{
+		ParseHelper_Node_Enter(ne);
+		MACRO_NODECHECK_LOOPBEGIN("vertices");
+			if(XML_CheckNode_NameEqual("vertex")) { ParseNode_Vertex(); continue; }
+		MACRO_NODECHECK_LOOPEND("vertices");
+		ParseHelper_Node_Exit();
+	}// if(!mReader->isEmptyElement())
+	else
+	{
+		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+	}// if(!mReader->isEmptyElement()) else
+
+	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+// <vertex>
+// </vertex>
+// A vertex to be referenced in triangles.
+// Multi elements - Yes.
+// Parent element - <vertices>.
+void AMFImporter::ParseNode_Vertex()
+{
+CAMFImporter_NodeElement* ne;
+
+	// create new mesh object.
+	ne = new CAMFImporter_NodeElement_Vertex(mNodeElement_Cur);
+	// Check for child nodes
+	if(!mReader->isEmptyElement())
+	{
+		bool col_read = false;
+		bool coord_read = false;
+
+		ParseHelper_Node_Enter(ne);
+		MACRO_NODECHECK_LOOPBEGIN("vertex");
+			if(XML_CheckNode_NameEqual("color"))
+			{
+				// Check if data already defined.
+				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <vertex>.");
+				// read data and set flag about it
+				ParseNode_Color();
+				col_read = true;
+
+				continue;
+			}
+
+			if(XML_CheckNode_NameEqual("coordinates"))
+			{
+				// Check if data already defined.
+				if(coord_read) Throw_MoreThanOnceDefined("coordinates", "Only one coordinates set can be defined for <vertex>.");
+				// read data and set flag about it
+				ParseNode_Coordinates();
+				coord_read = true;
+
+				continue;
+			}
+
+			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
+		MACRO_NODECHECK_LOOPEND("vertex");
+		ParseHelper_Node_Exit();
+	}// if(!mReader->isEmptyElement())
+	else
+	{
+		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+	}// if(!mReader->isEmptyElement()) else
+
+	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+// <coordinates>
+// </coordinates>
+// Specifies the 3D location of this vertex.
+// Multi elements - No.
+// Parent element - <vertex>.
+//
+// Children elements:
+//   <x>, <y>, <z>
+//   Multi elements - No.
+//   X, Y, or Z coordinate, respectively, of a vertex position in space.
+void AMFImporter::ParseNode_Coordinates()
+{
+CAMFImporter_NodeElement* ne;
+
+	// create new color object.
+	ne = new CAMFImporter_NodeElement_Coordinates(mNodeElement_Cur);
+
+	CAMFImporter_NodeElement_Coordinates& als = *((CAMFImporter_NodeElement_Coordinates*)ne);// alias for convenience
+
+	// Check for child nodes
+	if(!mReader->isEmptyElement())
+	{
+		bool read_flag[3] = { false, false, false };
+
+		ParseHelper_Node_Enter(ne);
+		MACRO_NODECHECK_LOOPBEGIN("coordinates");
+			MACRO_NODECHECK_READCOMP_F("x", read_flag[0], als.Coordinate.x);
+			MACRO_NODECHECK_READCOMP_F("y", read_flag[1], als.Coordinate.y);
+			MACRO_NODECHECK_READCOMP_F("z", read_flag[2], als.Coordinate.z);
+		MACRO_NODECHECK_LOOPEND("coordinates");
+		ParseHelper_Node_Exit();
+		// check that all components was defined
+		if((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all coordinate's components are defined.");
+
+	}// if(!mReader->isEmptyElement())
+	else
+	{
+		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+	}// if(!mReader->isEmptyElement()) else
+
+	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+// <volume
+// materialid="" - Which material to use.
+// type=""       - What this volume describes can be “region” or “support”. If none specified, “object” is assumed. If support, then the geometric
+//                 requirements 1-8 listed in section 5 do not need to be maintained.
+// >
+// </volume>
+// Defines a volume from the established vertex list.
+// Multi elements - Yes.
+// Parent element - <mesh>.
+void AMFImporter::ParseNode_Volume()
+{
+std::string materialid;
+std::string type;
+CAMFImporter_NodeElement* ne;
+
+	// Read attributes for node <color>.
+	MACRO_ATTRREAD_LOOPBEG;
+		MACRO_ATTRREAD_CHECK_RET("materialid", materialid, mReader->getAttributeValue);
+		MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
+	MACRO_ATTRREAD_LOOPEND;
+
+	// create new object.
+	ne = new CAMFImporter_NodeElement_Volume(mNodeElement_Cur);
+	// and assign read data
+	((CAMFImporter_NodeElement_Volume*)ne)->MaterialID = materialid;
+	((CAMFImporter_NodeElement_Volume*)ne)->Type = type;
+	// Check for child nodes
+	if(!mReader->isEmptyElement())
+	{
+		bool col_read = false;
+
+		ParseHelper_Node_Enter(ne);
+		MACRO_NODECHECK_LOOPBEGIN("volume");
+			if(XML_CheckNode_NameEqual("color"))
+			{
+				// Check if data already defined.
+				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <volume>.");
+				// read data and set flag about it
+				ParseNode_Color();
+				col_read = true;
+
+				continue;
+			}
+
+			if(XML_CheckNode_NameEqual("triangle")) { ParseNode_Triangle(); continue; }
+			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
+		MACRO_NODECHECK_LOOPEND("volume");
+		ParseHelper_Node_Exit();
+	}// if(!mReader->isEmptyElement())
+	else
+	{
+		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+	}// if(!mReader->isEmptyElement()) else
+
+	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+// <triangle>
+// </triangle>
+// Defines a 3D triangle from three vertices, according to the right-hand rule (counter-clockwise when looking from the outside).
+// Multi elements - Yes.
+// Parent element - <volume>.
+//
+// Children elements:
+//   <v1>, <v2>, <v3>
+//   Multi elements - No.
+//   Index of the desired vertices in a triangle or edge.
+void AMFImporter::ParseNode_Triangle()
+{
+CAMFImporter_NodeElement* ne;
+
+	// create new color object.
+	ne = new CAMFImporter_NodeElement_Triangle(mNodeElement_Cur);
+
+	CAMFImporter_NodeElement_Triangle& als = *((CAMFImporter_NodeElement_Triangle*)ne);// alias for convenience
+
+	// Check for child nodes
+	if(!mReader->isEmptyElement())
+	{
+		bool col_read = false, tex_read = false;
+		bool read_flag[3] = { false, false, false };
+
+		ParseHelper_Node_Enter(ne);
+		MACRO_NODECHECK_LOOPBEGIN("triangle");
+			if(XML_CheckNode_NameEqual("color"))
+			{
+				// Check if data already defined.
+				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <triangle>.");
+				// read data and set flag about it
+				ParseNode_Color();
+				col_read = true;
+
+				continue;
+			}
+
+			if(XML_CheckNode_NameEqual("texmap"))// new name of node: "texmap".
+			{
+				// Check if data already defined.
+				if(tex_read) Throw_MoreThanOnceDefined("texmap", "Only one texture coordinate can be defined for <triangle>.");
+				// read data and set flag about it
+				ParseNode_TexMap();
+				tex_read = true;
+
+				continue;
+			}
+			else if(XML_CheckNode_NameEqual("map"))// old name of node: "map".
+			{
+				// Check if data already defined.
+				if(tex_read) Throw_MoreThanOnceDefined("map", "Only one texture coordinate can be defined for <triangle>.");
+				// read data and set flag about it
+				ParseNode_TexMap(true);
+				tex_read = true;
+
+				continue;
+			}
+
+			MACRO_NODECHECK_READCOMP_U32("v1", read_flag[0], als.V[0]);
+			MACRO_NODECHECK_READCOMP_U32("v2", read_flag[1], als.V[1]);
+			MACRO_NODECHECK_READCOMP_U32("v3", read_flag[2], als.V[2]);
+		MACRO_NODECHECK_LOOPEND("triangle");
+		ParseHelper_Node_Exit();
+		// check that all components was defined
+		if((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all vertices of the triangle are defined.");
+
+	}// if(!mReader->isEmptyElement())
+	else
+	{
+		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+	}// if(!mReader->isEmptyElement()) else
+
+	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 166 - 0
code/AssetLib/AMF/AMFImporter_Macro.hpp

@@ -0,0 +1,166 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/// \file AMFImporter_Macro.hpp
+/// \brief Useful macrodefines.
+/// \date 2016
+/// \author [email protected]
+
+#pragma once
+#ifndef AMFIMPORTER_MACRO_HPP_INCLUDED
+#define AMFIMPORTER_MACRO_HPP_INCLUDED
+
+/// \def MACRO_ATTRREAD_LOOPBEG
+/// Begin of loop that read attributes values.
+#define MACRO_ATTRREAD_LOOPBEG \
+	for(int idx = 0, idx_end = mReader->getAttributeCount(); idx < idx_end; idx++) \
+	{ \
+		std::string an(mReader->getAttributeName(idx));
+
+/// \def MACRO_ATTRREAD_LOOPEND
+/// End of loop that read attributes values.
+#define MACRO_ATTRREAD_LOOPEND \
+		Throw_IncorrectAttr(an); \
+	}
+
+/// \def MACRO_ATTRREAD_LOOPEND_WSKIP
+/// End of loop that read attributes values. Difference from \ref MACRO_ATTRREAD_LOOPEND in that: current macro skip unknown attributes, but
+/// \ref MACRO_ATTRREAD_LOOPEND throw an exception.
+#define MACRO_ATTRREAD_LOOPEND_WSKIP \
+		continue; \
+	}
+
+/// \def MACRO_ATTRREAD_CHECK_REF
+/// Check current attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
+/// "continue" will called.
+/// \param [in] pAttrName - attribute name.
+/// \param [out] pVarName - output variable name.
+/// \param [in] pFunction - function which read attribute value and write it to pVarName.
+#define MACRO_ATTRREAD_CHECK_REF(pAttrName, pVarName, pFunction) \
+	if(an == pAttrName) \
+	{ \
+		pFunction(idx, pVarName); \
+		continue; \
+	}
+
+/// \def MACRO_ATTRREAD_CHECK_RET
+/// Check current attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
+/// If result was read then  "continue" will called.
+/// \param [in] pAttrName - attribute name.
+/// \param [out] pVarName - output variable name.
+/// \param [in] pFunction - function which read attribute value and write it to pVarName.
+#define MACRO_ATTRREAD_CHECK_RET(pAttrName, pVarName, pFunction) \
+	if(an == pAttrName) \
+	{ \
+		pVarName = pFunction(idx); \
+		continue; \
+	}
+
+/// \def MACRO_NODECHECK_LOOPBEGIN(pNodeName)
+/// Begin of loop of parsing child nodes. Do not add ';' at end.
+/// \param [in] pNodeName - current node name.
+#define MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
+	do { \
+	bool close_found = false; \
+	 \
+	while(mReader->read()) \
+	{ \
+		if(mReader->getNodeType() == irr::io::EXN_ELEMENT) \
+		{
+
+/// \def MACRO_NODECHECK_LOOPEND(pNodeName)
+/// End of loop of parsing child nodes.
+/// \param [in] pNodeName - current node name.
+#define MACRO_NODECHECK_LOOPEND(pNodeName) \
+			XML_CheckNode_SkipUnsupported(pNodeName); \
+		}/* if(mReader->getNodeType() == irr::io::EXN_ELEMENT) */ \
+		else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) \
+		{ \
+			if(XML_CheckNode_NameEqual(pNodeName)) \
+			{ \
+				close_found = true; \
+	 \
+				break; \
+			} \
+		}/* else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) */ \
+	}/* while(mReader->read()) */ \
+	 \
+	if(!close_found) Throw_CloseNotFound(pNodeName); \
+	 \
+	} while(false)
+
+/// \def MACRO_NODECHECK_READCOMP_F
+/// Check current node name and if it equal to requested then read value. Result write to output variable of type "float".
+/// If result was read then  "continue" will called. Also check if node data already read then raise exception.
+/// \param [in] pNodeName - node name.
+/// \param [in, out] pReadFlag - read flag.
+/// \param [out] pVarName - output variable name.
+#define MACRO_NODECHECK_READCOMP_F(pNodeName, pReadFlag, pVarName) \
+	if(XML_CheckNode_NameEqual(pNodeName)) \
+	{ \
+		/* Check if field already read before. */ \
+		if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
+		/* Read color component and assign it to object. */ \
+		pVarName = XML_ReadNode_GetVal_AsFloat(); \
+		pReadFlag = true; \
+		continue; \
+	}
+
+/// \def MACRO_NODECHECK_READCOMP_U32
+/// Check current node name and if it equal to requested then read value. Result write to output variable of type "uint32_t".
+/// If result was read then  "continue" will called. Also check if node data already read then raise exception.
+/// \param [in] pNodeName - node name.
+/// \param [in, out] pReadFlag - read flag.
+/// \param [out] pVarName - output variable name.
+#define MACRO_NODECHECK_READCOMP_U32(pNodeName, pReadFlag, pVarName) \
+	if(XML_CheckNode_NameEqual(pNodeName)) \
+	{ \
+		/* Check if field already read before. */ \
+		if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
+		/* Read color component and assign it to object. */ \
+		pVarName = XML_ReadNode_GetVal_AsU32(); \
+		pReadFlag = true; \
+		continue; \
+	}
+
+#endif // AMFIMPORTER_MACRO_HPP_INCLUDED

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

@@ -1,4 +1,4 @@
-/*
+/*
 ---------------------------------------------------------------------------
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------

+ 1 - 1
code/AMF/AMFImporter_Node.hpp → code/AssetLib/AMF/AMFImporter_Node.hpp

@@ -1,4 +1,4 @@
-/*
+/*
 ---------------------------------------------------------------------------
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------

+ 872 - 0
code/AssetLib/AMF/AMFImporter_Postprocess.cpp

@@ -0,0 +1,872 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/// \file AMFImporter_Postprocess.cpp
+/// \brief Convert built scenegraph and objects to Assimp scenegraph.
+/// \date 2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+
+#include "AMFImporter.hpp"
+
+// Header files, Assimp.
+#include <assimp/SceneCombiner.h>
+#include <assimp/StandardShapes.h>
+#include <assimp/StringUtils.h>
+
+// Header files, stdlib.
+#include <iterator>
+
+namespace Assimp {
+
+aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const {
+    aiColor4D tcol;
+
+    // Check if stored data are supported.
+    if (!Composition.empty()) {
+        throw DeadlyImportError("IME. GetColor for composition");
+    } else if (Color->Composed) {
+        throw DeadlyImportError("IME. GetColor, composed color");
+    } else {
+        tcol = Color->Color;
+    }
+
+    // Check if default color must be used
+    if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) {
+        tcol.r = 0.5f;
+        tcol.g = 0.5f;
+        tcol.b = 0.5f;
+        tcol.a = 1;
+    }
+
+    return tcol;
+}
+
+void AMFImporter::PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh &pNodeElement, std::vector<aiVector3D> &pVertexCoordinateArray,
+        std::vector<CAMFImporter_NodeElement_Color *> &pVertexColorArray) const {
+    CAMFImporter_NodeElement_Vertices *vn = nullptr;
+    size_t col_idx;
+
+    // All data stored in "vertices", search for it.
+    for (CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Vertices) vn = (CAMFImporter_NodeElement_Vertices *)ne_child;
+    }
+
+    // If "vertices" not found then no work for us.
+    if (vn == nullptr) return;
+
+    pVertexCoordinateArray.reserve(vn->Child.size()); // all coordinates stored as child and we need to reserve space for future push_back's.
+    pVertexColorArray.resize(vn->Child.size()); // colors count equal vertices count.
+    col_idx = 0;
+    // Inside vertices collect all data and place to arrays
+    for (CAMFImporter_NodeElement *vn_child : vn->Child) {
+        // vertices, colors
+        if (vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex) {
+            // by default clear color for current vertex
+            pVertexColorArray[col_idx] = nullptr;
+
+            for (CAMFImporter_NodeElement *vtx : vn_child->Child) {
+                if (vtx->Type == CAMFImporter_NodeElement::ENET_Coordinates) {
+                    pVertexCoordinateArray.push_back(((CAMFImporter_NodeElement_Coordinates *)vtx)->Coordinate);
+
+                    continue;
+                }
+
+                if (vtx->Type == CAMFImporter_NodeElement::ENET_Color) {
+                    pVertexColorArray[col_idx] = (CAMFImporter_NodeElement_Color *)vtx;
+
+                    continue;
+                }
+            } // for(CAMFImporter_NodeElement* vtx: vn_child->Child)
+
+            col_idx++;
+        } // if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
+    } // for(CAMFImporter_NodeElement* vn_child: vn->Child)
+}
+
+size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &pID_R, const std::string &pID_G, const std::string &pID_B,
+        const std::string &pID_A) {
+    size_t TextureConverted_Index;
+    std::string TextureConverted_ID;
+
+    // check input data
+    if (pID_R.empty() && pID_G.empty() && pID_B.empty() && pID_A.empty())
+        throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined.");
+
+    // Create ID
+    TextureConverted_ID = pID_R + "_" + pID_G + "_" + pID_B + "_" + pID_A;
+    // Check if texture specified by set of IDs is converted already.
+    TextureConverted_Index = 0;
+    for (const SPP_Texture &tex_convd : mTexture_Converted) {
+        if (tex_convd.ID == TextureConverted_ID) {
+            return TextureConverted_Index;
+        } else {
+            ++TextureConverted_Index;
+        }
+    }
+
+    //
+    // Converted texture not found, create it.
+    //
+    CAMFImporter_NodeElement_Texture *src_texture[4]{ nullptr };
+    std::vector<CAMFImporter_NodeElement_Texture *> src_texture_4check;
+    SPP_Texture converted_texture;
+
+    { // find all specified source textures
+        CAMFImporter_NodeElement *t_tex;
+
+        // R
+        if (!pID_R.empty()) {
+            if (!Find_NodeElement(pID_R, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R);
+
+            src_texture[0] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[0] = nullptr;
+        }
+
+        // G
+        if (!pID_G.empty()) {
+            if (!Find_NodeElement(pID_G, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_G);
+
+            src_texture[1] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[1] = nullptr;
+        }
+
+        // B
+        if (!pID_B.empty()) {
+            if (!Find_NodeElement(pID_B, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_B);
+
+            src_texture[2] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[2] = nullptr;
+        }
+
+        // A
+        if (!pID_A.empty()) {
+            if (!Find_NodeElement(pID_A, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_A);
+
+            src_texture[3] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[3] = nullptr;
+        }
+    } // END: find all specified source textures
+
+    // check that all textures has same size
+    if (src_texture_4check.size() > 1) {
+        for (size_t i = 0, i_e = (src_texture_4check.size() - 1); i < i_e; i++) {
+            if ((src_texture_4check[i]->Width != src_texture_4check[i + 1]->Width) || (src_texture_4check[i]->Height != src_texture_4check[i + 1]->Height) ||
+                    (src_texture_4check[i]->Depth != src_texture_4check[i + 1]->Depth)) {
+                throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size.");
+            }
+        }
+    } // if(src_texture_4check.size() > 1)
+
+    // set texture attributes
+    converted_texture.Width = src_texture_4check[0]->Width;
+    converted_texture.Height = src_texture_4check[0]->Height;
+    converted_texture.Depth = src_texture_4check[0]->Depth;
+    // if one of source texture is tiled then converted texture is tiled too.
+    converted_texture.Tiled = false;
+    for (uint8_t i = 0; i < src_texture_4check.size(); i++)
+        converted_texture.Tiled |= src_texture_4check[i]->Tiled;
+
+    // Create format hint.
+    strcpy(converted_texture.FormatHint, "rgba0000"); // copy initial string.
+    if (!pID_R.empty()) converted_texture.FormatHint[4] = '8';
+    if (!pID_G.empty()) converted_texture.FormatHint[5] = '8';
+    if (!pID_B.empty()) converted_texture.FormatHint[6] = '8';
+    if (!pID_A.empty()) converted_texture.FormatHint[7] = '8';
+
+    //
+    // Сopy data of textures.
+    //
+    size_t tex_size = 0;
+    size_t step = 0;
+    size_t off_g = 0;
+    size_t off_b = 0;
+
+    // Calculate size of the target array and rule how data will be copied.
+    if (!pID_R.empty() && nullptr != src_texture[0]) {
+        tex_size += src_texture[0]->Data.size();
+        step++, off_g++, off_b++;
+    }
+    if (!pID_G.empty() && nullptr != src_texture[1]) {
+        tex_size += src_texture[1]->Data.size();
+        step++, off_b++;
+    }
+    if (!pID_B.empty() && nullptr != src_texture[2]) {
+        tex_size += src_texture[2]->Data.size();
+        step++;
+    }
+    if (!pID_A.empty() && nullptr != src_texture[3]) {
+        tex_size += src_texture[3]->Data.size();
+        step++;
+    }
+
+    // Create target array.
+    converted_texture.Data = new uint8_t[tex_size];
+    // And copy data
+    auto CopyTextureData = [&](const std::string &pID, const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void {
+        if (!pID.empty()) {
+            for (size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) {
+                CAMFImporter_NodeElement_Texture *tex = src_texture[pSrcTexNum];
+                ai_assert(tex);
+                converted_texture.Data[idx_target] = tex->Data.at(idx_src);
+            }
+        }
+    }; // auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
+
+    CopyTextureData(pID_R, 0, step, 0);
+    CopyTextureData(pID_G, off_g, step, 1);
+    CopyTextureData(pID_B, off_b, step, 2);
+    CopyTextureData(pID_A, step - 1, step, 3);
+
+    // Store new converted texture ID
+    converted_texture.ID = TextureConverted_ID;
+    // Store new converted texture
+    mTexture_Converted.push_back(converted_texture);
+
+    return TextureConverted_Index;
+}
+
+void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace> &pInputList, std::list<std::list<SComplexFace>> &pOutputList_Separated) {
+    auto texmap_is_equal = [](const CAMFImporter_NodeElement_TexMap *pTexMap1, const CAMFImporter_NodeElement_TexMap *pTexMap2) -> bool {
+        if ((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true;
+        if (pTexMap1 == nullptr) return false;
+        if (pTexMap2 == nullptr) return false;
+
+        if (pTexMap1->TextureID_R != pTexMap2->TextureID_R) return false;
+        if (pTexMap1->TextureID_G != pTexMap2->TextureID_G) return false;
+        if (pTexMap1->TextureID_B != pTexMap2->TextureID_B) return false;
+        if (pTexMap1->TextureID_A != pTexMap2->TextureID_A) return false;
+
+        return true;
+    };
+
+    pOutputList_Separated.clear();
+    if (pInputList.empty()) return;
+
+    do {
+        SComplexFace face_start = pInputList.front();
+        std::list<SComplexFace> face_list_cur;
+
+        for (std::list<SComplexFace>::iterator it = pInputList.begin(), it_end = pInputList.end(); it != it_end;) {
+            if (texmap_is_equal(face_start.TexMap, it->TexMap)) {
+                auto it_old = it;
+
+                ++it;
+                face_list_cur.push_back(*it_old);
+                pInputList.erase(it_old);
+            } else {
+                ++it;
+            }
+        }
+
+        if (!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur);
+
+    } while (!pInputList.empty());
+}
+
+void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata *> &metadataList, aiNode &sceneNode) const {
+    if (!metadataList.empty()) {
+        if (sceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
+
+        // copy collected metadata to output node.
+        sceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metadataList.size()));
+        size_t meta_idx(0);
+
+        for (const CAMFImporter_NodeElement_Metadata &metadata : metadataList) {
+            sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata.Type, aiString(metadata.Value));
+        }
+    } // if(!metadataList.empty())
+}
+
+void AMFImporter::Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object &pNodeElement, std::list<aiMesh *> &pMeshList, aiNode **pSceneNode) {
+    CAMFImporter_NodeElement_Color *object_color = nullptr;
+
+    // create new aiNode and set name as <object> has.
+    *pSceneNode = new aiNode;
+    (*pSceneNode)->mName = pNodeElement.ID;
+    // read mesh and color
+    for (const CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
+        std::vector<aiVector3D> vertex_arr;
+        std::vector<CAMFImporter_NodeElement_Color *> color_arr;
+
+        // color for object
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Color) object_color = (CAMFImporter_NodeElement_Color *)ne_child;
+
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Mesh) {
+            // Create arrays from children of mesh: vertices.
+            PostprocessHelper_CreateMeshDataArray(*((CAMFImporter_NodeElement_Mesh *)ne_child), vertex_arr, color_arr);
+            // Use this arrays as a source when creating every aiMesh
+            Postprocess_BuildMeshSet(*((CAMFImporter_NodeElement_Mesh *)ne_child), vertex_arr, color_arr, object_color, pMeshList, **pSceneNode);
+        }
+    } // for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
+}
+
+void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray,
+        const std::vector<CAMFImporter_NodeElement_Color *> &pVertexColorArray,
+        const CAMFImporter_NodeElement_Color *pObjectColor, std::list<aiMesh *> &pMeshList, aiNode &pSceneNode) {
+    std::list<unsigned int> mesh_idx;
+
+    // all data stored in "volume", search for it.
+    for (const CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
+        const CAMFImporter_NodeElement_Color *ne_volume_color = nullptr;
+        const SPP_Material *cur_mat = nullptr;
+
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Volume) {
+            /******************* Get faces *******************/
+            const CAMFImporter_NodeElement_Volume *ne_volume = reinterpret_cast<const CAMFImporter_NodeElement_Volume *>(ne_child);
+
+            std::list<SComplexFace> complex_faces_list; // List of the faces of the volume.
+            std::list<std::list<SComplexFace>> complex_faces_toplist; // List of the face list for every mesh.
+
+            // check if volume use material
+            if (!ne_volume->MaterialID.empty()) {
+                if (!Find_ConvertedMaterial(ne_volume->MaterialID, &cur_mat)) Throw_ID_NotFound(ne_volume->MaterialID);
+            }
+
+            // inside "volume" collect all data and place to arrays or create new objects
+            for (const CAMFImporter_NodeElement *ne_volume_child : ne_volume->Child) {
+                // color for volume
+                if (ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Color) {
+                    ne_volume_color = reinterpret_cast<const CAMFImporter_NodeElement_Color *>(ne_volume_child);
+                } else if (ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Triangle) // triangles, triangles colors
+                {
+                    const CAMFImporter_NodeElement_Triangle &tri_al = *reinterpret_cast<const CAMFImporter_NodeElement_Triangle *>(ne_volume_child);
+
+                    SComplexFace complex_face;
+
+                    // initialize pointers
+                    complex_face.Color = nullptr;
+                    complex_face.TexMap = nullptr;
+                    // get data from triangle children: color, texture coordinates.
+                    if (tri_al.Child.size()) {
+                        for (const CAMFImporter_NodeElement *ne_triangle_child : tri_al.Child) {
+                            if (ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_Color)
+                                complex_face.Color = reinterpret_cast<const CAMFImporter_NodeElement_Color *>(ne_triangle_child);
+                            else if (ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_TexMap)
+                                complex_face.TexMap = reinterpret_cast<const CAMFImporter_NodeElement_TexMap *>(ne_triangle_child);
+                        }
+                    } // if(tri_al.Child.size())
+
+                    // create new face and store it.
+                    complex_face.Face.mNumIndices = 3;
+                    complex_face.Face.mIndices = new unsigned int[3];
+                    complex_face.Face.mIndices[0] = static_cast<unsigned int>(tri_al.V[0]);
+                    complex_face.Face.mIndices[1] = static_cast<unsigned int>(tri_al.V[1]);
+                    complex_face.Face.mIndices[2] = static_cast<unsigned int>(tri_al.V[2]);
+                    complex_faces_list.push_back(complex_face);
+                }
+            } // for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
+
+            /**** Split faces list: one list per mesh ****/
+            PostprocessHelper_SplitFacesByTextureID(complex_faces_list, complex_faces_toplist);
+
+            /***** Create mesh for every faces list ******/
+            for (std::list<SComplexFace> &face_list_cur : complex_faces_toplist) {
+                auto VertexIndex_GetMinimal = [](const std::list<SComplexFace> &pFaceList, const size_t *pBiggerThan) -> size_t {
+                    size_t rv = 0;
+
+                    if (pBiggerThan != nullptr) {
+                        bool found = false;
+
+                        for (const SComplexFace &face : pFaceList) {
+                            for (size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++) {
+                                if (face.Face.mIndices[idx_vert] > *pBiggerThan) {
+                                    rv = face.Face.mIndices[idx_vert];
+                                    found = true;
+
+                                    break;
+                                }
+                            }
+
+                            if (found) break;
+                        }
+
+                        if (!found) return *pBiggerThan;
+                    } else {
+                        rv = pFaceList.front().Face.mIndices[0];
+                    } // if(pBiggerThan != nullptr) else
+
+                    for (const SComplexFace &face : pFaceList) {
+                        for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) {
+                            if (face.Face.mIndices[vi] < rv) {
+                                if (pBiggerThan != nullptr) {
+                                    if (face.Face.mIndices[vi] > *pBiggerThan) rv = face.Face.mIndices[vi];
+                                } else {
+                                    rv = face.Face.mIndices[vi];
+                                }
+                            }
+                        }
+                    } // for(const SComplexFace& face: pFaceList)
+
+                    return rv;
+                }; // auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
+
+                auto VertexIndex_Replace = [](std::list<SComplexFace> &pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void {
+                    for (const SComplexFace &face : pFaceList) {
+                        for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) {
+                            if (face.Face.mIndices[vi] == pIdx_From) face.Face.mIndices[vi] = static_cast<unsigned int>(pIdx_To);
+                        }
+                    }
+                }; // auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
+
+                auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D {
+                    // Color priorities(In descending order):
+                    // 1. triangle color;
+                    // 2. vertex color;
+                    // 3. volume color;
+                    // 4. object color;
+                    // 5. material;
+                    // 6. default - invisible coat.
+                    //
+                    // Fill vertices colors in color priority list above that's points from 1 to 6.
+                    if ((pIdx < pVertexColorArray.size()) && (pVertexColorArray[pIdx] != nullptr)) // check for vertex color
+                    {
+                        if (pVertexColorArray[pIdx]->Composed)
+                            throw DeadlyImportError("IME: vertex color composed");
+                        else
+                            return pVertexColorArray[pIdx]->Color;
+                    } else if (ne_volume_color != nullptr) // check for volume color
+                    {
+                        if (ne_volume_color->Composed)
+                            throw DeadlyImportError("IME: volume color composed");
+                        else
+                            return ne_volume_color->Color;
+                    } else if (pObjectColor != nullptr) // check for object color
+                    {
+                        if (pObjectColor->Composed)
+                            throw DeadlyImportError("IME: object color composed");
+                        else
+                            return pObjectColor->Color;
+                    } else if (cur_mat != nullptr) // check for material
+                    {
+                        return cur_mat->GetColor(pVertexCoordinateArray.at(pIdx).x, pVertexCoordinateArray.at(pIdx).y, pVertexCoordinateArray.at(pIdx).z);
+                    } else // set default color.
+                    {
+                        return { 0, 0, 0, 0 };
+                    } // if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else
+                }; // auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
+
+                aiMesh *tmesh = new aiMesh;
+
+                tmesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // Only triangles is supported by AMF.
+                //
+                // set geometry and colors (vertices)
+                //
+                // copy faces/triangles
+                tmesh->mNumFaces = static_cast<unsigned int>(face_list_cur.size());
+                tmesh->mFaces = new aiFace[tmesh->mNumFaces];
+
+                // Create vertices list and optimize indices. Optimisation mean following.In AMF all volumes use one big list of vertices. And one volume
+                // can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10.
+                // Do you need all this thousands of garbage? Of course no. So, optimisation step transformate sparse indices set to continuous.
+                size_t VertexCount_Max = tmesh->mNumFaces * 3; // 3 - triangles.
+                std::vector<aiVector3D> vert_arr, texcoord_arr;
+                std::vector<aiColor4D> col_arr;
+
+                vert_arr.reserve(VertexCount_Max * 2); // "* 2" - see below TODO.
+                col_arr.reserve(VertexCount_Max * 2);
+
+                { // fill arrays
+                    size_t vert_idx_from, vert_idx_to;
+
+                    // first iteration.
+                    vert_idx_to = 0;
+                    vert_idx_from = VertexIndex_GetMinimal(face_list_cur, nullptr);
+                    vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
+                    col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
+                    if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
+
+                    // rest iterations
+                    do {
+                        vert_idx_from = VertexIndex_GetMinimal(face_list_cur, &vert_idx_to);
+                        if (vert_idx_from == vert_idx_to) break; // all indices are transferred,
+
+                        vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
+                        col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
+                        vert_idx_to++;
+                        if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
+
+                    } while (true);
+                } // fill arrays. END.
+
+                //
+                // check if triangle colors are used and create additional faces if needed.
+                //
+                for (const SComplexFace &face_cur : face_list_cur) {
+                    if (face_cur.Color != nullptr) {
+                        aiColor4D face_color;
+                        size_t vert_idx_new = vert_arr.size();
+
+                        if (face_cur.Color->Composed)
+                            throw DeadlyImportError("IME: face color composed");
+                        else
+                            face_color = face_cur.Color->Color;
+
+                        for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) {
+                            vert_arr.push_back(vert_arr.at(face_cur.Face.mIndices[idx_ind]));
+                            col_arr.push_back(face_color);
+                            face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(vert_idx_new++);
+                        }
+                    } // if(face_cur.Color != nullptr)
+                } // for(const SComplexFace& face_cur: face_list_cur)
+
+                //
+                // if texture is used then copy texture coordinates too.
+                //
+                if (face_list_cur.front().TexMap != nullptr) {
+                    size_t idx_vert_new = vert_arr.size();
+                    ///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for
+                    /// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about
+                    /// optimisation.
+                    bool *idx_vert_used;
+
+                    idx_vert_used = new bool[VertexCount_Max * 2];
+                    for (size_t i = 0, i_e = VertexCount_Max * 2; i < i_e; i++)
+                        idx_vert_used[i] = false;
+
+                    // This ID's will be used when set materials ID in scene.
+                    tmesh->mMaterialIndex = static_cast<unsigned int>(PostprocessHelper_GetTextureID_Or_Create(face_list_cur.front().TexMap->TextureID_R,
+                            face_list_cur.front().TexMap->TextureID_G,
+                            face_list_cur.front().TexMap->TextureID_B,
+                            face_list_cur.front().TexMap->TextureID_A));
+                    texcoord_arr.resize(VertexCount_Max * 2);
+                    for (const SComplexFace &face_cur : face_list_cur) {
+                        for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) {
+                            const size_t idx_vert = face_cur.Face.mIndices[idx_ind];
+
+                            if (!idx_vert_used[idx_vert]) {
+                                texcoord_arr.at(idx_vert) = face_cur.TexMap->TextureCoordinate[idx_ind];
+                                idx_vert_used[idx_vert] = true;
+                            } else if (texcoord_arr.at(idx_vert) != face_cur.TexMap->TextureCoordinate[idx_ind]) {
+                                // in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture
+                                // coordinates.
+                                vert_arr.push_back(vert_arr.at(idx_vert));
+                                col_arr.push_back(col_arr.at(idx_vert));
+                                texcoord_arr.at(idx_vert_new) = face_cur.TexMap->TextureCoordinate[idx_ind];
+                                face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(idx_vert_new++);
+                            }
+                        } // for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
+                    } // for(const SComplexFace& face_cur: face_list_cur)
+
+                    delete[] idx_vert_used;
+                    // shrink array
+                    texcoord_arr.resize(idx_vert_new);
+                } // if(face_list_cur.front().TexMap != nullptr)
+
+                //
+                // copy collected data to mesh
+                //
+                tmesh->mNumVertices = static_cast<unsigned int>(vert_arr.size());
+                tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
+                tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
+
+                memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
+                memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D));
+                if (texcoord_arr.size() > 0) {
+                    tmesh->mTextureCoords[0] = new aiVector3D[tmesh->mNumVertices];
+                    memcpy(tmesh->mTextureCoords[0], texcoord_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
+                    tmesh->mNumUVComponents[0] = 2; // U and V stored in "x", "y" of aiVector3D.
+                }
+
+                size_t idx_face = 0;
+                for (const SComplexFace &face_cur : face_list_cur)
+                    tmesh->mFaces[idx_face++] = face_cur.Face;
+
+                // store new aiMesh
+                mesh_idx.push_back(static_cast<unsigned int>(pMeshList.size()));
+                pMeshList.push_back(tmesh);
+            } // for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
+        } // if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
+    } // for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
+
+    // if meshes was created then assign new indices with current aiNode
+    if (!mesh_idx.empty()) {
+        std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
+
+        pSceneNode.mNumMeshes = static_cast<unsigned int>(mesh_idx.size());
+        pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
+        for (size_t i = 0; i < pSceneNode.mNumMeshes; i++)
+            pSceneNode.mMeshes[i] = *mit++;
+    } // if(mesh_idx.size() > 0)
+}
+
+void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material &pMaterial) {
+    SPP_Material new_mat;
+
+    new_mat.ID = pMaterial.ID;
+    for (const CAMFImporter_NodeElement *mat_child : pMaterial.Child) {
+        if (mat_child->Type == CAMFImporter_NodeElement::ENET_Color) {
+            new_mat.Color = (CAMFImporter_NodeElement_Color *)mat_child;
+        } else if (mat_child->Type == CAMFImporter_NodeElement::ENET_Metadata) {
+            new_mat.Metadata.push_back((CAMFImporter_NodeElement_Metadata *)mat_child);
+        }
+    } // for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
+
+    // place converted material to special list
+    mMaterial_Converted.push_back(new_mat);
+}
+
+void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation &pConstellation, std::list<aiNode *> &pNodeList) const {
+    aiNode *con_node;
+    std::list<aiNode *> ch_node;
+
+    // We will build next hierarchy:
+    // aiNode as parent (<constellation>) for set of nodes as a children
+    //  |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
+    //  ...
+    //  \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
+    con_node = new aiNode;
+    con_node->mName = pConstellation.ID;
+    // Walk through children and search for instances of another objects, constellations.
+    for (const CAMFImporter_NodeElement *ne : pConstellation.Child) {
+        aiMatrix4x4 tmat;
+        aiNode *t_node;
+        aiNode *found_node;
+
+        if (ne->Type == CAMFImporter_NodeElement::ENET_Metadata) continue;
+        if (ne->Type != CAMFImporter_NodeElement::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
+
+        // create alias for conveniance
+        CAMFImporter_NodeElement_Instance &als = *((CAMFImporter_NodeElement_Instance *)ne);
+        // find referenced object
+        if (!Find_ConvertedNode(als.ObjectID, pNodeList, &found_node)) Throw_ID_NotFound(als.ObjectID);
+
+        // create node for applying transformation
+        t_node = new aiNode;
+        t_node->mParent = con_node;
+        // apply transformation
+        aiMatrix4x4::Translation(als.Delta, tmat), t_node->mTransformation *= tmat;
+        aiMatrix4x4::RotationX(als.Rotation.x, tmat), t_node->mTransformation *= tmat;
+        aiMatrix4x4::RotationY(als.Rotation.y, tmat), t_node->mTransformation *= tmat;
+        aiMatrix4x4::RotationZ(als.Rotation.z, tmat), t_node->mTransformation *= tmat;
+        // create array for one child node
+        t_node->mNumChildren = 1;
+        t_node->mChildren = new aiNode *[t_node->mNumChildren];
+        SceneCombiner::Copy(&t_node->mChildren[0], found_node);
+        t_node->mChildren[0]->mParent = t_node;
+        ch_node.push_back(t_node);
+    } // for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
+
+    // copy found aiNode's as children
+    if (ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>.");
+
+    size_t ch_idx = 0;
+
+    con_node->mNumChildren = static_cast<unsigned int>(ch_node.size());
+    con_node->mChildren = new aiNode *[con_node->mNumChildren];
+    for (aiNode *node : ch_node)
+        con_node->mChildren[ch_idx++] = node;
+
+    // and place "root" of <constellation> node to node list
+    pNodeList.push_back(con_node);
+}
+
+void AMFImporter::Postprocess_BuildScene(aiScene *pScene) {
+    std::list<aiNode *> node_list;
+    std::list<aiMesh *> mesh_list;
+    std::list<CAMFImporter_NodeElement_Metadata *> meta_list;
+
+    //
+    // Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
+    // For building aiScene we are must to do few steps:
+    // at first creating root node for aiScene.
+    pScene->mRootNode = new aiNode;
+    pScene->mRootNode->mParent = nullptr;
+    pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
+    // search for root(<amf>) element
+    CAMFImporter_NodeElement *root_el = nullptr;
+
+    for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
+        if (ne->Type != CAMFImporter_NodeElement::ENET_Root) continue;
+
+        root_el = ne;
+
+        break;
+    } // for(const CAMFImporter_NodeElement* ne: mNodeElement_List)
+
+    // Check if root element are found.
+    if (root_el == nullptr) throw DeadlyImportError("Root(<amf>) element not found.");
+
+    // after that walk through children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>,
+    // <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read
+    // at any moment.
+    //
+    // 1. <material>
+    // 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet
+    for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Material) Postprocess_BuildMaterial(*((CAMFImporter_NodeElement_Material *)root_child));
+    }
+
+    // After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
+    //
+    // 3. <object>
+    for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Object) {
+            aiNode *tnode = nullptr;
+
+            // for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance>
+            Postprocess_BuildNodeAndObject(*((CAMFImporter_NodeElement_Object *)root_child), mesh_list, &tnode);
+            if (tnode != nullptr) node_list.push_back(tnode);
+        }
+    } // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
+
+    // And finally read rest of nodes.
+    //
+    for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
+        // 4. <constellation>
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Constellation) {
+            // <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's.
+            Postprocess_BuildConstellation(*((CAMFImporter_NodeElement_Constellation *)root_child), node_list);
+        }
+
+        // 5, <metadata>
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Metadata) meta_list.push_back((CAMFImporter_NodeElement_Metadata *)root_child);
+    } // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
+
+    // at now we can add collected metadata to root node
+    Postprocess_AddMetadata(meta_list, *pScene->mRootNode);
+    //
+    // Check constellation children
+    //
+    // As said in specification:
+    // "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing."
+    // What that means? For example: if some object is used in constellation then you must show only constellation but not original object.
+    // And at this step we are checking that relations.
+nl_clean_loop:
+
+    if (node_list.size() > 1) {
+        // walk through all nodes
+        for (std::list<aiNode *>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it) {
+            // and try to find them in another top nodes.
+            std::list<aiNode *>::const_iterator next_it = nl_it;
+
+            ++next_it;
+            for (; next_it != node_list.end(); ++next_it) {
+                if ((*next_it)->FindNode((*nl_it)->mName) != nullptr) {
+                    // if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
+                    node_list.erase(nl_it);
+
+                    goto nl_clean_loop;
+                }
+            } // for(; next_it != node_list.end(); next_it++)
+        } // for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
+    }
+
+    //
+    // move created objects to aiScene
+    //
+    //
+    // Nodes
+    if (!node_list.empty()) {
+        std::list<aiNode *>::const_iterator nl_it = node_list.begin();
+
+        pScene->mRootNode->mNumChildren = static_cast<unsigned int>(node_list.size());
+        pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
+        for (size_t i = 0; i < pScene->mRootNode->mNumChildren; i++) {
+            // Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have
+            // mRootNode only as parent.
+            (*nl_it)->mParent = pScene->mRootNode;
+            pScene->mRootNode->mChildren[i] = *nl_it++;
+        }
+    } // if(node_list.size() > 0)
+
+    //
+    // Meshes
+    if (!mesh_list.empty()) {
+        std::list<aiMesh *>::const_iterator ml_it = mesh_list.begin();
+
+        pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
+        pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
+        for (size_t i = 0; i < pScene->mNumMeshes; i++)
+            pScene->mMeshes[i] = *ml_it++;
+    } // if(mesh_list.size() > 0)
+
+    //
+    // Textures
+    pScene->mNumTextures = static_cast<unsigned int>(mTexture_Converted.size());
+    if (pScene->mNumTextures > 0) {
+        size_t idx;
+
+        idx = 0;
+        pScene->mTextures = new aiTexture *[pScene->mNumTextures];
+        for (const SPP_Texture &tex_convd : mTexture_Converted) {
+            pScene->mTextures[idx] = new aiTexture;
+            pScene->mTextures[idx]->mWidth = static_cast<unsigned int>(tex_convd.Width);
+            pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height);
+            pScene->mTextures[idx]->pcData = (aiTexel *)tex_convd.Data;
+            // texture format description.
+            strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint);
+            idx++;
+        } // for(const SPP_Texture& tex_convd: mTexture_Converted)
+
+        // Create materials for embedded textures.
+        idx = 0;
+        pScene->mNumMaterials = static_cast<unsigned int>(mTexture_Converted.size());
+        pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
+        for (const SPP_Texture &tex_convd : mTexture_Converted) {
+            const aiString texture_id(AI_EMBEDDED_TEXNAME_PREFIX + to_string(idx));
+            const int mode = aiTextureOp_Multiply;
+            const int repeat = tex_convd.Tiled ? 1 : 0;
+
+            pScene->mMaterials[idx] = new aiMaterial;
+            pScene->mMaterials[idx]->AddProperty(&texture_id, AI_MATKEY_TEXTURE_DIFFUSE(0));
+            pScene->mMaterials[idx]->AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
+            pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+            pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+            idx++;
+        }
+    } // if(pScene->mNumTextures > 0)
+} // END: after that walk through children of root and collect data
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

文件差异内容过多而无法显示
+ 271 - 285
code/AssetLib/ASE/ASELoader.cpp


+ 0 - 0
code/ASE/ASELoader.h → code/AssetLib/ASE/ASELoader.h


文件差异内容过多而无法显示
+ 242 - 371
code/AssetLib/ASE/ASEParser.cpp


+ 15 - 4
code/ASE/ASEParser.h → code/AssetLib/ASE/ASEParser.h

@@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/qnan.h>
 
 // ASE is quite similar to 3ds. We can reuse some structures
-#include "3DS/3DSLoader.h"
+#include "AssetLib/3DS/3DSLoader.h"
 
 namespace Assimp    {
 namespace ASE   {
@@ -80,7 +80,18 @@ struct Material : public D3DS::Material
     }
 
     Material(const Material &other)            = default;
-    Material &operator=(const Material &other) = default;
+
+    Material &operator=(const Material &other) {
+        if (this == &other) {
+            return *this;
+        }
+
+        avSubMaterials = other.avSubMaterials;
+        pcInstance = other.pcInstance;
+        bNeed = other.bNeed;
+
+        return *this;
+    }
 
 
     //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
@@ -94,12 +105,12 @@ struct Material : public D3DS::Material
     }
 
 
-    Material &operator=(Material &&other) AI_NO_EXCEPT {
+    Material &operator=( Material &&other) AI_NO_EXCEPT {
         if (this == &other) {
             return *this;
         }
 
-        D3DS::Material::operator=(std::move(other));
+        //D3DS::Material::operator=(std::move(other));
 
         avSubMaterials = std::move(other.avSubMaterials);
         pcInstance = std::move(other.pcInstance);

+ 68 - 0
code/AssetLib/Assbin/AssbinExporter.cpp

@@ -0,0 +1,68 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file  AssbinExporter.cpp
+ *  ASSBIN exporter main code
+ */
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
+
+#include "AssbinFileWriter.h"
+
+#include <assimp/scene.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp {
+
+void ExportSceneAssbin(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    DumpSceneToAssbin(
+            pFile,
+            "\0", // no command(s).
+            pIOSystem,
+            pScene,
+            false, // shortened?
+            false); // compressed?
+}
+} // end of namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_ASSBIN_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 0 - 0
code/Assbin/AssbinExporter.h → code/AssetLib/Assbin/AssbinExporter.h


+ 832 - 0
code/AssetLib/Assbin/AssbinFileWriter.cpp

@@ -0,0 +1,832 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file  AssbinFileWriter.cpp
+ *  @brief Implementation of Assbin file writer.
+ */
+
+#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
+#include <zlib.h>
+#else
+#include "../contrib/zlib/zlib.h"
+#endif
+
+#include <time.h>
+
+#ifdef _WIN32
+#pragma warning(push)
+#pragma warning(disable : 4706)
+#endif // _WIN32
+
+namespace Assimp {
+
+template <typename T>
+size_t Write(IOStream *stream, const T &v) {
+    return stream->Write(&v, sizeof(T), 1);
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiString
+template <>
+inline size_t Write<aiString>(IOStream *stream, const aiString &s) {
+    const size_t s2 = (uint32_t)s.length;
+    stream->Write(&s, 4, 1);
+    stream->Write(s.data, s2, 1);
+
+    return s2 + 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an unsigned int as uint32_t
+template <>
+inline size_t Write<unsigned int>(IOStream *stream, const unsigned int &w) {
+    const uint32_t t = (uint32_t)w;
+    if (w > t) {
+        // this shouldn't happen, integers in Assimp data structures never exceed 2^32
+        throw DeadlyExportError("loss of data due to 64 -> 32 bit integer conversion");
+    }
+
+    stream->Write(&t, 4, 1);
+
+    return 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an unsigned int as uint16_t
+template <>
+inline size_t Write<uint16_t>(IOStream *stream, const uint16_t &w) {
+    static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t)==2");
+    stream->Write(&w, 2, 1);
+
+    return 2;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a float
+template <>
+inline size_t Write<float>(IOStream *stream, const float &f) {
+    static_assert(sizeof(float) == 4, "sizeof(float)==4");
+    stream->Write(&f, 4, 1);
+
+    return 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a double
+template <>
+inline size_t Write<double>(IOStream *stream, const double &f) {
+    static_assert(sizeof(double) == 8, "sizeof(double)==8");
+    stream->Write(&f, 8, 1);
+
+    return 8;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a vec3
+template <>
+inline size_t Write<aiVector3D>(IOStream *stream, const aiVector3D &v) {
+    size_t t = Write<float>(stream, v.x);
+    t += Write<float>(stream, v.y);
+    t += Write<float>(stream, v.z);
+
+    return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a color value
+template <>
+inline size_t Write<aiColor3D>(IOStream *stream, const aiColor3D &v) {
+    size_t t = Write<float>(stream, v.r);
+    t += Write<float>(stream, v.g);
+    t += Write<float>(stream, v.b);
+
+    return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a color value
+template <>
+inline size_t Write<aiColor4D>(IOStream *stream, const aiColor4D &v) {
+    size_t t = Write<float>(stream, v.r);
+    t += Write<float>(stream, v.g);
+    t += Write<float>(stream, v.b);
+    t += Write<float>(stream, v.a);
+
+    return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a quaternion
+template <>
+inline size_t Write<aiQuaternion>(IOStream *stream, const aiQuaternion &v) {
+    size_t t = Write<float>(stream, v.w);
+    t += Write<float>(stream, v.x);
+    t += Write<float>(stream, v.y);
+    t += Write<float>(stream, v.z);
+    ai_assert(t == 16);
+
+    return 16;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a vertex weight
+template <>
+inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) {
+    size_t t = Write<unsigned int>(stream, v.mVertexId);
+
+    return t + Write<float>(stream, v.mWeight);
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a mat4x4
+template <>
+inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) {
+    for (unsigned int i = 0; i < 4; ++i) {
+        for (unsigned int i2 = 0; i2 < 4; ++i2) {
+            Write<float>(stream, m[i][i2]);
+        }
+    }
+
+    return 64;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiVectorKey
+template <>
+inline size_t Write<aiVectorKey>(IOStream *stream, const aiVectorKey &v) {
+    const size_t t = Write<double>(stream, v.mTime);
+    return t + Write<aiVector3D>(stream, v.mValue);
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiQuatKey
+template <>
+inline size_t Write<aiQuatKey>(IOStream *stream, const aiQuatKey &v) {
+    const size_t t = Write<double>(stream, v.mTime);
+    return t + Write<aiQuaternion>(stream, v.mValue);
+}
+
+template <typename T>
+inline size_t WriteBounds(IOStream *stream, const T *in, unsigned int size) {
+    T minc, maxc;
+    ArrayBounds(in, size, minc, maxc);
+
+    const size_t t = Write<T>(stream, minc);
+    return t + Write<T>(stream, maxc);
+}
+
+// We use this to write out non-byte arrays so that we write using the specializations.
+// This way we avoid writing out extra bytes that potentially come from struct alignment.
+template <typename T>
+inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
+    size_t n = 0;
+    for (unsigned int i = 0; i < size; i++)
+        n += Write<T>(stream, in[i]);
+
+    return n;
+}
+
+// ----------------------------------------------------------------------------------
+/** @class  AssbinChunkWriter
+ *  @brief  Chunk writer mechanism for the .assbin file structure
+ *
+ *  This is a standard in-memory IOStream (most of the code is based on BlobIOStream),
+ *  the difference being that this takes another IOStream as a "container" in the
+ *  constructor, and when it is destroyed, it appends the magic number, the chunk size,
+ *  and the chunk contents to the container stream. This allows relatively easy chunk
+ *  chunk construction, even recursively.
+ */
+class AssbinChunkWriter : public IOStream {
+private:
+    uint8_t *buffer;
+    uint32_t magic;
+    IOStream *container;
+    size_t cur_size, cursor, initial;
+
+private:
+    // -------------------------------------------------------------------
+    void Grow(size_t need = 0) {
+        size_t new_size = std::max(initial, std::max(need, cur_size + (cur_size >> 1)));
+
+        const uint8_t *const old = buffer;
+        buffer = new uint8_t[new_size];
+
+        if (old) {
+            memcpy(buffer, old, cur_size);
+            delete[] old;
+        }
+
+        cur_size = new_size;
+    }
+
+public:
+    AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) :
+            buffer(nullptr),
+            magic(magic),
+            container(container),
+            cur_size(0),
+            cursor(0),
+            initial(initial) {
+        // empty
+    }
+
+    virtual ~AssbinChunkWriter() {
+        if (container) {
+            container->Write(&magic, sizeof(uint32_t), 1);
+            container->Write(&cursor, sizeof(uint32_t), 1);
+            container->Write(buffer, 1, cursor);
+        }
+        if (buffer) delete[] buffer;
+    }
+
+    void *GetBufferPointer() { return buffer; }
+
+    // -------------------------------------------------------------------
+    virtual size_t Read(void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) {
+        return 0;
+    }
+    virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
+        return aiReturn_FAILURE;
+    }
+    virtual size_t Tell() const {
+        return cursor;
+    }
+    virtual void Flush() {
+        // not implemented
+    }
+
+    virtual size_t FileSize() const {
+        return cursor;
+    }
+
+    // -------------------------------------------------------------------
+    virtual size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) {
+        pSize *= pCount;
+        if (cursor + pSize > cur_size) {
+            Grow(cursor + pSize);
+        }
+
+        memcpy(buffer + cursor, pvBuffer, pSize);
+        cursor += pSize;
+
+        return pCount;
+    }
+};
+
+// ----------------------------------------------------------------------------------
+/** @class  AssbinFileWriter
+ *  @brief  Assbin file writer class
+ *
+ *  This class writes an .assbin file, and is responsible for the file layout.
+ */
+class AssbinFileWriter {
+private:
+    bool shortened;
+    bool compressed;
+
+protected:
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryNode(IOStream *container, const aiNode *node) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODE);
+
+        unsigned int nb_metadata = (node->mMetaData != NULL ? node->mMetaData->mNumProperties : 0);
+
+        Write<aiString>(&chunk, node->mName);
+        Write<aiMatrix4x4>(&chunk, node->mTransformation);
+        Write<unsigned int>(&chunk, node->mNumChildren);
+        Write<unsigned int>(&chunk, node->mNumMeshes);
+        Write<unsigned int>(&chunk, nb_metadata);
+
+        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+            Write<unsigned int>(&chunk, node->mMeshes[i]);
+        }
+
+        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+            WriteBinaryNode(&chunk, node->mChildren[i]);
+        }
+
+        for (unsigned int i = 0; i < nb_metadata; ++i) {
+            const aiString &key = node->mMetaData->mKeys[i];
+            aiMetadataType type = node->mMetaData->mValues[i].mType;
+            void *value = node->mMetaData->mValues[i].mData;
+
+            Write<aiString>(&chunk, key);
+            Write<uint16_t>(&chunk, (uint16_t)type);
+
+            switch (type) {
+            case AI_BOOL:
+                Write<bool>(&chunk, *((bool *)value));
+                break;
+            case AI_INT32:
+                Write<int32_t>(&chunk, *((int32_t *)value));
+                break;
+            case AI_UINT64:
+                Write<uint64_t>(&chunk, *((uint64_t *)value));
+                break;
+            case AI_FLOAT:
+                Write<float>(&chunk, *((float *)value));
+                break;
+            case AI_DOUBLE:
+                Write<double>(&chunk, *((double *)value));
+                break;
+            case AI_AISTRING:
+                Write<aiString>(&chunk, *((aiString *)value));
+                break;
+            case AI_AIVECTOR3D:
+                Write<aiVector3D>(&chunk, *((aiVector3D *)value));
+                break;
+#ifdef SWIG
+                case FORCE_32BIT:
+#endif // SWIG
+            default:
+                break;
+            }
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryTexture(IOStream *container, const aiTexture *tex) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AITEXTURE);
+
+        Write<unsigned int>(&chunk, tex->mWidth);
+        Write<unsigned int>(&chunk, tex->mHeight);
+        // Write the texture format, but don't include the null terminator.
+        chunk.Write(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1);
+
+        if (!shortened) {
+            if (!tex->mHeight) {
+                chunk.Write(tex->pcData, 1, tex->mWidth);
+            } else {
+                chunk.Write(tex->pcData, 1, tex->mWidth * tex->mHeight * 4);
+            }
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryBone(IOStream *container, const aiBone *b) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIBONE);
+
+        Write<aiString>(&chunk, b->mName);
+        Write<unsigned int>(&chunk, b->mNumWeights);
+        Write<aiMatrix4x4>(&chunk, b->mOffsetMatrix);
+
+        // for the moment we write dumb min/max values for the bones, too.
+        // maybe I'll add a better, hash-like solution later
+        if (shortened) {
+            WriteBounds(&chunk, b->mWeights, b->mNumWeights);
+        } // else write as usual
+        else
+            WriteArray<aiVertexWeight>(&chunk, b->mWeights, b->mNumWeights);
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryMesh(IOStream *container, const aiMesh *mesh) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMESH);
+
+        Write<unsigned int>(&chunk, mesh->mPrimitiveTypes);
+        Write<unsigned int>(&chunk, mesh->mNumVertices);
+        Write<unsigned int>(&chunk, mesh->mNumFaces);
+        Write<unsigned int>(&chunk, mesh->mNumBones);
+        Write<unsigned int>(&chunk, mesh->mMaterialIndex);
+
+        // first of all, write bits for all existent vertex components
+        unsigned int c = 0;
+        if (mesh->mVertices) {
+            c |= ASSBIN_MESH_HAS_POSITIONS;
+        }
+        if (mesh->mNormals) {
+            c |= ASSBIN_MESH_HAS_NORMALS;
+        }
+        if (mesh->mTangents && mesh->mBitangents) {
+            c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS;
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
+            if (!mesh->mTextureCoords[n]) {
+                break;
+            }
+            c |= ASSBIN_MESH_HAS_TEXCOORD(n);
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
+            if (!mesh->mColors[n]) {
+                break;
+            }
+            c |= ASSBIN_MESH_HAS_COLOR(n);
+        }
+        Write<unsigned int>(&chunk, c);
+
+        aiVector3D minVec, maxVec;
+        if (mesh->mVertices) {
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mVertices, mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiVector3D>(&chunk, mesh->mVertices, mesh->mNumVertices);
+        }
+        if (mesh->mNormals) {
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mNormals, mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiVector3D>(&chunk, mesh->mNormals, mesh->mNumVertices);
+        }
+        if (mesh->mTangents && mesh->mBitangents) {
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mTangents, mesh->mNumVertices);
+                WriteBounds(&chunk, mesh->mBitangents, mesh->mNumVertices);
+            } // else write as usual
+            else {
+                WriteArray<aiVector3D>(&chunk, mesh->mTangents, mesh->mNumVertices);
+                WriteArray<aiVector3D>(&chunk, mesh->mBitangents, mesh->mNumVertices);
+            }
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
+            if (!mesh->mColors[n])
+                break;
+
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mColors[n], mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiColor4D>(&chunk, mesh->mColors[n], mesh->mNumVertices);
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
+            if (!mesh->mTextureCoords[n])
+                break;
+
+            // write number of UV components
+            Write<unsigned int>(&chunk, mesh->mNumUVComponents[n]);
+
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiVector3D>(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
+        }
+
+        // write faces. There are no floating-point calculations involved
+        // in these, so we can write a simple hash over the face data
+        // to the dump file. We generate a single 32 Bit hash for 512 faces
+        // using Assimp's standard hashing function.
+        if (shortened) {
+            unsigned int processed = 0;
+            for (unsigned int job; (job = std::min(mesh->mNumFaces - processed, 512u)); processed += job) {
+                uint32_t hash = 0;
+                for (unsigned int a = 0; a < job; ++a) {
+
+                    const aiFace &f = mesh->mFaces[processed + a];
+                    uint32_t tmp = f.mNumIndices;
+                    hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash);
+                    for (unsigned int i = 0; i < f.mNumIndices; ++i) {
+                        static_assert(AI_MAX_VERTICES <= 0xffffffff, "AI_MAX_VERTICES <= 0xffffffff");
+                        tmp = static_cast<uint32_t>(f.mIndices[i]);
+                        hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash);
+                    }
+                }
+                Write<unsigned int>(&chunk, hash);
+            }
+        } else // else write as usual
+        {
+            // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
+            for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+                const aiFace &f = mesh->mFaces[i];
+
+                static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
+                Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mNumIndices));
+
+                for (unsigned int a = 0; a < f.mNumIndices; ++a) {
+                    if (mesh->mNumVertices < (1u << 16)) {
+                        Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mIndices[a]));
+                    } else {
+                        Write<unsigned int>(&chunk, f.mIndices[a]);
+                    }
+                }
+            }
+        }
+
+        // write bones
+        if (mesh->mNumBones) {
+            for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
+                const aiBone *b = mesh->mBones[a];
+                WriteBinaryBone(&chunk, b);
+            }
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryMaterialProperty(IOStream *container, const aiMaterialProperty *prop) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIALPROPERTY);
+
+        Write<aiString>(&chunk, prop->mKey);
+        Write<unsigned int>(&chunk, prop->mSemantic);
+        Write<unsigned int>(&chunk, prop->mIndex);
+
+        Write<unsigned int>(&chunk, prop->mDataLength);
+        Write<unsigned int>(&chunk, (unsigned int)prop->mType);
+        chunk.Write(prop->mData, 1, prop->mDataLength);
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryMaterial(IOStream *container, const aiMaterial *mat) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIAL);
+
+        Write<unsigned int>(&chunk, mat->mNumProperties);
+        for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
+            WriteBinaryMaterialProperty(&chunk, mat->mProperties[i]);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryNodeAnim(IOStream *container, const aiNodeAnim *nd) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODEANIM);
+
+        Write<aiString>(&chunk, nd->mNodeName);
+        Write<unsigned int>(&chunk, nd->mNumPositionKeys);
+        Write<unsigned int>(&chunk, nd->mNumRotationKeys);
+        Write<unsigned int>(&chunk, nd->mNumScalingKeys);
+        Write<unsigned int>(&chunk, nd->mPreState);
+        Write<unsigned int>(&chunk, nd->mPostState);
+
+        if (nd->mPositionKeys) {
+            if (shortened) {
+                WriteBounds(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
+
+            } // else write as usual
+            else
+                WriteArray<aiVectorKey>(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
+        }
+        if (nd->mRotationKeys) {
+            if (shortened) {
+                WriteBounds(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
+
+            } // else write as usual
+            else
+                WriteArray<aiQuatKey>(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
+        }
+        if (nd->mScalingKeys) {
+            if (shortened) {
+                WriteBounds(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
+
+            } // else write as usual
+            else
+                WriteArray<aiVectorKey>(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryAnim(IOStream *container, const aiAnimation *anim) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIANIMATION);
+
+        Write<aiString>(&chunk, anim->mName);
+        Write<double>(&chunk, anim->mDuration);
+        Write<double>(&chunk, anim->mTicksPerSecond);
+        Write<unsigned int>(&chunk, anim->mNumChannels);
+
+        for (unsigned int a = 0; a < anim->mNumChannels; ++a) {
+            const aiNodeAnim *nd = anim->mChannels[a];
+            WriteBinaryNodeAnim(&chunk, nd);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryLight(IOStream *container, const aiLight *l) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AILIGHT);
+
+        Write<aiString>(&chunk, l->mName);
+        Write<unsigned int>(&chunk, l->mType);
+
+        if (l->mType != aiLightSource_DIRECTIONAL) {
+            Write<float>(&chunk, l->mAttenuationConstant);
+            Write<float>(&chunk, l->mAttenuationLinear);
+            Write<float>(&chunk, l->mAttenuationQuadratic);
+        }
+
+        Write<aiColor3D>(&chunk, l->mColorDiffuse);
+        Write<aiColor3D>(&chunk, l->mColorSpecular);
+        Write<aiColor3D>(&chunk, l->mColorAmbient);
+
+        if (l->mType == aiLightSource_SPOT) {
+            Write<float>(&chunk, l->mAngleInnerCone);
+            Write<float>(&chunk, l->mAngleOuterCone);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryCamera(IOStream *container, const aiCamera *cam) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AICAMERA);
+
+        Write<aiString>(&chunk, cam->mName);
+        Write<aiVector3D>(&chunk, cam->mPosition);
+        Write<aiVector3D>(&chunk, cam->mLookAt);
+        Write<aiVector3D>(&chunk, cam->mUp);
+        Write<float>(&chunk, cam->mHorizontalFOV);
+        Write<float>(&chunk, cam->mClipPlaneNear);
+        Write<float>(&chunk, cam->mClipPlaneFar);
+        Write<float>(&chunk, cam->mAspect);
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryScene(IOStream *container, const aiScene *scene) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AISCENE);
+
+        // basic scene information
+        Write<unsigned int>(&chunk, scene->mFlags);
+        Write<unsigned int>(&chunk, scene->mNumMeshes);
+        Write<unsigned int>(&chunk, scene->mNumMaterials);
+        Write<unsigned int>(&chunk, scene->mNumAnimations);
+        Write<unsigned int>(&chunk, scene->mNumTextures);
+        Write<unsigned int>(&chunk, scene->mNumLights);
+        Write<unsigned int>(&chunk, scene->mNumCameras);
+
+        // write node graph
+        WriteBinaryNode(&chunk, scene->mRootNode);
+
+        // write all meshes
+        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+            const aiMesh *mesh = scene->mMeshes[i];
+            WriteBinaryMesh(&chunk, mesh);
+        }
+
+        // write materials
+        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+            const aiMaterial *mat = scene->mMaterials[i];
+            WriteBinaryMaterial(&chunk, mat);
+        }
+
+        // write all animations
+        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
+            const aiAnimation *anim = scene->mAnimations[i];
+            WriteBinaryAnim(&chunk, anim);
+        }
+
+        // write all textures
+        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
+            const aiTexture *mesh = scene->mTextures[i];
+            WriteBinaryTexture(&chunk, mesh);
+        }
+
+        // write lights
+        for (unsigned int i = 0; i < scene->mNumLights; ++i) {
+            const aiLight *l = scene->mLights[i];
+            WriteBinaryLight(&chunk, l);
+        }
+
+        // write cameras
+        for (unsigned int i = 0; i < scene->mNumCameras; ++i) {
+            const aiCamera *cam = scene->mCameras[i];
+            WriteBinaryCamera(&chunk, cam);
+        }
+    }
+
+public:
+    AssbinFileWriter(bool shortened, bool compressed) :
+            shortened(shortened), compressed(compressed) {
+    }
+
+    // -----------------------------------------------------------------------------------
+    // Write a binary model dump
+    void WriteBinaryDump(const char *pFile, const char *cmd, IOSystem *pIOSystem, const aiScene *pScene) {
+        IOStream *out = pIOSystem->Open(pFile, "wb");
+        if (!out)
+            throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n');
+
+        auto CloseIOStream = [&]() {
+            if (out) {
+                pIOSystem->Close(out);
+                out = nullptr; // Ensure this is only done once.
+            }
+        };
+
+        try {
+            time_t tt = time(NULL);
+#if _WIN32
+            tm *p = gmtime(&tt);
+#else
+            struct tm now;
+            tm *p = gmtime_r(&tt, &now);
+#endif
+
+            // header
+            char s[64];
+            memset(s, 0, 64);
+#if _MSC_VER >= 1400
+            sprintf_s(s, "ASSIMP.binary-dump.%s", asctime(p));
+#else
+            ai_snprintf(s, 64, "ASSIMP.binary-dump.%s", asctime(p));
+#endif
+            out->Write(s, 44, 1);
+            // == 44 bytes
+
+            Write<unsigned int>(out, ASSBIN_VERSION_MAJOR);
+            Write<unsigned int>(out, ASSBIN_VERSION_MINOR);
+            Write<unsigned int>(out, aiGetVersionRevision());
+            Write<unsigned int>(out, aiGetCompileFlags());
+            Write<uint16_t>(out, shortened);
+            Write<uint16_t>(out, compressed);
+            // ==  20 bytes
+
+            char buff[256] = { 0 };
+            ai_snprintf(buff, 256, "%s", pFile);
+            out->Write(buff, sizeof(char), 256);
+
+            memset(buff, 0, sizeof(buff));
+            ai_snprintf(buff, 128, "%s", cmd);
+            out->Write(buff, sizeof(char), 128);
+
+            // leave 64 bytes free for future extensions
+            memset(buff, 0xcd, 64);
+            out->Write(buff, sizeof(char), 64);
+            // == 435 bytes
+
+            // ==== total header size: 512 bytes
+            ai_assert(out->Tell() == ASSBIN_HEADER_LENGTH);
+
+            // Up to here the data is uncompressed. For compressed files, the rest
+            // is compressed using standard DEFLATE from zlib.
+            if (compressed) {
+                AssbinChunkWriter uncompressedStream(NULL, 0);
+                WriteBinaryScene(&uncompressedStream, pScene);
+
+                uLongf uncompressedSize = static_cast<uLongf>(uncompressedStream.Tell());
+                uLongf compressedSize = (uLongf)compressBound(uncompressedSize);
+                uint8_t *compressedBuffer = new uint8_t[compressedSize];
+
+                int res = compress2(compressedBuffer, &compressedSize, (const Bytef *)uncompressedStream.GetBufferPointer(), uncompressedSize, 9);
+                if (res != Z_OK) {
+                    delete[] compressedBuffer;
+                    throw DeadlyExportError("Compression failed.");
+                }
+
+                out->Write(&uncompressedSize, sizeof(uint32_t), 1);
+                out->Write(compressedBuffer, sizeof(char), compressedSize);
+
+                delete[] compressedBuffer;
+            } else {
+                WriteBinaryScene(out, pScene);
+            }
+
+            CloseIOStream();
+        } catch (...) {
+            CloseIOStream();
+            throw;
+        }
+    }
+};
+
+void DumpSceneToAssbin(
+        const char *pFile, const char *cmd, IOSystem *pIOSystem,
+        const aiScene *pScene, bool shortened, bool compressed) {
+    AssbinFileWriter fileWriter(shortened, compressed);
+    fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
+}
+#ifdef _WIN32
+#pragma warning(pop)
+#endif // _WIN32
+
+} // end of namespace Assimp

+ 65 - 0
code/AssetLib/Assbin/AssbinFileWriter.h

@@ -0,0 +1,65 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file AssbinFileWriter.h
+ *  @brief Declaration of Assbin file writer.
+ */
+
+#ifndef AI_ASSBINFILEWRITER_H_INC
+#define AI_ASSBINFILEWRITER_H_INC
+
+#include <assimp/defs.h>
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp {
+
+void ASSIMP_API DumpSceneToAssbin(
+        const char *pFile,
+        const char *cmd,
+        IOSystem *pIOSystem,
+        const aiScene *pScene,
+        bool shortened,
+        bool compressed);
+
+}
+
+#endif // AI_ASSBINFILEWRITER_H_INC

+ 225 - 228
code/Assbin/AssbinLoader.cpp → code/AssetLib/Assbin/AssbinLoader.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -50,19 +48,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 
 // internal headers
-#include "Assbin/AssbinLoader.h"
+#include "AssetLib/Assbin/AssbinLoader.h"
 #include "Common/assbin_chunks.h"
 #include <assimp/MemoryIOWrapper.h>
-#include <assimp/mesh.h>
 #include <assimp/anim.h>
-#include <assimp/scene.h>
 #include <assimp/importerdesc.h>
+#include <assimp/mesh.h>
+#include <assimp/scene.h>
 #include <memory>
 
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#   include <zlib.h>
+#include <zlib.h>
 #else
-#   include <contrib/zlib/zlib.h>
+#include <contrib/zlib/zlib.h>
 #endif
 
 using namespace Assimp;
@@ -81,94 +79,97 @@ static const aiImporterDesc desc = {
 };
 
 // -----------------------------------------------------------------------------------
-const aiImporterDesc* AssbinImporter::GetInfo() const {
+const aiImporterDesc *AssbinImporter::GetInfo() const {
     return &desc;
 }
 
 // -----------------------------------------------------------------------------------
-bool AssbinImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
-    IOStream * in = pIOHandler->Open(pFile);
+bool AssbinImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    IOStream *in = pIOHandler->Open(pFile);
     if (nullptr == in) {
         return false;
     }
 
     char s[32];
-    in->Read( s, sizeof(char), 32 );
+    in->Read(s, sizeof(char), 32);
 
     pIOHandler->Close(in);
 
-    return strncmp( s, "ASSIMP.binary-dump.", 19 ) == 0;
+    return strncmp(s, "ASSIMP.binary-dump.", 19) == 0;
 }
 
 // -----------------------------------------------------------------------------------
 template <typename T>
-T Read(IOStream * stream) {
+T Read(IOStream *stream) {
     T t;
-    size_t res = stream->Read( &t, sizeof(T), 1 );
-    if(res != 1)
+    size_t res = stream->Read(&t, sizeof(T), 1);
+    if (res != 1) {
         throw DeadlyImportError("Unexpected EOF");
+    }
     return t;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiVector3D Read<aiVector3D>(IOStream * stream) {
+aiVector3D Read<aiVector3D>(IOStream *stream) {
     aiVector3D v;
-    v.x = Read<float>(stream);
-    v.y = Read<float>(stream);
-    v.z = Read<float>(stream);
+    v.x = Read<ai_real>(stream);
+    v.y = Read<ai_real>(stream);
+    v.z = Read<ai_real>(stream);
     return v;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiColor4D Read<aiColor4D>(IOStream * stream) {
+aiColor4D Read<aiColor4D>(IOStream *stream) {
     aiColor4D c;
-    c.r = Read<float>(stream);
-    c.g = Read<float>(stream);
-    c.b = Read<float>(stream);
-    c.a = Read<float>(stream);
+    c.r = Read<ai_real>(stream);
+    c.g = Read<ai_real>(stream);
+    c.b = Read<ai_real>(stream);
+    c.a = Read<ai_real>(stream);
     return c;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiQuaternion Read<aiQuaternion>(IOStream * stream) {
+aiQuaternion Read<aiQuaternion>(IOStream *stream) {
     aiQuaternion v;
-    v.w = Read<float>(stream);
-    v.x = Read<float>(stream);
-    v.y = Read<float>(stream);
-    v.z = Read<float>(stream);
+    v.w = Read<ai_real>(stream);
+    v.x = Read<ai_real>(stream);
+    v.y = Read<ai_real>(stream);
+    v.z = Read<ai_real>(stream);
     return v;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiString Read<aiString>(IOStream * stream) {
+aiString Read<aiString>(IOStream *stream) {
     aiString s;
-    stream->Read(&s.length,4,1);
-    if(s.length)
-        stream->Read(s.data,s.length,1);
+    stream->Read(&s.length, 4, 1);
+    if (s.length) {
+        stream->Read(s.data, s.length, 1);
+    }
     s.data[s.length] = 0;
+
     return s;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiVertexWeight Read<aiVertexWeight>(IOStream * stream) {
+aiVertexWeight Read<aiVertexWeight>(IOStream *stream) {
     aiVertexWeight w;
     w.mVertexId = Read<unsigned int>(stream);
-    w.mWeight = Read<float>(stream);
+    w.mWeight = Read<ai_real>(stream);
     return w;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream) {
+aiMatrix4x4 Read<aiMatrix4x4>(IOStream *stream) {
     aiMatrix4x4 m;
-    for (unsigned int i = 0; i < 4;++i) {
-        for (unsigned int i2 = 0; i2 < 4;++i2) {
-            m[i][i2] = Read<float>(stream);
+    for (unsigned int i = 0; i < 4; ++i) {
+        for (unsigned int i2 = 0; i2 < 4; ++i2) {
+            m[i][i2] = Read<ai_real>(stream);
         }
     }
     return m;
@@ -176,7 +177,7 @@ aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream) {
 
 // -----------------------------------------------------------------------------------
 template <>
-aiVectorKey Read<aiVectorKey>(IOStream * stream) {
+aiVectorKey Read<aiVectorKey>(IOStream *stream) {
     aiVectorKey v;
     v.mTime = Read<double>(stream);
     v.mValue = Read<aiVector3D>(stream);
@@ -185,7 +186,7 @@ aiVectorKey Read<aiVectorKey>(IOStream * stream) {
 
 // -----------------------------------------------------------------------------------
 template <>
-aiQuatKey Read<aiQuatKey>(IOStream * stream) {
+aiQuatKey Read<aiQuatKey>(IOStream *stream) {
     aiQuatKey v;
     v.mTime = Read<double>(stream);
     v.mValue = Read<aiQuaternion>(stream);
@@ -194,27 +195,27 @@ aiQuatKey Read<aiQuatKey>(IOStream * stream) {
 
 // -----------------------------------------------------------------------------------
 template <typename T>
-void ReadArray( IOStream *stream, T * out, unsigned int size) {
-    ai_assert( nullptr != stream );
-    ai_assert( nullptr != out );
+void ReadArray(IOStream *stream, T *out, unsigned int size) {
+    ai_assert(nullptr != stream);
+    ai_assert(nullptr != out);
 
-    for (unsigned int i=0; i<size; i++) {
+    for (unsigned int i = 0; i < size; i++) {
         out[i] = Read<T>(stream);
     }
 }
 
 // -----------------------------------------------------------------------------------
 template <typename T>
-void ReadBounds( IOStream * stream, T* /*p*/, unsigned int n ) {
+void ReadBounds(IOStream *stream, T * /*p*/, unsigned int n) {
     // not sure what to do here, the data isn't really useful.
-    stream->Seek( sizeof(T) * n, aiOrigin_CUR );
+    stream->Seek(sizeof(T) * n, aiOrigin_CUR);
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** onode, aiNode* parent ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODE)
+void AssbinImporter::ReadBinaryNode(IOStream *stream, aiNode **onode, aiNode *parent) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     std::unique_ptr<aiNode> node(new aiNode());
 
@@ -222,14 +223,13 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** onode, aiNode*
     node->mTransformation = Read<aiMatrix4x4>(stream);
     unsigned numChildren = Read<unsigned int>(stream);
     unsigned numMeshes = Read<unsigned int>(stream);
-	unsigned int nb_metadata = Read<unsigned int>(stream);
+    unsigned int nb_metadata = Read<unsigned int>(stream);
 
-    if(parent) {
+    if (parent) {
         node->mParent = parent;
     }
 
-    if (numMeshes)
-    {
+    if (numMeshes) {
         node->mMeshes = new unsigned int[numMeshes];
         for (unsigned int i = 0; i < numMeshes; ++i) {
             node->mMeshes[i] = Read<unsigned int>(stream);
@@ -238,60 +238,60 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** onode, aiNode*
     }
 
     if (numChildren) {
-        node->mChildren = new aiNode*[numChildren];
+        node->mChildren = new aiNode *[numChildren];
         for (unsigned int i = 0; i < numChildren; ++i) {
-            ReadBinaryNode( stream, &node->mChildren[i], node.get() );
+            ReadBinaryNode(stream, &node->mChildren[i], node.get());
             node->mNumChildren++;
         }
     }
 
-    if ( nb_metadata > 0 ) {
+    if (nb_metadata > 0) {
         node->mMetaData = aiMetadata::Alloc(nb_metadata);
         for (unsigned int i = 0; i < nb_metadata; ++i) {
             node->mMetaData->mKeys[i] = Read<aiString>(stream);
-            node->mMetaData->mValues[i].mType = (aiMetadataType) Read<uint16_t>(stream);
-            void* data = nullptr;
+            node->mMetaData->mValues[i].mType = (aiMetadataType)Read<uint16_t>(stream);
+            void *data = nullptr;
 
             switch (node->mMetaData->mValues[i].mType) {
-                case AI_BOOL:
-                    data = new bool(Read<bool>(stream));
-                    break;
-                case AI_INT32:
-                    data = new int32_t(Read<int32_t>(stream));
-                    break;
-                case AI_UINT64:
-                    data = new uint64_t(Read<uint64_t>(stream));
-                    break;
-                case AI_FLOAT:
-                    data = new float(Read<float>(stream));
-                    break;
-                case AI_DOUBLE:
-                    data = new double(Read<double>(stream));
-                    break;
-                case AI_AISTRING:
-                    data = new aiString(Read<aiString>(stream));
-                    break;
-                case AI_AIVECTOR3D:
-                    data = new aiVector3D(Read<aiVector3D>(stream));
-                    break;
+            case AI_BOOL:
+                data = new bool(Read<bool>(stream));
+                break;
+            case AI_INT32:
+                data = new int32_t(Read<int32_t>(stream));
+                break;
+            case AI_UINT64:
+                data = new uint64_t(Read<uint64_t>(stream));
+                break;
+            case AI_FLOAT:
+                data = new ai_real(Read<ai_real>(stream));
+                break;
+            case AI_DOUBLE:
+                data = new double(Read<double>(stream));
+                break;
+            case AI_AISTRING:
+                data = new aiString(Read<aiString>(stream));
+                break;
+            case AI_AIVECTOR3D:
+                data = new aiVector3D(Read<aiVector3D>(stream));
+                break;
 #ifndef SWIG
-                case FORCE_32BIT:
+            case FORCE_32BIT:
 #endif // SWIG
-                default:
-                    break;
+            default:
+                break;
             }
 
-			node->mMetaData->mValues[i].mData = data;
-		}
-	}
+            node->mMetaData->mValues[i].mData = data;
+        }
+    }
     *onode = node.release();
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIBONE)
+void AssbinImporter::ReadBinaryBone(IOStream *stream, aiBone *b) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIBONE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     b->mName = Read<aiString>(stream);
     b->mNumWeights = Read<unsigned int>(stream);
@@ -300,23 +300,24 @@ void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b ) {
     // for the moment we write dumb min/max values for the bones, too.
     // maybe I'll add a better, hash-like solution later
     if (shortened) {
-        ReadBounds(stream,b->mWeights,b->mNumWeights);
+        ReadBounds(stream, b->mWeights, b->mNumWeights);
     } else {
         // else write as usual
         b->mWeights = new aiVertexWeight[b->mNumWeights];
-        ReadArray<aiVertexWeight>(stream,b->mWeights,b->mNumWeights);
+        ReadArray<aiVertexWeight>(stream, b->mWeights, b->mNumWeights);
     }
 }
 
 // -----------------------------------------------------------------------------------
 static bool fitsIntoUI16(unsigned int mNumVertices) {
-    return ( mNumVertices < (1u<<16) );
+    return (mNumVertices < (1u << 16));
 }
+
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMESH)
+void AssbinImporter::ReadBinaryMesh(IOStream *stream, aiMesh *mesh) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMESH)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     mesh->mPrimitiveTypes = Read<unsigned int>(stream);
     mesh->mNumVertices = Read<unsigned int>(stream);
@@ -329,48 +330,48 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
 
     if (c & ASSBIN_MESH_HAS_POSITIONS) {
         if (shortened) {
-            ReadBounds(stream,mesh->mVertices,mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mVertices, mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mVertices = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mVertices,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mVertices, mesh->mNumVertices);
         }
     }
     if (c & ASSBIN_MESH_HAS_NORMALS) {
         if (shortened) {
-            ReadBounds(stream,mesh->mNormals,mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mNormals, mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mNormals = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mNormals,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mNormals, mesh->mNumVertices);
         }
     }
     if (c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS) {
         if (shortened) {
-            ReadBounds(stream,mesh->mTangents,mesh->mNumVertices);
-            ReadBounds(stream,mesh->mBitangents,mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mTangents, mesh->mNumVertices);
+            ReadBounds(stream, mesh->mBitangents, mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mTangents = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mTangents,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mTangents, mesh->mNumVertices);
             mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mBitangents,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mBitangents, mesh->mNumVertices);
         }
     }
-    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
+    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
         if (!(c & ASSBIN_MESH_HAS_COLOR(n))) {
             break;
         }
 
         if (shortened) {
-            ReadBounds(stream,mesh->mColors[n],mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mColors[n], mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mColors[n] = new aiColor4D[mesh->mNumVertices];
-            ReadArray<aiColor4D>(stream,mesh->mColors[n],mesh->mNumVertices);
+            ReadArray<aiColor4D>(stream, mesh->mColors[n], mesh->mNumVertices);
         }
     }
-    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
+    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
         if (!(c & ASSBIN_MESH_HAS_TEXCOORD(n))) {
             break;
         }
@@ -379,11 +380,11 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
         mesh->mNumUVComponents[n] = Read<unsigned int>(stream);
 
         if (shortened) {
-            ReadBounds(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mTextureCoords[n], mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mTextureCoords[n], mesh->mNumVertices);
         }
     }
 
@@ -393,20 +394,20 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
     // using Assimp's standard hashing function.
     if (shortened) {
         Read<unsigned int>(stream);
-    } else  {
+    } else {
         // else write as usual
         // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
         mesh->mFaces = new aiFace[mesh->mNumFaces];
-        for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
-            aiFace& f = mesh->mFaces[i];
+        for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+            aiFace &f = mesh->mFaces[i];
 
             static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
             f.mNumIndices = Read<uint16_t>(stream);
             f.mIndices = new unsigned int[f.mNumIndices];
 
-            for (unsigned int a = 0; a < f.mNumIndices;++a) {
+            for (unsigned int a = 0; a < f.mNumIndices; ++a) {
                 // Check if unsigned  short ( 16 bit  ) are big enought for the indices
-                if ( fitsIntoUI16( mesh->mNumVertices ) ) {
+                if (fitsIntoUI16(mesh->mNumVertices)) {
                     f.mIndices[a] = Read<uint16_t>(stream);
                 } else {
                     f.mIndices[a] = Read<unsigned int>(stream);
@@ -417,19 +418,19 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
 
     // write bones
     if (mesh->mNumBones) {
-        mesh->mBones = new C_STRUCT aiBone*[mesh->mNumBones];
-        for (unsigned int a = 0; a < mesh->mNumBones;++a) {
+        mesh->mBones = new C_STRUCT aiBone *[mesh->mNumBones];
+        for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
             mesh->mBones[a] = new aiBone();
-            ReadBinaryBone(stream,mesh->mBones[a]);
+            ReadBinaryBone(stream, mesh->mBones[a]);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIALPROPERTY)
+void AssbinImporter::ReadBinaryMaterialProperty(IOStream *stream, aiMaterialProperty *prop) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIALPROPERTY)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     prop->mKey = Read<aiString>(stream);
     prop->mSemantic = Read<unsigned int>(stream);
@@ -437,36 +438,34 @@ void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialPro
 
     prop->mDataLength = Read<unsigned int>(stream);
     prop->mType = (aiPropertyTypeInfo)Read<unsigned int>(stream);
-    prop->mData = new char [ prop->mDataLength ];
-    stream->Read(prop->mData,1,prop->mDataLength);
+    prop->mData = new char[prop->mDataLength];
+    stream->Read(prop->mData, 1, prop->mDataLength);
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIAL)
+void AssbinImporter::ReadBinaryMaterial(IOStream *stream, aiMaterial *mat) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIAL)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     mat->mNumAllocated = mat->mNumProperties = Read<unsigned int>(stream);
-    if (mat->mNumProperties)
-    {
-        if (mat->mProperties)
-        {
+    if (mat->mNumProperties) {
+        if (mat->mProperties) {
             delete[] mat->mProperties;
         }
-        mat->mProperties = new aiMaterialProperty*[mat->mNumProperties];
-        for (unsigned int i = 0; i < mat->mNumProperties;++i) {
+        mat->mProperties = new aiMaterialProperty *[mat->mNumProperties];
+        for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
             mat->mProperties[i] = new aiMaterialProperty();
-            ReadBinaryMaterialProperty( stream, mat->mProperties[i]);
+            ReadBinaryMaterialProperty(stream, mat->mProperties[i]);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODEANIM)
+void AssbinImporter::ReadBinaryNodeAnim(IOStream *stream, aiNodeAnim *nd) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODEANIM)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     nd->mNodeName = Read<aiString>(stream);
     nd->mNumPositionKeys = Read<unsigned int>(stream);
@@ -477,82 +476,82 @@ void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd) {
 
     if (nd->mNumPositionKeys) {
         if (shortened) {
-            ReadBounds(stream,nd->mPositionKeys,nd->mNumPositionKeys);
+            ReadBounds(stream, nd->mPositionKeys, nd->mNumPositionKeys);
 
         } // else write as usual
         else {
             nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
-            ReadArray<aiVectorKey>(stream,nd->mPositionKeys,nd->mNumPositionKeys);
+            ReadArray<aiVectorKey>(stream, nd->mPositionKeys, nd->mNumPositionKeys);
         }
     }
     if (nd->mNumRotationKeys) {
         if (shortened) {
-            ReadBounds(stream,nd->mRotationKeys,nd->mNumRotationKeys);
+            ReadBounds(stream, nd->mRotationKeys, nd->mNumRotationKeys);
 
-        }  else {
+        } else {
             // else write as usual
             nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
-            ReadArray<aiQuatKey>(stream,nd->mRotationKeys,nd->mNumRotationKeys);
+            ReadArray<aiQuatKey>(stream, nd->mRotationKeys, nd->mNumRotationKeys);
         }
     }
     if (nd->mNumScalingKeys) {
         if (shortened) {
-            ReadBounds(stream,nd->mScalingKeys,nd->mNumScalingKeys);
+            ReadBounds(stream, nd->mScalingKeys, nd->mNumScalingKeys);
 
-        }  else {
+        } else {
             // else write as usual
             nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys];
-            ReadArray<aiVectorKey>(stream,nd->mScalingKeys,nd->mNumScalingKeys);
+            ReadArray<aiVectorKey>(stream, nd->mScalingKeys, nd->mNumScalingKeys);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIANIMATION)
+void AssbinImporter::ReadBinaryAnim(IOStream *stream, aiAnimation *anim) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIANIMATION)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
-    anim->mName = Read<aiString> (stream);
-    anim->mDuration = Read<double> (stream);
-    anim->mTicksPerSecond = Read<double> (stream);
+    anim->mName = Read<aiString>(stream);
+    anim->mDuration = Read<double>(stream);
+    anim->mTicksPerSecond = Read<double>(stream);
     anim->mNumChannels = Read<unsigned int>(stream);
 
     if (anim->mNumChannels) {
-        anim->mChannels = new aiNodeAnim*[ anim->mNumChannels ];
-        for (unsigned int a = 0; a < anim->mNumChannels;++a) {
+        anim->mChannels = new aiNodeAnim *[anim->mNumChannels];
+        for (unsigned int a = 0; a < anim->mNumChannels; ++a) {
             anim->mChannels[a] = new aiNodeAnim();
-            ReadBinaryNodeAnim(stream,anim->mChannels[a]);
+            ReadBinaryNodeAnim(stream, anim->mChannels[a]);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AITEXTURE)
+void AssbinImporter::ReadBinaryTexture(IOStream *stream, aiTexture *tex) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AITEXTURE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     tex->mWidth = Read<unsigned int>(stream);
     tex->mHeight = Read<unsigned int>(stream);
-    stream->Read( tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1 );
+    stream->Read(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1);
 
-    if(!shortened) {
+    if (!shortened) {
         if (!tex->mHeight) {
-            tex->pcData = new aiTexel[ tex->mWidth ];
-            stream->Read(tex->pcData,1,tex->mWidth);
+            tex->pcData = new aiTexel[tex->mWidth];
+            stream->Read(tex->pcData, 1, tex->mWidth);
         } else {
-            tex->pcData = new aiTexel[ tex->mWidth*tex->mHeight ];
-            stream->Read(tex->pcData,1,tex->mWidth*tex->mHeight*4);
+            tex->pcData = new aiTexel[tex->mWidth * tex->mHeight];
+            stream->Read(tex->pcData, 1, tex->mWidth * tex->mHeight * 4);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AILIGHT)
+void AssbinImporter::ReadBinaryLight(IOStream *stream, aiLight *l) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AILIGHT)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     l->mName = Read<aiString>(stream);
     l->mType = (aiLightSourceType)Read<unsigned int>(stream);
@@ -574,10 +573,10 @@ void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l ) {
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AICAMERA)
+void AssbinImporter::ReadBinaryCamera(IOStream *stream, aiCamera *cam) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AICAMERA)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     cam->mName = Read<aiString>(stream);
     cam->mPosition = Read<aiVector3D>(stream);
@@ -590,141 +589,139 @@ void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) {
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AISCENE)
+void AssbinImporter::ReadBinaryScene(IOStream *stream, aiScene *scene) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AISCENE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
-    scene->mFlags         = Read<unsigned int>(stream);
-    scene->mNumMeshes     = Read<unsigned int>(stream);
-    scene->mNumMaterials  = Read<unsigned int>(stream);
+    scene->mFlags = Read<unsigned int>(stream);
+    scene->mNumMeshes = Read<unsigned int>(stream);
+    scene->mNumMaterials = Read<unsigned int>(stream);
     scene->mNumAnimations = Read<unsigned int>(stream);
-    scene->mNumTextures   = Read<unsigned int>(stream);
-    scene->mNumLights     = Read<unsigned int>(stream);
-    scene->mNumCameras    = Read<unsigned int>(stream);
+    scene->mNumTextures = Read<unsigned int>(stream);
+    scene->mNumLights = Read<unsigned int>(stream);
+    scene->mNumCameras = Read<unsigned int>(stream);
 
     // Read node graph
     //scene->mRootNode = new aiNode[1];
-    ReadBinaryNode( stream, &scene->mRootNode, (aiNode*)NULL );
+    ReadBinaryNode(stream, &scene->mRootNode, (aiNode *)NULL);
 
     // Read all meshes
     if (scene->mNumMeshes) {
-        scene->mMeshes = new aiMesh*[scene->mNumMeshes];
-        memset(scene->mMeshes, 0, scene->mNumMeshes*sizeof(aiMesh*));
-        for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+        scene->mMeshes = new aiMesh *[scene->mNumMeshes];
+        memset(scene->mMeshes, 0, scene->mNumMeshes * sizeof(aiMesh *));
+        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
             scene->mMeshes[i] = new aiMesh();
-            ReadBinaryMesh( stream,scene->mMeshes[i]);
+            ReadBinaryMesh(stream, scene->mMeshes[i]);
         }
     }
 
     // Read materials
     if (scene->mNumMaterials) {
-        scene->mMaterials = new aiMaterial*[scene->mNumMaterials];
-        memset(scene->mMaterials, 0, scene->mNumMaterials*sizeof(aiMaterial*));
-        for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
+        scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
+        memset(scene->mMaterials, 0, scene->mNumMaterials * sizeof(aiMaterial *));
+        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
             scene->mMaterials[i] = new aiMaterial();
-            ReadBinaryMaterial(stream,scene->mMaterials[i]);
+            ReadBinaryMaterial(stream, scene->mMaterials[i]);
         }
     }
 
     // Read all animations
     if (scene->mNumAnimations) {
-        scene->mAnimations = new aiAnimation*[scene->mNumAnimations];
-        memset(scene->mAnimations, 0, scene->mNumAnimations*sizeof(aiAnimation*));
-        for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
+        scene->mAnimations = new aiAnimation *[scene->mNumAnimations];
+        memset(scene->mAnimations, 0, scene->mNumAnimations * sizeof(aiAnimation *));
+        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
             scene->mAnimations[i] = new aiAnimation();
-            ReadBinaryAnim(stream,scene->mAnimations[i]);
+            ReadBinaryAnim(stream, scene->mAnimations[i]);
         }
     }
 
     // Read all textures
     if (scene->mNumTextures) {
-        scene->mTextures = new aiTexture*[scene->mNumTextures];
-        memset(scene->mTextures, 0, scene->mNumTextures*sizeof(aiTexture*));
-        for (unsigned int i = 0; i < scene->mNumTextures;++i) {
+        scene->mTextures = new aiTexture *[scene->mNumTextures];
+        memset(scene->mTextures, 0, scene->mNumTextures * sizeof(aiTexture *));
+        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
             scene->mTextures[i] = new aiTexture();
-            ReadBinaryTexture(stream,scene->mTextures[i]);
+            ReadBinaryTexture(stream, scene->mTextures[i]);
         }
     }
 
     // Read lights
     if (scene->mNumLights) {
-        scene->mLights = new aiLight*[scene->mNumLights];
-        memset(scene->mLights, 0, scene->mNumLights*sizeof(aiLight*));
-        for (unsigned int i = 0; i < scene->mNumLights;++i) {
+        scene->mLights = new aiLight *[scene->mNumLights];
+        memset(scene->mLights, 0, scene->mNumLights * sizeof(aiLight *));
+        for (unsigned int i = 0; i < scene->mNumLights; ++i) {
             scene->mLights[i] = new aiLight();
-            ReadBinaryLight(stream,scene->mLights[i]);
+            ReadBinaryLight(stream, scene->mLights[i]);
         }
     }
 
     // Read cameras
     if (scene->mNumCameras) {
-        scene->mCameras = new aiCamera*[scene->mNumCameras];
-        memset(scene->mCameras, 0, scene->mNumCameras*sizeof(aiCamera*));
-        for (unsigned int i = 0; i < scene->mNumCameras;++i) {
+        scene->mCameras = new aiCamera *[scene->mNumCameras];
+        memset(scene->mCameras, 0, scene->mNumCameras * sizeof(aiCamera *));
+        for (unsigned int i = 0; i < scene->mNumCameras; ++i) {
             scene->mCameras[i] = new aiCamera();
-            ReadBinaryCamera(stream,scene->mCameras[i]);
+            ReadBinaryCamera(stream, scene->mCameras[i]);
         }
     }
-
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ) {
-    IOStream * stream = pIOHandler->Open(pFile,"rb");
+void AssbinImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    IOStream *stream = pIOHandler->Open(pFile, "rb");
     if (nullptr == stream) {
         return;
     }
 
     // signature
-    stream->Seek( 44, aiOrigin_CUR );
+    stream->Seek(44, aiOrigin_CUR);
 
     unsigned int versionMajor = Read<unsigned int>(stream);
     unsigned int versionMinor = Read<unsigned int>(stream);
     if (versionMinor != ASSBIN_VERSION_MINOR || versionMajor != ASSBIN_VERSION_MAJOR) {
-        throw DeadlyImportError( "Invalid version, data format not compatible!" );
+        throw DeadlyImportError("Invalid version, data format not compatible!");
     }
 
-    /*unsigned int versionRevision =*/ Read<unsigned int>(stream);
-    /*unsigned int compileFlags =*/ Read<unsigned int>(stream);
+    /*unsigned int versionRevision =*/Read<unsigned int>(stream);
+    /*unsigned int compileFlags =*/Read<unsigned int>(stream);
 
     shortened = Read<uint16_t>(stream) > 0;
     compressed = Read<uint16_t>(stream) > 0;
 
     if (shortened)
-        throw DeadlyImportError( "Shortened binaries are not supported!" );
+        throw DeadlyImportError("Shortened binaries are not supported!");
 
-    stream->Seek( 256, aiOrigin_CUR ); // original filename
-    stream->Seek( 128, aiOrigin_CUR ); // options
-    stream->Seek( 64, aiOrigin_CUR ); // padding
+    stream->Seek(256, aiOrigin_CUR); // original filename
+    stream->Seek(128, aiOrigin_CUR); // options
+    stream->Seek(64, aiOrigin_CUR); // padding
 
     if (compressed) {
         uLongf uncompressedSize = Read<uint32_t>(stream);
         uLongf compressedSize = static_cast<uLongf>(stream->FileSize() - stream->Tell());
 
-        unsigned char * compressedData = new unsigned char[ compressedSize ];
-        size_t len = stream->Read( compressedData, 1, compressedSize );
+        unsigned char *compressedData = new unsigned char[compressedSize];
+        size_t len = stream->Read(compressedData, 1, compressedSize);
         ai_assert(len == compressedSize);
 
-        unsigned char * uncompressedData = new unsigned char[ uncompressedSize ];
+        unsigned char *uncompressedData = new unsigned char[uncompressedSize];
 
-        int res = uncompress( uncompressedData, &uncompressedSize, compressedData, (uLong) len );
-        if(res != Z_OK)
-        {
-            delete [] uncompressedData;
-            delete [] compressedData;
+        int res = uncompress(uncompressedData, &uncompressedSize, compressedData, (uLong)len);
+        if (res != Z_OK) {
+            delete[] uncompressedData;
+            delete[] compressedData;
             pIOHandler->Close(stream);
             throw DeadlyImportError("Zlib decompression failed.");
         }
 
-        MemoryIOStream io( uncompressedData, uncompressedSize );
+        MemoryIOStream io(uncompressedData, uncompressedSize);
 
-        ReadBinaryScene(&io,pScene);
+        ReadBinaryScene(&io, pScene);
 
         delete[] uncompressedData;
         delete[] compressedData;
     } else {
-        ReadBinaryScene(stream,pScene);
+        ReadBinaryScene(stream, pScene);
     }
 
     pIOHandler->Close(stream);

+ 0 - 0
code/Assbin/AssbinLoader.h → code/AssetLib/Assbin/AssbinLoader.h


+ 4 - 0
code/Assjson/cencode.c → code/AssetLib/Assjson/cencode.c

@@ -9,6 +9,9 @@ For details, see http://sourceforge.net/projects/libb64
 
 const int CHARS_PER_LINE = 72;
 
+#pragma warning(push)
+#pragma warning(disable : 4244)
+
 void base64_init_encodestate(base64_encodestate* state_in)
 {
 	state_in->step = step_A;
@@ -107,3 +110,4 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
 	return (int)(codechar - code_out);
 }
 
+#pragma warning(pop)

+ 4 - 0
code/Assjson/cencode.h → code/AssetLib/Assjson/cencode.h

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

+ 88 - 99
code/Assjson/json_exporter.cpp → code/AssetLib/Assjson/json_exporter.cpp

@@ -9,30 +9,31 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
 
-#include <assimp/Importer.hpp>
+#include <assimp/scene.h>
 #include <assimp/Exporter.hpp>
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
-#include <assimp/scene.h>
+#include <assimp/Importer.hpp>
+#include <assimp/Exceptional.h>
 
-#include <sstream>
-#include <limits>
 #include <cassert>
+#include <limits>
 #include <memory>
+#include <sstream>
 
 #define CURRENT_FORMAT_VERSION 100
 
-// grab scoped_ptr from assimp to avoid a dependency on boost. 
+// grab scoped_ptr from assimp to avoid a dependency on boost.
 //#include <assimp/../../code/BoostWorkaround/boost/scoped_ptr.hpp>
 
 #include "mesh_splitter.h"
 
 extern "C" {
-    #include "cencode.h"
+#include "cencode.h"
 }
 namespace Assimp {
 
-void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
+void ExportAssimp2Json(const char *, Assimp::IOSystem *, const aiScene *, const Assimp::ExportProperties *);
 
 // small utility class to simplify serializing the aiScene to Json
 class JSONWriter {
@@ -42,10 +43,8 @@ public:
         Flag_WriteSpecialFloats = 0x2,
     };
 
-    JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u)
-    : out(out)
-    , first()
-    , flags(flags) {
+    JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) :
+            out(out), first(), 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"));
     }
@@ -68,43 +67,43 @@ public:
         indent.erase(indent.end() - 1);
     }
 
-    void Key(const std::string& name) {
+    void Key(const std::string &name) {
         AddIndentation();
         Delimit();
         buff << '\"' + name + "\": ";
     }
 
-    template<typename Literal>
-    void Element(const Literal& name) {
+    template <typename Literal>
+    void Element(const Literal &name) {
         AddIndentation();
         Delimit();
 
         LiteralToString(buff, name) << '\n';
     }
 
-    template<typename Literal>
-    void SimpleValue(const Literal& s) {
+    template <typename Literal>
+    void SimpleValue(const Literal &s) {
         LiteralToString(buff, s) << '\n';
     }
 
-    void SimpleValue(const void* buffer, size_t len) {
+    void SimpleValue(const void *buffer, size_t len) {
         base64_encodestate s;
         base64_init_encodestate(&s);
 
-        char* const out = new char[std::max(len * 2, static_cast<size_t>(16u))];
-        const int n = base64_encode_block(reinterpret_cast<const char*>(buffer), static_cast<int>(len), out, &s);
-        out[n + base64_encode_blockend(out + n, &s)] = '\0';
+        char *const cur_out = new char[std::max(len * 2, static_cast<size_t>(16u))];
+        const int n = base64_encode_block(reinterpret_cast<const char *>(buffer), static_cast<int>(len), cur_out, &s);
+        cur_out[n + base64_encode_blockend(cur_out + n, &s)] = '\0';
 
         // base64 encoding may add newlines, but JSON strings may not contain 'real' newlines
         // (only escaped ones). Remove any newlines in out.
-        for (char* cur = out; *cur; ++cur) {
+        for (char *cur = cur_out; *cur; ++cur) {
             if (*cur == '\n') {
                 *cur = ' ';
             }
         }
 
-        buff << '\"' << out << "\"\n";
-        delete[] out;
+        buff << '\"' << cur_out << "\"\n";
+        delete[] cur_out;
     }
 
     void StartObj(bool is_element = false) {
@@ -156,21 +155,20 @@ public:
     void Delimit() {
         if (!first) {
             buff << ',';
-        }
-        else {
+        } else {
             buff << ' ';
             first = false;
         }
     }
 
 private:
-    template<typename Literal>
-    std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) {
+    template <typename Literal>
+    std::stringstream &LiteralToString(std::stringstream &stream, const Literal &s) {
         stream << s;
         return stream;
     }
 
-    std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) {
+    std::stringstream &LiteralToString(std::stringstream &stream, const aiString &s) {
         std::string t;
 
         // escape backslashes and single quotes, both would render the JSON invalid if left as is
@@ -189,10 +187,10 @@ private:
         return stream;
     }
 
-    std::stringstream& LiteralToString(std::stringstream& stream, float f) {
+    std::stringstream &LiteralToString(std::stringstream &stream, float f) {
         if (!std::numeric_limits<float>::is_iec559) {
             // on a non IEEE-754 platform, we make no assumptions about the representation or existence
-            // of special floating-point numbers. 
+            // of special floating-point numbers.
             stream << f;
             return stream;
         }
@@ -228,7 +226,7 @@ private:
     }
 
 private:
-    Assimp::IOStream& out;
+    Assimp::IOStream &out;
     std::string indent, newline;
     std::stringstream buff;
     bool first;
@@ -236,7 +234,7 @@ private:
     unsigned int flags;
 };
 
-void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiVector3D &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     out.Element(ai.x);
     out.Element(ai.y);
@@ -244,7 +242,7 @@ void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiQuaternion &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     out.Element(ai.w);
     out.Element(ai.x);
@@ -253,7 +251,7 @@ void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiColor3D &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     out.Element(ai.r);
     out.Element(ai.g);
@@ -261,7 +259,7 @@ void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiMatrix4x4 &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     for (unsigned int x = 0; x < 4; ++x) {
         for (unsigned int y = 0; y < 4; ++y) {
@@ -271,7 +269,7 @@ void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiBone &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -292,7 +290,7 @@ void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiFace &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     for (unsigned int i = 0; i < ai.mNumIndices; ++i) {
         out.Element(ai.mIndices[i]);
@@ -300,7 +298,7 @@ void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiMesh &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -411,7 +409,7 @@ void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiNode &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -441,13 +439,13 @@ void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiMaterial &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("properties");
     out.StartArray();
     for (unsigned int i = 0; i < ai.mNumProperties; ++i) {
-        const aiMaterialProperty* const prop = ai.mProperties[i];
+        const aiMaterialProperty *const prop = ai.mProperties[i];
         out.StartObj(true);
         out.Key("key");
         out.SimpleValue(prop->mKey);
@@ -461,46 +459,41 @@ void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
 
         out.Key("value");
         switch (prop->mType) {
-            case aiPTI_Float:
-                if (prop->mDataLength / sizeof(float) > 1) {
-                    out.StartArray();
-                    for (unsigned int i = 0; i < prop->mDataLength / sizeof(float); ++i) {
-                        out.Element(reinterpret_cast<float*>(prop->mData)[i]);
-                    }
-                    out.EndArray();
-                }
-                else {
-                    out.SimpleValue(*reinterpret_cast<float*>(prop->mData));
-                }
-                break;
-
-            case aiPTI_Integer:
-                if (prop->mDataLength / sizeof(int) > 1) {
-                    out.StartArray();
-                    for (unsigned int i = 0; i < prop->mDataLength / sizeof(int); ++i) {
-                        out.Element(reinterpret_cast<int*>(prop->mData)[i]);
-                    }
-                    out.EndArray();
-                } else {
-                    out.SimpleValue(*reinterpret_cast<int*>(prop->mData));
+        case aiPTI_Float:
+            if (prop->mDataLength / sizeof(float) > 1) {
+                out.StartArray();
+                for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(float); ++ii) {
+                    out.Element(reinterpret_cast<float *>(prop->mData)[ii]);
                 }
-                break;
+                out.EndArray();
+            } else {
+                out.SimpleValue(*reinterpret_cast<float *>(prop->mData));
+            }
+            break;
 
-            case aiPTI_String:
-                {
-                    aiString s;
-                    aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
-                    out.SimpleValue(s);
-                }
-                break;
-            case aiPTI_Buffer:
-                {
-                    // binary data is written as series of hex-encoded octets
-                    out.SimpleValue(prop->mData, prop->mDataLength);
+        case aiPTI_Integer:
+            if (prop->mDataLength / sizeof(int) > 1) {
+                out.StartArray();
+                for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(int); ++ii) {
+                    out.Element(reinterpret_cast<int *>(prop->mData)[ii]);
                 }
-                break;
-            default:
-                assert(false);
+                out.EndArray();
+            } else {
+                out.SimpleValue(*reinterpret_cast<int *>(prop->mData));
+            }
+            break;
+
+        case aiPTI_String: {
+            aiString s;
+            aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
+            out.SimpleValue(s);
+        } break;
+        case aiPTI_Buffer: {
+            // binary data is written as series of hex-encoded octets
+            out.SimpleValue(prop->mData, prop->mDataLength);
+        } break;
+        default:
+            assert(false);
         }
 
         out.EndObj();
@@ -510,7 +503,7 @@ void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiTexture &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("width");
@@ -525,13 +518,12 @@ void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
     out.Key("data");
     if (!ai.mHeight) {
         out.SimpleValue(ai.pcData, ai.mWidth);
-    }
-    else {
+    } else {
         out.StartArray();
         for (unsigned int y = 0; y < ai.mHeight; ++y) {
             out.StartArray(true);
             for (unsigned int x = 0; x < ai.mWidth; ++x) {
-                const aiTexel& tx = ai.pcData[y*ai.mWidth + x];
+                const aiTexel &tx = ai.pcData[y * ai.mWidth + x];
                 out.StartArray(true);
                 out.Element(static_cast<unsigned int>(tx.r));
                 out.Element(static_cast<unsigned int>(tx.g));
@@ -547,7 +539,7 @@ void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiLight &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -585,7 +577,6 @@ void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
     if (ai.mType != aiLightSource_POINT) {
         out.Key("direction");
         Write(out, ai.mDirection, false);
-
     }
 
     if (ai.mType != aiLightSource_DIRECTIONAL) {
@@ -596,7 +587,7 @@ void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiNodeAnim &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -612,7 +603,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
         out.Key("positionkeys");
         out.StartArray();
         for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) {
-            const aiVectorKey& pos = ai.mPositionKeys[n];
+            const aiVectorKey &pos = ai.mPositionKeys[n];
             out.StartArray(true);
             out.Element(pos.mTime);
             Write(out, pos.mValue);
@@ -625,7 +616,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
         out.Key("rotationkeys");
         out.StartArray();
         for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) {
-            const aiQuatKey& rot = ai.mRotationKeys[n];
+            const aiQuatKey &rot = ai.mRotationKeys[n];
             out.StartArray(true);
             out.Element(rot.mTime);
             Write(out, rot.mValue);
@@ -638,7 +629,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
         out.Key("scalingkeys");
         out.StartArray();
         for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) {
-            const aiVectorKey& scl = ai.mScalingKeys[n];
+            const aiVectorKey &scl = ai.mScalingKeys[n];
             out.StartArray(true);
             out.Element(scl.mTime);
             Write(out, scl.mValue);
@@ -649,7 +640,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiAnimation &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -670,7 +661,7 @@ void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiCamera &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -697,7 +688,7 @@ void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void WriteFormatInfo(JSONWriter& out) {
+void WriteFormatInfo(JSONWriter &out) {
     out.StartObj();
     out.Key("format");
     out.SimpleValue("\"assimp2json\"");
@@ -706,7 +697,7 @@ void WriteFormatInfo(JSONWriter& out) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiScene& ai) {
+void Write(JSONWriter &out, const aiScene &ai) {
     out.StartObj();
 
     out.Key("__metadata__");
@@ -774,15 +765,14 @@ 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 *) {
     std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
     if (!str) {
-        //throw Assimp::DeadlyExportError("could not open output file");
+        throw DeadlyExportError("could not open output file");
     }
 
     // get a copy of the scene so we can modify it
-    aiScene* scenecopy_tmp;
+    aiScene *scenecopy_tmp;
     aiCopyScene(scene, &scenecopy_tmp);
 
     try {
@@ -795,15 +785,14 @@ void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* sc
         JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
         Write(s, *scenecopy_tmp);
 
-    }
-    catch (...) {
+    } catch (...) {
         aiFreeScene(scenecopy_tmp);
         throw;
     }
     aiFreeScene(scenecopy_tmp);
 }
 
-}
+} // namespace Assimp
 
 #endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 0 - 0
code/Assjson/mesh_splitter.cpp → code/AssetLib/Assjson/mesh_splitter.cpp


+ 0 - 0
code/Assjson/mesh_splitter.h → code/AssetLib/Assjson/mesh_splitter.h


+ 68 - 0
code/AssetLib/Assxml/AssxmlExporter.cpp

@@ -0,0 +1,68 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file  AssxmlExporter.cpp
+ *  ASSXML exporter main code
+ */
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
+
+#include "AssxmlFileWriter.h"
+#include <assimp/IOSystem.hpp>
+#include <assimp/Exporter.hpp>
+
+namespace Assimp   { 
+
+void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
+{
+    DumpSceneToAssxml(
+        pFile,
+        "\0", // command(s)
+        pIOSystem,
+        pScene,
+        false); // shortened?
+}
+
+} // end of namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_ASSXML_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 0 - 0
code/Assxml/AssxmlExporter.h → code/AssetLib/Assxml/AssxmlExporter.h


+ 659 - 0
code/AssetLib/Assxml/AssxmlFileWriter.cpp

@@ -0,0 +1,659 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file AssxmlFileWriter.cpp
+ *  @brief Implementation of Assxml file writer.
+ */
+
+#include "AssxmlFileWriter.h"
+
+#include "PostProcessing/ProcessHelper.h"
+
+#include <assimp/version.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <stdarg.h>
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#include <zlib.h>
+#else
+#include <contrib/zlib/zlib.h>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <memory>
+
+using namespace Assimp;
+
+namespace Assimp {
+
+namespace AssxmlFileWriter {
+
+// -----------------------------------------------------------------------------------
+static int ioprintf(IOStream *io, const char *format, ...) {
+    using namespace std;
+    if (nullptr == io) {
+        return -1;
+    }
+
+    static const int Size = 4096;
+    char sz[Size];
+    ::memset(sz, '\0', Size);
+    va_list va;
+    va_start(va, format);
+    const unsigned int nSize = vsnprintf(sz, Size - 1, format, va);
+    ai_assert(nSize < Size);
+    va_end(va);
+
+    io->Write(sz, sizeof(char), nSize);
+
+    return nSize;
+}
+
+// -----------------------------------------------------------------------------------
+// Convert a name to standard XML format
+static void ConvertName(aiString &out, const aiString &in) {
+    out.length = 0;
+    for (unsigned int i = 0; i < in.length; ++i) {
+        switch (in.data[i]) {
+        case '<':
+            out.Append("&lt;");
+            break;
+        case '>':
+            out.Append("&gt;");
+            break;
+        case '&':
+            out.Append("&amp;");
+            break;
+        case '\"':
+            out.Append("&quot;");
+            break;
+        case '\'':
+            out.Append("&apos;");
+            break;
+        default:
+            out.data[out.length++] = in.data[i];
+        }
+    }
+    out.data[out.length] = 0;
+}
+
+// -----------------------------------------------------------------------------------
+// Write a single node as text dump
+static void WriteNode(const aiNode *node, IOStream *io, unsigned int depth) {
+    char prefix[512];
+    for (unsigned int i = 0; i < depth; ++i)
+        prefix[i] = '\t';
+    prefix[depth] = '\0';
+
+    const aiMatrix4x4 &m = node->mTransformation;
+
+    aiString name;
+    ConvertName(name, node->mName);
+    ioprintf(io, "%s<Node name=\"%s\"> \n"
+                 "%s\t<Matrix4> \n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t</Matrix4> \n",
+            prefix, name.data, prefix,
+            prefix, m.a1, m.a2, m.a3, m.a4,
+            prefix, m.b1, m.b2, m.b3, m.b4,
+            prefix, m.c1, m.c2, m.c3, m.c4,
+            prefix, m.d1, m.d2, m.d3, m.d4, prefix);
+
+    if (node->mNumMeshes) {
+        ioprintf(io, "%s\t<MeshRefs num=\"%u\">\n%s\t",
+                prefix, node->mNumMeshes, prefix);
+
+        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+            ioprintf(io, "%u ", node->mMeshes[i]);
+        }
+        ioprintf(io, "\n%s\t</MeshRefs>\n", prefix);
+    }
+
+    if (node->mNumChildren) {
+        ioprintf(io, "%s\t<NodeList num=\"%u\">\n",
+                prefix, node->mNumChildren);
+
+        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+            WriteNode(node->mChildren[i], io, depth + 2);
+        }
+        ioprintf(io, "%s\t</NodeList>\n", prefix);
+    }
+    ioprintf(io, "%s</Node>\n", prefix);
+}
+
+// -----------------------------------------------------------------------------------
+// Some chuncks 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;
+    buffer.reserve(data.size());
+    for (size_t pos = 0; pos != data.size(); ++pos) {
+        switch (data[pos]) {
+        case '&': buffer.append("&amp;"); break;
+        case '\"': buffer.append("&quot;"); break;
+        case '\'': buffer.append("&apos;"); break;
+        case '<': buffer.append("&lt;"); break;
+        case '>': buffer.append("&gt;"); break;
+        default: buffer.append(&data[pos], 1); break;
+        }
+    }
+    return buffer;
+}
+
+// -----------------------------------------------------------------------------------
+// Write a text model dump
+static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene, IOStream *io, bool shortened) {
+    time_t tt = ::time(NULL);
+#if _WIN32
+    tm *p = gmtime(&tt);
+#else
+    struct tm now;
+    tm *p = gmtime_r(&tt, &now);
+#endif
+    ai_assert(nullptr != p);
+
+    std::string c = cmd;
+    std::string::size_type s;
+
+    // https://sourceforge.net/tracker/?func=detail&aid=3167364&group_id=226462&atid=1067632
+    // -- not allowed in XML comments
+    while ((s = c.find("--")) != std::string::npos) {
+        c[s] = '?';
+    }
+
+    // write header
+    std::string header(
+            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+            "<ASSIMP format_id=\"1\">\n\n"
+            "<!-- XML Model dump produced by assimp dump\n"
+            "  Library version: %u.%u.%u\n"
+            "  Source: %s\n"
+            "  Command line: %s\n"
+            "  %s\n"
+            "-->"
+            " \n\n"
+            "<Scene flags=\"%u\" postprocessing=\"%u\">\n");
+
+    const unsigned int majorVersion(aiGetVersionMajor());
+    const unsigned int minorVersion(aiGetVersionMinor());
+    const unsigned int rev(aiGetVersionRevision());
+    const char *curtime(asctime(p));
+    ioprintf(io, header.c_str(), majorVersion, minorVersion, rev, pFile, c.c_str(), curtime, scene->mFlags, 0u);
+
+    // write the node graph
+    WriteNode(scene->mRootNode, io, 0);
+
+#if 0
+    // write cameras
+    for (unsigned int i = 0; i < scene->mNumCameras;++i) {
+        aiCamera* cam  = scene->mCameras[i];
+        ConvertName(name,cam->mName);
+
+        // camera header
+        ioprintf(io,"\t<Camera parent=\"%s\">\n"
+            "\t\t<Vector3 name=\"up\"        > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"lookat\"    > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"pos\"       > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Float   name=\"fov\"       > %f </Float>\n"
+            "\t\t<Float   name=\"aspect\"    > %f </Float>\n"
+            "\t\t<Float   name=\"near_clip\" > %f </Float>\n"
+            "\t\t<Float   name=\"far_clip\"  > %f </Float>\n"
+            "\t</Camera>\n",
+            name.data,
+            cam->mUp.x,cam->mUp.y,cam->mUp.z,
+            cam->mLookAt.x,cam->mLookAt.y,cam->mLookAt.z,
+            cam->mPosition.x,cam->mPosition.y,cam->mPosition.z,
+            cam->mHorizontalFOV,cam->mAspect,cam->mClipPlaneNear,cam->mClipPlaneFar,i);
+    }
+
+    // write lights
+    for (unsigned int i = 0; i < scene->mNumLights;++i) {
+        aiLight* l  = scene->mLights[i];
+        ConvertName(name,l->mName);
+
+        // light header
+        ioprintf(io,"\t<Light parent=\"%s\"> type=\"%s\"\n"
+            "\t\t<Vector3 name=\"diffuse\"   > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"specular\"  > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"ambient\"   > %0 8f %0 8f %0 8f </Vector3>\n",
+            name.data,
+            (l->mType == aiLightSource_DIRECTIONAL ? "directional" :
+            (l->mType == aiLightSource_POINT ? "point" : "spot" )),
+            l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b,
+            l->mColorSpecular.r,l->mColorSpecular.g,l->mColorSpecular.b,
+            l->mColorAmbient.r, l->mColorAmbient.g, l->mColorAmbient.b);
+
+        if (l->mType != aiLightSource_DIRECTIONAL) {
+            ioprintf(io,
+                "\t\t<Vector3 name=\"pos\"       > %0 8f %0 8f %0 8f </Vector3>\n"
+                "\t\t<Float   name=\"atten_cst\" > %f </Float>\n"
+                "\t\t<Float   name=\"atten_lin\" > %f </Float>\n"
+                "\t\t<Float   name=\"atten_sqr\" > %f </Float>\n",
+                l->mPosition.x,l->mPosition.y,l->mPosition.z,
+                l->mAttenuationConstant,l->mAttenuationLinear,l->mAttenuationQuadratic);
+        }
+
+        if (l->mType != aiLightSource_POINT) {
+            ioprintf(io,
+                "\t\t<Vector3 name=\"lookat\"    > %0 8f %0 8f %0 8f </Vector3>\n",
+                l->mDirection.x,l->mDirection.y,l->mDirection.z);
+        }
+
+        if (l->mType == aiLightSource_SPOT) {
+            ioprintf(io,
+                "\t\t<Float   name=\"cone_out\" > %f </Float>\n"
+                "\t\t<Float   name=\"cone_inn\" > %f </Float>\n",
+                l->mAngleOuterCone,l->mAngleInnerCone);
+        }
+        ioprintf(io,"\t</Light>\n");
+    }
+#endif
+    aiString name;
+
+    // write textures
+    if (scene->mNumTextures) {
+        ioprintf(io, "<TextureList num=\"%u\">\n", scene->mNumTextures);
+        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
+            aiTexture *tex = scene->mTextures[i];
+            bool compressed = (tex->mHeight == 0);
+
+            // mesh header
+            ioprintf(io, "\t<Texture width=\"%u\" height=\"%u\" compressed=\"%s\"> \n",
+                    (compressed ? -1 : tex->mWidth), (compressed ? -1 : tex->mHeight),
+                    (compressed ? "true" : "false"));
+
+            if (compressed) {
+                ioprintf(io, "\t\t<Data length=\"%u\"> \n", tex->mWidth);
+
+                if (!shortened) {
+                    for (unsigned int n = 0; n < tex->mWidth; ++n) {
+                        ioprintf(io, "\t\t\t%2x", reinterpret_cast<uint8_t *>(tex->pcData)[n]);
+                        if (n && !(n % 50)) {
+                            ioprintf(io, "\n");
+                        }
+                    }
+                }
+            } else if (!shortened) {
+                ioprintf(io, "\t\t<Data length=\"%u\"> \n", tex->mWidth * tex->mHeight * 4);
+
+                // const unsigned int width = (unsigned int)std::log10((double)std::max(tex->mHeight,tex->mWidth))+1;
+                for (unsigned int y = 0; y < tex->mHeight; ++y) {
+                    for (unsigned int x = 0; x < tex->mWidth; ++x) {
+                        aiTexel *tx = tex->pcData + y * tex->mWidth + x;
+                        unsigned int r = tx->r, g = tx->g, b = tx->b, a = tx->a;
+                        ioprintf(io, "\t\t\t%2x %2x %2x %2x", r, g, b, a);
+
+                        // group by four for readability
+                        if (0 == (x + y * tex->mWidth) % 4) {
+                            ioprintf(io, "\n");
+                        }
+                    }
+                }
+            }
+            ioprintf(io, "\t\t</Data>\n\t</Texture>\n");
+        }
+        ioprintf(io, "</TextureList>\n");
+    }
+
+    // write materials
+    if (scene->mNumMaterials) {
+        ioprintf(io, "<MaterialList num=\"%u\">\n", scene->mNumMaterials);
+        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+            const aiMaterial *mat = scene->mMaterials[i];
+
+            ioprintf(io, "\t<Material>\n");
+            ioprintf(io, "\t\t<MatPropertyList  num=\"%u\">\n", mat->mNumProperties);
+            for (unsigned int n = 0; n < mat->mNumProperties; ++n) {
+
+                const aiMaterialProperty *prop = mat->mProperties[n];
+                const char *sz = "";
+                if (prop->mType == aiPTI_Float) {
+                    sz = "float";
+                } else if (prop->mType == aiPTI_Integer) {
+                    sz = "integer";
+                } else if (prop->mType == aiPTI_String) {
+                    sz = "string";
+                } else if (prop->mType == aiPTI_Buffer) {
+                    sz = "binary_buffer";
+                }
+
+                ioprintf(io, "\t\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%u\"",
+                        prop->mKey.data, sz,
+                        ::TextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex);
+
+                if (prop->mType == aiPTI_Float) {
+                    ioprintf(io, " size=\"%i\">\n\t\t\t\t",
+                            static_cast<int>(prop->mDataLength / sizeof(float)));
+
+                    for (unsigned int pp = 0; pp < prop->mDataLength / sizeof(float); ++pp) {
+                        ioprintf(io, "%f ", *((float *)(prop->mData + pp * sizeof(float))));
+                    }
+                } else if (prop->mType == aiPTI_Integer) {
+                    ioprintf(io, " size=\"%i\">\n\t\t\t\t",
+                            static_cast<int>(prop->mDataLength / sizeof(int)));
+
+                    for (unsigned int pp = 0; pp < prop->mDataLength / sizeof(int); ++pp) {
+                        ioprintf(io, "%i ", *((int *)(prop->mData + pp * sizeof(int))));
+                    }
+                } else if (prop->mType == aiPTI_Buffer) {
+                    ioprintf(io, " size=\"%i\">\n\t\t\t\t",
+                            static_cast<int>(prop->mDataLength));
+
+                    for (unsigned int pp = 0; pp < prop->mDataLength; ++pp) {
+                        ioprintf(io, "%2x ", prop->mData[pp]);
+                        if (pp && 0 == pp % 30) {
+                            ioprintf(io, "\n\t\t\t\t");
+                        }
+                    }
+                } else if (prop->mType == aiPTI_String) {
+                    ioprintf(io, ">\n\t\t\t\t\"%s\"", encodeXML(prop->mData + 4).c_str() /* skip length */);
+                }
+                ioprintf(io, "\n\t\t\t</MatProperty>\n");
+            }
+            ioprintf(io, "\t\t</MatPropertyList>\n");
+            ioprintf(io, "\t</Material>\n");
+        }
+        ioprintf(io, "</MaterialList>\n");
+    }
+
+    // write animations
+    if (scene->mNumAnimations) {
+        ioprintf(io, "<AnimationList num=\"%u\">\n", scene->mNumAnimations);
+        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
+            aiAnimation *anim = scene->mAnimations[i];
+
+            // anim header
+            ConvertName(name, anim->mName);
+            ioprintf(io, "\t<Animation name=\"%s\" duration=\"%e\" tick_cnt=\"%e\">\n",
+                    name.data, anim->mDuration, anim->mTicksPerSecond);
+
+            // write bone animation channels
+            if (anim->mNumChannels) {
+                ioprintf(io, "\t\t<NodeAnimList num=\"%u\">\n", anim->mNumChannels);
+                for (unsigned int n = 0; n < anim->mNumChannels; ++n) {
+                    aiNodeAnim *nd = anim->mChannels[n];
+
+                    // node anim header
+                    ConvertName(name, nd->mNodeName);
+                    ioprintf(io, "\t\t\t<NodeAnim node=\"%s\">\n", name.data);
+
+                    if (!shortened) {
+                        // write position keys
+                        if (nd->mNumPositionKeys) {
+                            ioprintf(io, "\t\t\t\t<PositionKeyList num=\"%u\">\n", nd->mNumPositionKeys);
+                            for (unsigned int a = 0; a < nd->mNumPositionKeys; ++a) {
+                                aiVectorKey *vc = nd->mPositionKeys + a;
+                                ioprintf(io, "\t\t\t\t\t<PositionKey time=\"%e\">\n"
+                                             "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</PositionKey>\n",
+                                        vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z);
+                            }
+                            ioprintf(io, "\t\t\t\t</PositionKeyList>\n");
+                        }
+
+                        // write scaling keys
+                        if (nd->mNumScalingKeys) {
+                            ioprintf(io, "\t\t\t\t<ScalingKeyList num=\"%u\">\n", nd->mNumScalingKeys);
+                            for (unsigned int a = 0; a < nd->mNumScalingKeys; ++a) {
+                                aiVectorKey *vc = nd->mScalingKeys + a;
+                                ioprintf(io, "\t\t\t\t\t<ScalingKey time=\"%e\">\n"
+                                             "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</ScalingKey>\n",
+                                        vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z);
+                            }
+                            ioprintf(io, "\t\t\t\t</ScalingKeyList>\n");
+                        }
+
+                        // write rotation keys
+                        if (nd->mNumRotationKeys) {
+                            ioprintf(io, "\t\t\t\t<RotationKeyList num=\"%u\">\n", nd->mNumRotationKeys);
+                            for (unsigned int a = 0; a < nd->mNumRotationKeys; ++a) {
+                                aiQuatKey *vc = nd->mRotationKeys + a;
+                                ioprintf(io, "\t\t\t\t\t<RotationKey time=\"%e\">\n"
+                                             "\t\t\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t\t\t</RotationKey>\n",
+                                        vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z, vc->mValue.w);
+                            }
+                            ioprintf(io, "\t\t\t\t</RotationKeyList>\n");
+                        }
+                    }
+                    ioprintf(io, "\t\t\t</NodeAnim>\n");
+                }
+                ioprintf(io, "\t\t</NodeAnimList>\n");
+            }
+            ioprintf(io, "\t</Animation>\n");
+        }
+        ioprintf(io, "</AnimationList>\n");
+    }
+
+    // write meshes
+    if (scene->mNumMeshes) {
+        ioprintf(io, "<MeshList num=\"%u\">\n", scene->mNumMeshes);
+        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+            aiMesh *mesh = scene->mMeshes[i];
+            // const unsigned int width = (unsigned int)std::log10((double)mesh->mNumVertices)+1;
+
+            // mesh header
+            ioprintf(io, "\t<Mesh types=\"%s %s %s %s\" material_index=\"%u\">\n",
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_POINT ? "points" : ""),
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_LINE ? "lines" : ""),
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE ? "triangles" : ""),
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON ? "polygons" : ""),
+                    mesh->mMaterialIndex);
+
+            // bones
+            if (mesh->mNumBones) {
+                ioprintf(io, "\t\t<BoneList num=\"%u\">\n", mesh->mNumBones);
+
+                for (unsigned int n = 0; n < mesh->mNumBones; ++n) {
+                    aiBone *bone = mesh->mBones[n];
+
+                    ConvertName(name, bone->mName);
+                    // bone header
+                    ioprintf(io, "\t\t\t<Bone name=\"%s\">\n"
+                                 "\t\t\t\t<Matrix4> \n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t</Matrix4> \n",
+                            name.data,
+                            bone->mOffsetMatrix.a1, bone->mOffsetMatrix.a2, bone->mOffsetMatrix.a3, bone->mOffsetMatrix.a4,
+                            bone->mOffsetMatrix.b1, bone->mOffsetMatrix.b2, bone->mOffsetMatrix.b3, bone->mOffsetMatrix.b4,
+                            bone->mOffsetMatrix.c1, bone->mOffsetMatrix.c2, bone->mOffsetMatrix.c3, bone->mOffsetMatrix.c4,
+                            bone->mOffsetMatrix.d1, bone->mOffsetMatrix.d2, bone->mOffsetMatrix.d3, bone->mOffsetMatrix.d4);
+
+                    if (!shortened && bone->mNumWeights) {
+                        ioprintf(io, "\t\t\t\t<WeightList num=\"%u\">\n", bone->mNumWeights);
+
+                        // bone weights
+                        for (unsigned int a = 0; a < bone->mNumWeights; ++a) {
+                            aiVertexWeight *wght = bone->mWeights + a;
+
+                            ioprintf(io, "\t\t\t\t\t<Weight index=\"%u\">\n\t\t\t\t\t\t%f\n\t\t\t\t\t</Weight>\n",
+                                    wght->mVertexId, wght->mWeight);
+                        }
+                        ioprintf(io, "\t\t\t\t</WeightList>\n");
+                    }
+                    ioprintf(io, "\t\t\t</Bone>\n");
+                }
+                ioprintf(io, "\t\t</BoneList>\n");
+            }
+
+            // faces
+            if (!shortened && mesh->mNumFaces) {
+                ioprintf(io, "\t\t<FaceList num=\"%u\">\n", mesh->mNumFaces);
+                for (unsigned int n = 0; n < mesh->mNumFaces; ++n) {
+                    aiFace &f = mesh->mFaces[n];
+                    ioprintf(io, "\t\t\t<Face num=\"%u\">\n"
+                                 "\t\t\t\t",
+                            f.mNumIndices);
+
+                    for (unsigned int j = 0; j < f.mNumIndices; ++j)
+                        ioprintf(io, "%u ", f.mIndices[j]);
+
+                    ioprintf(io, "\n\t\t\t</Face>\n");
+                }
+                ioprintf(io, "\t\t</FaceList>\n");
+            }
+
+            // vertex positions
+            if (mesh->HasPositions()) {
+                ioprintf(io, "\t\t<Positions num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mVertices[n].x,
+                                mesh->mVertices[n].y,
+                                mesh->mVertices[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Positions>\n");
+            }
+
+            // vertex normals
+            if (mesh->HasNormals()) {
+                ioprintf(io, "\t\t<Normals num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mNormals[n].x,
+                                mesh->mNormals[n].y,
+                                mesh->mNormals[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Normals>\n");
+            }
+
+            // vertex tangents and bitangents
+            if (mesh->HasTangentsAndBitangents()) {
+                ioprintf(io, "\t\t<Tangents num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mTangents[n].x,
+                                mesh->mTangents[n].y,
+                                mesh->mTangents[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Tangents>\n");
+
+                ioprintf(io, "\t\t<Bitangents num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mBitangents[n].x,
+                                mesh->mBitangents[n].y,
+                                mesh->mBitangents[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Bitangents>\n");
+            }
+
+            // texture coordinates
+            for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+                if (!mesh->mTextureCoords[a])
+                    break;
+
+                ioprintf(io, "\t\t<TextureCoords num=\"%u\" set=\"%u\" num_components=\"%u\"> \n", mesh->mNumVertices,
+                        a, mesh->mNumUVComponents[a]);
+
+                if (!shortened) {
+                    if (mesh->mNumUVComponents[a] == 3) {
+                        for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                            ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                    mesh->mTextureCoords[a][n].x,
+                                    mesh->mTextureCoords[a][n].y,
+                                    mesh->mTextureCoords[a][n].z);
+                        }
+                    } else {
+                        for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                            ioprintf(io, "\t\t%0 8f %0 8f\n",
+                                    mesh->mTextureCoords[a][n].x,
+                                    mesh->mTextureCoords[a][n].y);
+                        }
+                    }
+                }
+                ioprintf(io, "\t\t</TextureCoords>\n");
+            }
+
+            // vertex colors
+            for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+                if (!mesh->mColors[a])
+                    break;
+                ioprintf(io, "\t\t<Colors num=\"%u\" set=\"%u\" num_components=\"4\"> \n", mesh->mNumVertices, a);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f %0 8f\n",
+                                mesh->mColors[a][n].r,
+                                mesh->mColors[a][n].g,
+                                mesh->mColors[a][n].b,
+                                mesh->mColors[a][n].a);
+                    }
+                }
+                ioprintf(io, "\t\t</Colors>\n");
+            }
+            ioprintf(io, "\t</Mesh>\n");
+        }
+        ioprintf(io, "</MeshList>\n");
+    }
+    ioprintf(io, "</Scene>\n</ASSIMP>");
+}
+
+} // end of namespace AssxmlFileWriter
+
+void DumpSceneToAssxml(
+        const char *pFile, const char *cmd, IOSystem *pIOSystem,
+        const aiScene *pScene, bool shortened) {
+    std::unique_ptr<IOStream> file(pIOSystem->Open(pFile, "wt"));
+    if (!file.get()) {
+        throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n');
+    }
+
+    AssxmlFileWriter::WriteDump(pFile, cmd, pScene, file.get(), shortened);
+}
+
+} // end of namespace Assimp

+ 65 - 0
code/AssetLib/Assxml/AssxmlFileWriter.h

@@ -0,0 +1,65 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file AssxmlFileWriter.h
+ *  @brief Declaration of Assxml file writer.
+ */
+
+#ifndef AI_ASSXMLFILEWRITER_H_INC
+#define AI_ASSXMLFILEWRITER_H_INC
+
+#include <assimp/defs.h>
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp {
+
+void ASSIMP_API DumpSceneToAssxml(
+    const char* pFile,
+    const char* cmd,
+    IOSystem* pIOSystem,
+    const aiScene* pScene,
+    bool shortened);
+
+}
+
+#endif // AI_ASSXMLFILEWRITER_H_INC

+ 744 - 0
code/AssetLib/B3D/B3DImporter.cpp

@@ -0,0 +1,744 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  B3DImporter.cpp
+ *  @brief Implementation of the b3d importer class
+ */
+
+#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
+
+// internal headers
+#include "AssetLib/B3D/B3DImporter.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+#include "PostProcessing/TextureTransform.h"
+
+#include <assimp/StringUtils.h>
+#include <assimp/anim.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <memory>
+
+using namespace Assimp;
+using namespace std;
+
+static const aiImporterDesc desc = {
+    "BlitzBasic 3D Importer",
+    "",
+    "",
+    "http://www.blitzbasic.com/",
+    aiImporterFlags_SupportBinaryFlavour,
+    0,
+    0,
+    0,
+    0,
+    "b3d"
+};
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4018)
+#endif
+
+//#define DEBUG_B3D
+
+template <typename T>
+void DeleteAllBarePointers(std::vector<T> &x) {
+    for (auto p : x) {
+        delete p;
+    }
+}
+
+B3DImporter::~B3DImporter() {
+}
+
+// ------------------------------------------------------------------------------------------------
+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;
+    }
+
+    string ext = pFile.substr(pos + 1);
+    if (ext.size() != 3) {
+        return false;
+    }
+
+    return (ext[0] == 'b' || ext[0] == 'B') && (ext[1] == '3') && (ext[2] == 'd' || ext[2] == 'D');
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc *B3DImporter::GetInfo() const {
+    return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+
+    // Check whether we can read from the file
+    if (file.get() == nullptr) {
+        throw DeadlyImportError("Failed to open B3D file " + pFile + ".");
+    }
+
+    // check whether the .b3d file is large enough to contain
+    // at least one chunk.
+    size_t fileSize = file->FileSize();
+    if (fileSize < 8) {
+        throw DeadlyImportError("B3D File is too small.");
+    }
+
+    _pos = 0;
+    _buf.resize(fileSize);
+    file->Read(&_buf[0], 1, fileSize);
+    _stack.clear();
+
+    ReadBB3D(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+AI_WONT_RETURN void B3DImporter::Oops() {
+    throw DeadlyImportError("B3D Importer - INTERNAL ERROR");
+}
+
+// ------------------------------------------------------------------------------------------------
+AI_WONT_RETURN void B3DImporter::Fail(string str) {
+#ifdef DEBUG_B3D
+    ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
+#endif
+    throw DeadlyImportError("B3D Importer - error in B3D file data: " + str);
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadByte() {
+    if (_pos > _buf.size()) {
+        Fail("EOF");
+    }
+
+    return _buf[_pos++];
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadInt() {
+    if (_pos + 4 > _buf.size()) {
+        Fail("EOF");
+    }
+
+    int n;
+    memcpy(&n, &_buf[_pos], 4);
+    _pos += 4;
+
+    return n;
+}
+
+// ------------------------------------------------------------------------------------------------
+float B3DImporter::ReadFloat() {
+    if (_pos + 4 > _buf.size()) {
+        Fail("EOF");
+    }
+
+    float n;
+    memcpy(&n, &_buf[_pos], 4);
+    _pos += 4;
+
+    return n;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector2D B3DImporter::ReadVec2() {
+    float x = ReadFloat();
+    float y = ReadFloat();
+    return aiVector2D(x, y);
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector3D B3DImporter::ReadVec3() {
+    float x = ReadFloat();
+    float y = ReadFloat();
+    float z = ReadFloat();
+    return aiVector3D(x, y, z);
+}
+
+// ------------------------------------------------------------------------------------------------
+aiQuaternion B3DImporter::ReadQuat() {
+    // (aramis_acg) Fix to adapt the loader to changed quat orientation
+    float w = -ReadFloat();
+    float x = ReadFloat();
+    float y = ReadFloat();
+    float z = ReadFloat();
+    return aiQuaternion(w, x, y, z);
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadString() {
+    if (_pos > _buf.size()) {
+        Fail("EOF");
+    }
+    string str;
+    while (_pos < _buf.size()) {
+        char c = (char)ReadByte();
+        if (!c) {
+            return str;
+        }
+        str += c;
+    }
+    return string();
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadChunk() {
+    string tag;
+    for (int i = 0; i < 4; ++i) {
+        tag += char(ReadByte());
+    }
+#ifdef DEBUG_B3D
+    ASSIMP_LOG_DEBUG_F("ReadChunk: ", tag);
+#endif
+    unsigned sz = (unsigned)ReadInt();
+    _stack.push_back(_pos + sz);
+    return tag;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ExitChunk() {
+    _pos = _stack.back();
+    _stack.pop_back();
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t B3DImporter::ChunkSize() {
+    return _stack.back() - _pos;
+}
+// ------------------------------------------------------------------------------------------------
+
+template <class T>
+T *B3DImporter::to_array(const vector<T> &v) {
+    if (v.empty()) {
+        return 0;
+    }
+    T *p = new T[v.size()];
+    for (size_t i = 0; i < v.size(); ++i) {
+        p[i] = v[i];
+    }
+    return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+T **unique_to_array(vector<std::unique_ptr<T>> &v) {
+    if (v.empty()) {
+        return 0;
+    }
+    T **p = new T *[v.size()];
+    for (size_t i = 0; i < v.size(); ++i) {
+        p[i] = v[i].release();
+    }
+    return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTEXS() {
+    while (ChunkSize()) {
+        string name = ReadString();
+        /*int flags=*/ReadInt();
+        /*int blend=*/ReadInt();
+        /*aiVector2D pos=*/ReadVec2();
+        /*aiVector2D scale=*/ReadVec2();
+        /*float rot=*/ReadFloat();
+
+        _textures.push_back(name);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBRUS() {
+    int n_texs = ReadInt();
+    if (n_texs < 0 || n_texs > 8) {
+        Fail("Bad texture count");
+    }
+    while (ChunkSize()) {
+        string name = ReadString();
+        aiVector3D color = ReadVec3();
+        float alpha = ReadFloat();
+        float shiny = ReadFloat();
+        /*int blend=**/ ReadInt();
+        int fx = ReadInt();
+
+        std::unique_ptr<aiMaterial> mat(new aiMaterial);
+
+        // Name
+        aiString ainame(name);
+        mat->AddProperty(&ainame, AI_MATKEY_NAME);
+
+        // Diffuse color
+        mat->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE);
+
+        // Opacity
+        mat->AddProperty(&alpha, 1, AI_MATKEY_OPACITY);
+
+        // Specular color
+        aiColor3D speccolor(shiny, shiny, shiny);
+        mat->AddProperty(&speccolor, 1, AI_MATKEY_COLOR_SPECULAR);
+
+        // Specular power
+        float specpow = shiny * 128;
+        mat->AddProperty(&specpow, 1, AI_MATKEY_SHININESS);
+
+        // Double sided
+        if (fx & 0x10) {
+            int i = 1;
+            mat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
+        }
+
+        //Textures
+        for (int i = 0; i < n_texs; ++i) {
+            int texid = ReadInt();
+            if (texid < -1 || (texid >= 0 && texid >= static_cast<int>(_textures.size()))) {
+                Fail("Bad texture id");
+            }
+            if (i == 0 && texid >= 0) {
+                aiString texname(_textures[texid]);
+                mat->AddProperty(&texname, AI_MATKEY_TEXTURE_DIFFUSE(0));
+            }
+        }
+        _materials.emplace_back(std::move(mat));
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadVRTS() {
+    _vflags = ReadInt();
+    _tcsets = ReadInt();
+    _tcsize = ReadInt();
+    if (_tcsets < 0 || _tcsets > 4 || _tcsize < 0 || _tcsize > 4) {
+        Fail("Bad texcoord data");
+    }
+
+    int sz = 12 + (_vflags & 1 ? 12 : 0) + (_vflags & 2 ? 16 : 0) + (_tcsets * _tcsize * 4);
+    size_t n_verts = ChunkSize() / sz;
+
+    int v0 = static_cast<int>(_vertices.size());
+    _vertices.resize(v0 + n_verts);
+
+    for (unsigned int i = 0; i < n_verts; ++i) {
+        Vertex &v = _vertices[v0 + i];
+
+        memset(v.bones, 0, sizeof(v.bones));
+        memset(v.weights, 0, sizeof(v.weights));
+
+        v.vertex = ReadVec3();
+
+        if (_vflags & 1) {
+            v.normal = ReadVec3();
+        }
+
+        if (_vflags & 2) {
+            ReadQuat(); //skip v 4bytes...
+        }
+
+        for (int j = 0; j < _tcsets; ++j) {
+            float t[4] = { 0, 0, 0, 0 };
+            for (int k = 0; k < _tcsize; ++k) {
+                t[k] = ReadFloat();
+            }
+            t[1] = 1 - t[1];
+            if (!j) {
+                v.texcoords = aiVector3D(t[0], t[1], t[2]);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTRIS(int v0) {
+    int matid = ReadInt();
+    if (matid == -1) {
+        matid = 0;
+    } else if (matid < 0 || matid >= (int)_materials.size()) {
+#ifdef DEBUG_B3D
+        ASSIMP_LOG_ERROR_F("material id=", matid);
+#endif
+        Fail("Bad material id");
+    }
+
+    std::unique_ptr<aiMesh> mesh(new aiMesh);
+
+    mesh->mMaterialIndex = matid;
+    mesh->mNumFaces = 0;
+    mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+    size_t n_tris = ChunkSize() / 12;
+    aiFace *face = mesh->mFaces = new aiFace[n_tris];
+
+    for (unsigned int i = 0; i < n_tris; ++i) {
+        int i0 = ReadInt() + v0;
+        int i1 = ReadInt() + v0;
+        int i2 = ReadInt() + v0;
+        if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
+#ifdef DEBUG_B3D
+            ASSIMP_LOG_ERROR_F("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
+#endif
+            Fail("Bad triangle index");
+            continue;
+        }
+        face->mNumIndices = 3;
+        face->mIndices = new unsigned[3];
+        face->mIndices[0] = i0;
+        face->mIndices[1] = i1;
+        face->mIndices[2] = i2;
+        ++mesh->mNumFaces;
+        ++face;
+    }
+
+    _meshes.emplace_back(std::move(mesh));
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadMESH() {
+    /*int matid=*/ReadInt();
+
+    int v0 = static_cast<int>(_vertices.size());
+
+    while (ChunkSize()) {
+        string t = ReadChunk();
+        if (t == "VRTS") {
+            ReadVRTS();
+        } else if (t == "TRIS") {
+            ReadTRIS(v0);
+        }
+        ExitChunk();
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBONE(int id) {
+    while (ChunkSize()) {
+        int vertex = ReadInt();
+        float weight = ReadFloat();
+        if (vertex < 0 || vertex >= (int)_vertices.size()) {
+            Fail("Bad vertex index");
+        }
+
+        Vertex &v = _vertices[vertex];
+        for (int i = 0; i < 4; ++i) {
+            if (!v.weights[i]) {
+                v.bones[i] = static_cast<unsigned char>(id);
+                v.weights[i] = weight;
+                break;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadKEYS(aiNodeAnim *nodeAnim) {
+    vector<aiVectorKey> trans, scale;
+    vector<aiQuatKey> rot;
+    int flags = ReadInt();
+    while (ChunkSize()) {
+        int frame = ReadInt();
+        if (flags & 1) {
+            trans.push_back(aiVectorKey(frame, ReadVec3()));
+        }
+        if (flags & 2) {
+            scale.push_back(aiVectorKey(frame, ReadVec3()));
+        }
+        if (flags & 4) {
+            rot.push_back(aiQuatKey(frame, ReadQuat()));
+        }
+    }
+
+    if (flags & 1) {
+        nodeAnim->mNumPositionKeys = static_cast<unsigned int>(trans.size());
+        nodeAnim->mPositionKeys = to_array(trans);
+    }
+
+    if (flags & 2) {
+        nodeAnim->mNumScalingKeys = static_cast<unsigned int>(scale.size());
+        nodeAnim->mScalingKeys = to_array(scale);
+    }
+
+    if (flags & 4) {
+        nodeAnim->mNumRotationKeys = static_cast<unsigned int>(rot.size());
+        nodeAnim->mRotationKeys = to_array(rot);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadANIM() {
+    /*int flags=*/ReadInt();
+    int frames = ReadInt();
+    float fps = ReadFloat();
+
+    std::unique_ptr<aiAnimation> anim(new aiAnimation);
+
+    anim->mDuration = frames;
+    anim->mTicksPerSecond = fps;
+    _animations.emplace_back(std::move(anim));
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode *B3DImporter::ReadNODE(aiNode *parent) {
+
+    string name = ReadString();
+    aiVector3D t = ReadVec3();
+    aiVector3D s = ReadVec3();
+    aiQuaternion r = ReadQuat();
+
+    aiMatrix4x4 trans, scale, rot;
+
+    aiMatrix4x4::Translation(t, trans);
+    aiMatrix4x4::Scaling(s, scale);
+    rot = aiMatrix4x4(r.GetMatrix());
+
+    aiMatrix4x4 tform = trans * rot * scale;
+
+    int nodeid = static_cast<int>(_nodes.size());
+
+    aiNode *node = new aiNode(name);
+    _nodes.push_back(node);
+
+    node->mParent = parent;
+    node->mTransformation = tform;
+
+    std::unique_ptr<aiNodeAnim> nodeAnim;
+    vector<unsigned> meshes;
+    vector<aiNode *> children;
+
+    while (ChunkSize()) {
+        const string chunk = ReadChunk();
+        if (chunk == "MESH") {
+            unsigned int n = static_cast<unsigned int>(_meshes.size());
+            ReadMESH();
+            for (unsigned int i = n; i < static_cast<unsigned int>(_meshes.size()); ++i) {
+                meshes.push_back(i);
+            }
+        } else if (chunk == "BONE") {
+            ReadBONE(nodeid);
+        } else if (chunk == "ANIM") {
+            ReadANIM();
+        } else if (chunk == "KEYS") {
+            if (!nodeAnim) {
+                nodeAnim.reset(new aiNodeAnim);
+                nodeAnim->mNodeName = node->mName;
+            }
+            ReadKEYS(nodeAnim.get());
+        } else if (chunk == "NODE") {
+            aiNode *child = ReadNODE(node);
+            children.push_back(child);
+        }
+        ExitChunk();
+    }
+
+    if (nodeAnim) {
+        _nodeAnims.emplace_back(std::move(nodeAnim));
+    }
+
+    node->mNumMeshes = static_cast<unsigned int>(meshes.size());
+    node->mMeshes = to_array(meshes);
+
+    node->mNumChildren = static_cast<unsigned int>(children.size());
+    node->mChildren = to_array(children);
+
+    return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBB3D(aiScene *scene) {
+
+    _textures.clear();
+
+    _materials.clear();
+
+    _vertices.clear();
+
+    _meshes.clear();
+
+    DeleteAllBarePointers(_nodes);
+    _nodes.clear();
+
+    _nodeAnims.clear();
+
+    _animations.clear();
+
+    string t = ReadChunk();
+    if (t == "BB3D") {
+        int version = ReadInt();
+
+        if (!DefaultLogger::isNullLogger()) {
+            char dmp[128];
+            ai_snprintf(dmp, 128, "B3D file format version: %i", version);
+            ASSIMP_LOG_INFO(dmp);
+        }
+
+        while (ChunkSize()) {
+            const string chunk = ReadChunk();
+            if (chunk == "TEXS") {
+                ReadTEXS();
+            } else if (chunk == "BRUS") {
+                ReadBRUS();
+            } else if (chunk == "NODE") {
+                ReadNODE(0);
+            }
+            ExitChunk();
+        }
+    }
+    ExitChunk();
+
+    if (!_nodes.size()) {
+        Fail("No nodes");
+    }
+
+    if (!_meshes.size()) {
+        Fail("No meshes");
+    }
+
+    // Fix nodes/meshes/bones
+    for (size_t i = 0; i < _nodes.size(); ++i) {
+        aiNode *node = _nodes[i];
+
+        for (size_t j = 0; j < node->mNumMeshes; ++j) {
+            aiMesh *mesh = _meshes[node->mMeshes[j]].get();
+
+            int n_tris = mesh->mNumFaces;
+            int n_verts = mesh->mNumVertices = n_tris * 3;
+
+            aiVector3D *mv = mesh->mVertices = new aiVector3D[n_verts], *mn = 0, *mc = 0;
+            if (_vflags & 1) {
+                mn = mesh->mNormals = new aiVector3D[n_verts];
+            }
+            if (_tcsets) {
+                mc = mesh->mTextureCoords[0] = new aiVector3D[n_verts];
+            }
+
+            aiFace *face = mesh->mFaces;
+
+            vector<vector<aiVertexWeight>> vweights(_nodes.size());
+
+            for (int vertIdx = 0; vertIdx < n_verts; vertIdx += 3) {
+                for (int faceIndex = 0; faceIndex < 3; ++faceIndex) {
+                    Vertex &v = _vertices[face->mIndices[faceIndex]];
+
+                    *mv++ = v.vertex;
+                    if (mn) *mn++ = v.normal;
+                    if (mc) *mc++ = v.texcoords;
+
+                    face->mIndices[faceIndex] = vertIdx + faceIndex;
+
+                    for (int k = 0; k < 4; ++k) {
+                        if (!v.weights[k])
+                            break;
+
+                        int bone = v.bones[k];
+                        float weight = v.weights[k];
+
+                        vweights[bone].push_back(aiVertexWeight(vertIdx + faceIndex, weight));
+                    }
+                }
+                ++face;
+            }
+
+            vector<aiBone *> bones;
+            for (size_t weightIndx = 0; weightIndx < vweights.size(); ++weightIndx) {
+                vector<aiVertexWeight> &weights = vweights[weightIndx];
+                if (!weights.size()) {
+                    continue;
+                }
+
+                aiBone *bone = new aiBone;
+                bones.push_back(bone);
+
+                aiNode *bnode = _nodes[weightIndx];
+
+                bone->mName = bnode->mName;
+                bone->mNumWeights = static_cast<unsigned int>(weights.size());
+                bone->mWeights = to_array(weights);
+
+                aiMatrix4x4 mat = bnode->mTransformation;
+                while (bnode->mParent) {
+                    bnode = bnode->mParent;
+                    mat = bnode->mTransformation * mat;
+                }
+                bone->mOffsetMatrix = mat.Inverse();
+            }
+            mesh->mNumBones = static_cast<unsigned int>(bones.size());
+            mesh->mBones = to_array(bones);
+        }
+    }
+
+    //nodes
+    scene->mRootNode = _nodes[0];
+    _nodes.clear(); // node ownership now belongs to scene
+
+    //material
+    if (!_materials.size()) {
+        _materials.emplace_back(std::unique_ptr<aiMaterial>(new aiMaterial));
+    }
+    scene->mNumMaterials = static_cast<unsigned int>(_materials.size());
+    scene->mMaterials = unique_to_array(_materials);
+
+    //meshes
+    scene->mNumMeshes = static_cast<unsigned int>(_meshes.size());
+    scene->mMeshes = unique_to_array(_meshes);
+
+    //animations
+    if (_animations.size() == 1 && _nodeAnims.size()) {
+
+        aiAnimation *anim = _animations.back().get();
+        anim->mNumChannels = static_cast<unsigned int>(_nodeAnims.size());
+        anim->mChannels = unique_to_array(_nodeAnims);
+
+        scene->mNumAnimations = static_cast<unsigned int>(_animations.size());
+        scene->mAnimations = unique_to_array(_animations);
+    }
+
+    // convert to RH
+    MakeLeftHandedProcess makeleft;
+    makeleft.Execute(scene);
+
+    FlipWindingOrderProcess flip;
+    flip.Execute(scene);
+}
+
+#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER

+ 3 - 3
code/B3D/B3DImporter.h → code/AssetLib/B3D/B3DImporter.h

@@ -82,7 +82,7 @@ private:
     std::string ReadString();
     std::string ReadChunk();
     void ExitChunk();
-    unsigned ChunkSize();
+    size_t ChunkSize();
 
     template<class T>
     T *to_array( const std::vector<T> &v );
@@ -112,10 +112,10 @@ private:
 
     void ReadBB3D( aiScene *scene );
 
-    unsigned _pos;
+    size_t _pos;
 //  unsigned _size;
     std::vector<unsigned char> _buf;
-    std::vector<unsigned> _stack;
+    std::vector<size_t> _stack;
 
     std::vector<std::string> _textures;
     std::vector<std::unique_ptr<aiMaterial> > _materials;

+ 205 - 247
code/BVH/BVHLoader.cpp → code/AssetLib/BVH/BVHLoader.cpp

@@ -42,19 +42,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-
 #ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
 
 #include "BVHLoader.h"
-#include <assimp/fast_atof.h>
 #include <assimp/SkeletonMeshBuilder.h>
-#include <assimp/Importer.hpp>
-#include <memory>
 #include <assimp/TinyFormatter.h>
-#include <assimp/IOSystem.hpp>
-#include <assimp/scene.h>
+#include <assimp/fast_atof.h>
 #include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/Importer.hpp>
 #include <map>
+#include <memory>
 
 using namespace Assimp;
 using namespace Assimp::Formatter;
@@ -74,104 +73,98 @@ static const aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-BVHLoader::BVHLoader()
-    : mLine(),
-    mAnimTickDuration(),
-    mAnimNumFrames(),
-    noSkeletonMesh()
-{}
+BVHLoader::BVHLoader() :
+        mLine(),
+        mAnimTickDuration(),
+        mAnimNumFrames(),
+        noSkeletonMesh() {}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-BVHLoader::~BVHLoader()
-{}
+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
-{
+bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const {
     // check file extension
     const std::string extension = GetExtension(pFile);
 
-    if( extension == "bvh")
+    if (extension == "bvh")
         return true;
 
     if ((!extension.length() || cs) && pIOHandler) {
-        const char* tokens[] = {"HIERARCHY"};
-        return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+        const char *tokens[] = { "HIERARCHY" };
+        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
     }
     return false;
 }
 
 // ------------------------------------------------------------------------------------------------
-void BVHLoader::SetupProperties(const Importer* pImp)
-{
-    noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
+void BVHLoader::SetupProperties(const Importer *pImp) {
+    noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Loader meta information
-const aiImporterDesc* BVHLoader::GetInfo () const
-{
+const aiImporterDesc *BVHLoader::GetInfo() const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
-{
+void BVHLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
     mFileName = pFile;
 
     // read file into memory
-    std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
-    if( file.get() == NULL)
-        throw DeadlyImportError( "Failed to open file " + pFile + ".");
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+    if (file.get() == nullptr) {
+        throw DeadlyImportError("Failed to open file " + pFile + ".");
+    }
 
     size_t fileSize = file->FileSize();
-    if( fileSize == 0)
-        throw DeadlyImportError( "File is too small.");
+    if (fileSize == 0) {
+        throw DeadlyImportError("File is too small.");
+    }
 
-    mBuffer.resize( fileSize);
-    file->Read( &mBuffer.front(), 1, fileSize);
+    mBuffer.resize(fileSize);
+    file->Read(&mBuffer.front(), 1, fileSize);
 
     // start reading
     mReader = mBuffer.begin();
     mLine = 1;
-    ReadStructure( pScene);
+    ReadStructure(pScene);
 
     if (!noSkeletonMesh) {
         // build a dummy mesh for the skeleton so that we see something at least
-        SkeletonMeshBuilder meshBuilder( pScene);
+        SkeletonMeshBuilder meshBuilder(pScene);
     }
 
     // construct an animation from all the motion data we read
-    CreateAnimation( pScene);
+    CreateAnimation(pScene);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Reads the file
-void BVHLoader::ReadStructure( aiScene* pScene)
-{
+void BVHLoader::ReadStructure(aiScene *pScene) {
     // first comes hierarchy
     std::string header = GetNextToken();
-    if( header != "HIERARCHY")
-        ThrowException( "Expected header string \"HIERARCHY\".");
-    ReadHierarchy( pScene);
+    if (header != "HIERARCHY")
+        ThrowException("Expected header string \"HIERARCHY\".");
+    ReadHierarchy(pScene);
 
     // then comes the motion data
     std::string motion = GetNextToken();
-    if( motion != "MOTION")
-        ThrowException( "Expected beginning of motion data \"MOTION\".");
-    ReadMotion( pScene);
+    if (motion != "MOTION")
+        ThrowException("Expected beginning of motion data \"MOTION\".");
+    ReadMotion(pScene);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Reads the hierarchy
-void BVHLoader::ReadHierarchy( aiScene* pScene)
-{
+void BVHLoader::ReadHierarchy(aiScene *pScene) {
     std::string root = GetNextToken();
-    if( root != "ROOT")
-        ThrowException( "Expected root node \"ROOT\".");
+    if (root != "ROOT")
+        ThrowException("Expected root node \"ROOT\".");
 
     // Go read the hierarchy from here
     pScene->mRootNode = ReadNode();
@@ -179,73 +172,64 @@ void BVHLoader::ReadHierarchy( aiScene* pScene)
 
 // ------------------------------------------------------------------------------------------------
 // Reads a node and recursively its childs and returns the created node;
-aiNode* BVHLoader::ReadNode()
-{
+aiNode *BVHLoader::ReadNode() {
     // first token is name
     std::string nodeName = GetNextToken();
-    if( nodeName.empty() || nodeName == "{")
-        ThrowException( format() << "Expected node name, but found \"" << nodeName << "\"." );
+    if (nodeName.empty() || nodeName == "{")
+        ThrowException(format() << "Expected node name, but found \"" << nodeName << "\".");
 
     // then an opening brace should follow
     std::string openBrace = GetNextToken();
-    if( openBrace != "{")
-        ThrowException( format() << "Expected opening brace \"{\", but found \"" << openBrace << "\"." );
+    if (openBrace != "{")
+        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
 
     // Create a node
-    aiNode* node = new aiNode( nodeName);
-    std::vector<aiNode*> childNodes;
+    aiNode *node = new aiNode(nodeName);
+    std::vector<aiNode *> childNodes;
 
     // and create an bone entry for it
-    mNodes.push_back( Node( node));
-    Node& internNode = mNodes.back();
+    mNodes.push_back(Node(node));
+    Node &internNode = mNodes.back();
 
     // now read the node's contents
     std::string siteToken;
-    while( 1)
-    {
+    while (1) {
         std::string token = GetNextToken();
 
         // node offset to parent node
-        if( token == "OFFSET")
-            ReadNodeOffset( node);
-        else if( token == "CHANNELS")
-            ReadNodeChannels( internNode);
-        else if( token == "JOINT")
-        {
+        if (token == "OFFSET")
+            ReadNodeOffset(node);
+        else if (token == "CHANNELS")
+            ReadNodeChannels(internNode);
+        else if (token == "JOINT") {
             // child node follows
-            aiNode* child = ReadNode();
+            aiNode *child = ReadNode();
             child->mParent = node;
-            childNodes.push_back( child);
-        }
-        else if( token == "End")
-        {
+            childNodes.push_back(child);
+        } else if (token == "End") {
             // The real symbol is "End Site". Second part comes in a separate token
             siteToken.clear();
             siteToken = GetNextToken();
-            if( siteToken != "Site")
-                ThrowException( format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\"." );
+            if (siteToken != "Site")
+                ThrowException(format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\".");
 
-            aiNode* child = ReadEndSite( nodeName);
+            aiNode *child = ReadEndSite(nodeName);
             child->mParent = node;
-            childNodes.push_back( child);
-        }
-        else if( token == "}")
-        {
+            childNodes.push_back(child);
+        } else if (token == "}") {
             // we're done with that part of the hierarchy
             break;
-        } else
-        {
+        } else {
             // everything else is a parse error
-            ThrowException( format() << "Unknown keyword \"" << token << "\"." );
+            ThrowException(format() << "Unknown keyword \"" << token << "\".");
         }
     }
 
     // add the child nodes if there are any
-    if( childNodes.size() > 0)
-    {
+    if (childNodes.size() > 0) {
         node->mNumChildren = static_cast<unsigned int>(childNodes.size());
-        node->mChildren = new aiNode*[node->mNumChildren];
-        std::copy( childNodes.begin(), childNodes.end(), node->mChildren);
+        node->mChildren = new aiNode *[node->mNumChildren];
+        std::copy(childNodes.begin(), childNodes.end(), node->mChildren);
     }
 
     // and return the sub-hierarchy we built here
@@ -254,31 +238,30 @@ aiNode* BVHLoader::ReadNode()
 
 // ------------------------------------------------------------------------------------------------
 // Reads an end node and returns the created node.
-aiNode* BVHLoader::ReadEndSite( const std::string& pParentName)
-{
+aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
     // check opening brace
     std::string openBrace = GetNextToken();
-    if( openBrace != "{")
-        ThrowException( format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
+    if (openBrace != "{")
+        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
 
     // Create a node
-    aiNode* node = new aiNode( "EndSite_" + pParentName);
+    aiNode *node = new aiNode("EndSite_" + pParentName);
 
     // now read the node's contents. Only possible entry is "OFFSET"
     std::string token;
-    while( 1) {
+    while (1) {
         token.clear();
         token = GetNextToken();
 
         // end node's offset
-        if( token == "OFFSET") {
-            ReadNodeOffset( node);
-        } else if( token == "}") {
+        if (token == "OFFSET") {
+            ReadNodeOffset(node);
+        } else if (token == "}") {
             // we're done with the end node
             break;
         } else {
             // everything else is a parse error
-            ThrowException( format() << "Unknown keyword \"" << token << "\"." );
+            ThrowException(format() << "Unknown keyword \"" << token << "\".");
         }
     }
 
@@ -287,8 +270,7 @@ aiNode* BVHLoader::ReadEndSite( const std::string& pParentName)
 }
 // ------------------------------------------------------------------------------------------------
 // Reads a node offset for the given node
-void BVHLoader::ReadNodeOffset( aiNode* pNode)
-{
+void BVHLoader::ReadNodeOffset(aiNode *pNode) {
     // Offset consists of three floats to read
     aiVector3D offset;
     offset.x = GetNextTokenAsFloat();
@@ -296,74 +278,69 @@ void BVHLoader::ReadNodeOffset( aiNode* pNode)
     offset.z = GetNextTokenAsFloat();
 
     // build a transformation matrix from it
-    pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x,
-                                          0.0f, 1.0f, 0.0f, offset.y,
-                                          0.0f, 0.0f, 1.0f, offset.z,
-                                          0.0f, 0.0f, 0.0f, 1.0f);
+    pNode->mTransformation = aiMatrix4x4(1.0f, 0.0f, 0.0f, offset.x,
+            0.0f, 1.0f, 0.0f, offset.y,
+            0.0f, 0.0f, 1.0f, offset.z,
+            0.0f, 0.0f, 0.0f, 1.0f);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Reads the animation channels for the given node
-void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode)
-{
+void BVHLoader::ReadNodeChannels(BVHLoader::Node &pNode) {
     // number of channels. Use the float reader because we're lazy
     float numChannelsFloat = GetNextTokenAsFloat();
-    unsigned int numChannels = (unsigned int) numChannelsFloat;
+    unsigned int numChannels = (unsigned int)numChannelsFloat;
 
-    for( unsigned int a = 0; a < numChannels; a++)
-    {
+    for (unsigned int a = 0; a < numChannels; a++) {
         std::string channelToken = GetNextToken();
 
-        if( channelToken == "Xposition")
-            pNode.mChannels.push_back( Channel_PositionX);
-        else if( channelToken == "Yposition")
-            pNode.mChannels.push_back( Channel_PositionY);
-        else if( channelToken == "Zposition")
-            pNode.mChannels.push_back( Channel_PositionZ);
-        else if( channelToken == "Xrotation")
-            pNode.mChannels.push_back( Channel_RotationX);
-        else if( channelToken == "Yrotation")
-            pNode.mChannels.push_back( Channel_RotationY);
-        else if( channelToken == "Zrotation")
-            pNode.mChannels.push_back( Channel_RotationZ);
+        if (channelToken == "Xposition")
+            pNode.mChannels.push_back(Channel_PositionX);
+        else if (channelToken == "Yposition")
+            pNode.mChannels.push_back(Channel_PositionY);
+        else if (channelToken == "Zposition")
+            pNode.mChannels.push_back(Channel_PositionZ);
+        else if (channelToken == "Xrotation")
+            pNode.mChannels.push_back(Channel_RotationX);
+        else if (channelToken == "Yrotation")
+            pNode.mChannels.push_back(Channel_RotationY);
+        else if (channelToken == "Zrotation")
+            pNode.mChannels.push_back(Channel_RotationZ);
         else
-            ThrowException( format() << "Invalid channel specifier \"" << channelToken << "\"." );
+            ThrowException(format() << "Invalid channel specifier \"" << channelToken << "\".");
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Reads the motion data
-void BVHLoader::ReadMotion( aiScene* /*pScene*/)
-{
+void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
     // Read number of frames
     std::string tokenFrames = GetNextToken();
-    if( tokenFrames != "Frames:")
-        ThrowException( format() << "Expected frame count \"Frames:\", but found \"" << tokenFrames << "\".");
+    if (tokenFrames != "Frames:")
+        ThrowException(format() << "Expected frame count \"Frames:\", but found \"" << tokenFrames << "\".");
 
     float numFramesFloat = GetNextTokenAsFloat();
-    mAnimNumFrames = (unsigned int) numFramesFloat;
+    mAnimNumFrames = (unsigned int)numFramesFloat;
 
     // Read frame duration
     std::string tokenDuration1 = GetNextToken();
     std::string tokenDuration2 = GetNextToken();
-    if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
-        ThrowException( format() << "Expected frame duration \"Frame Time:\", but found \"" << tokenDuration1 << " " << tokenDuration2 << "\"." );
+    if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
+        ThrowException(format() << "Expected frame duration \"Frame Time:\", but found \"" << tokenDuration1 << " " << tokenDuration2 << "\".");
 
     mAnimTickDuration = GetNextTokenAsFloat();
 
     // resize value vectors for each node
-    for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
-        it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames);
+    for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+        it->mChannelValues.reserve(it->mChannels.size() * mAnimNumFrames);
 
     // now read all the data and store it in the corresponding node's value vector
-    for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame)
-    {
+    for (unsigned int frame = 0; frame < mAnimNumFrames; ++frame) {
         // on each line read the values for all nodes
-        for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
-        {
+        for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) {
             // get as many values as the node has channels
-            for( unsigned int c = 0; c < it->mChannels.size(); ++c)
-                it->mChannelValues.push_back( GetNextTokenAsFloat());
+            for (unsigned int c = 0; c < it->mChannels.size(); ++c)
+                it->mChannelValues.push_back(GetNextTokenAsFloat());
         }
 
         // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
@@ -372,16 +349,14 @@ void BVHLoader::ReadMotion( aiScene* /*pScene*/)
 
 // ------------------------------------------------------------------------------------------------
 // Retrieves the next token
-std::string BVHLoader::GetNextToken()
-{
+std::string BVHLoader::GetNextToken() {
     // skip any preceding whitespace
-    while( mReader != mBuffer.end())
-    {
-        if( !isspace( *mReader))
+    while (mReader != mBuffer.end()) {
+        if (!isspace(*mReader))
             break;
 
         // count lines
-        if( *mReader == '\n')
+        if (*mReader == '\n')
             mLine++;
 
         ++mReader;
@@ -389,16 +364,15 @@ std::string BVHLoader::GetNextToken()
 
     // collect all chars till the next whitespace. BVH is easy in respect to that.
     std::string token;
-    while( mReader != mBuffer.end())
-    {
-        if( isspace( *mReader))
+    while (mReader != mBuffer.end()) {
+        if (isspace(*mReader))
             break;
 
-        token.push_back( *mReader);
+        token.push_back(*mReader);
         ++mReader;
 
         // little extra logic to make sure braces are counted correctly
-        if( token == "{" || token == "}")
+        if (token == "{" || token == "}")
             break;
     }
 
@@ -408,111 +382,101 @@ std::string BVHLoader::GetNextToken()
 
 // ------------------------------------------------------------------------------------------------
 // Reads the next token as a float
-float BVHLoader::GetNextTokenAsFloat()
-{
+float BVHLoader::GetNextTokenAsFloat() {
     std::string token = GetNextToken();
-    if( token.empty())
-        ThrowException( "Unexpected end of file while trying to read a float");
+    if (token.empty())
+        ThrowException("Unexpected end of file while trying to read a float");
 
     // check if the float is valid by testing if the atof() function consumed every char of the token
-    const char* ctoken = token.c_str();
+    const char *ctoken = token.c_str();
     float result = 0.0f;
-    ctoken = fast_atoreal_move<float>( ctoken, result);
+    ctoken = fast_atoreal_move<float>(ctoken, result);
 
-    if( ctoken != token.c_str() + token.length())
-        ThrowException( format() << "Expected a floating point number, but found \"" << token << "\"." );
+    if (ctoken != token.c_str() + token.length())
+        ThrowException(format() << "Expected a floating point number, but found \"" << token << "\".");
 
     return result;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Aborts the file reading with an exception
-AI_WONT_RETURN void BVHLoader::ThrowException( const std::string& pError)
-{
-    throw DeadlyImportError( format() << mFileName << ":" << mLine << " - " << pError);
+AI_WONT_RETURN void BVHLoader::ThrowException(const std::string &pError) {
+    throw DeadlyImportError(format() << mFileName << ":" << mLine << " - " << pError);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Constructs an animation for the motion data and stores it in the given scene
-void BVHLoader::CreateAnimation( aiScene* pScene)
-{
+void BVHLoader::CreateAnimation(aiScene *pScene) {
     // create the animation
     pScene->mNumAnimations = 1;
-    pScene->mAnimations = new aiAnimation*[1];
-    aiAnimation* anim = new aiAnimation;
+    pScene->mAnimations = new aiAnimation *[1];
+    aiAnimation *anim = new aiAnimation;
     pScene->mAnimations[0] = anim;
 
     // put down the basic parameters
-    anim->mName.Set( "Motion");
-    anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration);
-    anim->mDuration = double( mAnimNumFrames - 1);
+    anim->mName.Set("Motion");
+    anim->mTicksPerSecond = 1.0 / double(mAnimTickDuration);
+    anim->mDuration = double(mAnimNumFrames - 1);
 
     // now generate the tracks for all nodes
     anim->mNumChannels = static_cast<unsigned int>(mNodes.size());
-    anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+    anim->mChannels = new aiNodeAnim *[anim->mNumChannels];
 
     // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown
-    for (unsigned int i = 0; i < anim->mNumChannels;++i)
+    for (unsigned int i = 0; i < anim->mNumChannels; ++i)
         anim->mChannels[i] = NULL;
 
-    for( unsigned int a = 0; a < anim->mNumChannels; a++)
-    {
-        const Node& node = mNodes[a];
-        const std::string nodeName = std::string( node.mNode->mName.data );
-        aiNodeAnim* nodeAnim = new aiNodeAnim;
+    for (unsigned int a = 0; a < anim->mNumChannels; a++) {
+        const Node &node = mNodes[a];
+        const std::string nodeName = std::string(node.mNode->mName.data);
+        aiNodeAnim *nodeAnim = new aiNodeAnim;
         anim->mChannels[a] = nodeAnim;
-        nodeAnim->mNodeName.Set( nodeName);
-		std::map<BVHLoader::ChannelType, int> channelMap;
+        nodeAnim->mNodeName.Set(nodeName);
+        std::map<BVHLoader::ChannelType, int> channelMap;
 
-		//Build map of channels 
-		for (unsigned int channel = 0; channel < node.mChannels.size(); ++channel)
-		{
-			channelMap[node.mChannels[channel]] = channel;
-		}
+        //Build map of channels
+        for (unsigned int channel = 0; channel < node.mChannels.size(); ++channel) {
+            channelMap[node.mChannels[channel]] = channel;
+        }
 
         // translational part, if given
-        if( node.mChannels.size() == 6)
-        {
+        if (node.mChannels.size() == 6) {
             nodeAnim->mNumPositionKeys = mAnimNumFrames;
             nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
-            aiVectorKey* poskey = nodeAnim->mPositionKeys;
-            for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
-            {
-                poskey->mTime = double( fr);
-
-                // Now compute all translations 
-                for(BVHLoader::ChannelType channel = Channel_PositionX; channel <= Channel_PositionZ; channel = (BVHLoader::ChannelType)(channel +1))
-                {
-					//Find channel in node
-					std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
-
-					if (mapIter == channelMap.end())
-						throw DeadlyImportError("Missing position channel in node " + nodeName);
-					else {
-						int channelIdx = mapIter->second;
-						switch (channel) {
-						    case Channel_PositionX: 
-                                poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; 
-                                break;
-						    case Channel_PositionY: 
-                                poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; 
-                                break;
-						    case Channel_PositionZ: 
-                                poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; 
-                                break;
-                                
-                            default:
-                                break;
-						}
-
-					}
+            aiVectorKey *poskey = nodeAnim->mPositionKeys;
+            for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
+                poskey->mTime = double(fr);
+
+                // Now compute all translations
+                for (BVHLoader::ChannelType channel = Channel_PositionX; channel <= Channel_PositionZ; channel = (BVHLoader::ChannelType)(channel + 1)) {
+                    //Find channel in node
+                    std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
+
+                    if (mapIter == channelMap.end())
+                        throw DeadlyImportError("Missing position channel in node " + nodeName);
+                    else {
+                        int channelIdx = mapIter->second;
+                        switch (channel) {
+                        case Channel_PositionX:
+                            poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
+                            break;
+                        case Channel_PositionY:
+                            poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
+                            break;
+                        case Channel_PositionZ:
+                            poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channelIdx];
+                            break;
+
+                        default:
+                            break;
+                        }
+                    }
                 }
                 ++poskey;
             }
-        } else
-        {
+        } else {
             // if no translation part is given, put a default sequence
-            aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
+            aiVector3D nodePos(node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
             nodeAnim->mNumPositionKeys = 1;
             nodeAnim->mPositionKeys = new aiVectorKey[1];
             nodeAnim->mPositionKeys[0].mTime = 0.0;
@@ -525,42 +489,36 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
             // Then create the number of rotation keys
             nodeAnim->mNumRotationKeys = mAnimNumFrames;
             nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
-            aiQuatKey* rotkey = nodeAnim->mRotationKeys;
-            for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
-            {
+            aiQuatKey *rotkey = nodeAnim->mRotationKeys;
+            for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
                 aiMatrix4x4 temp;
                 aiMatrix3x3 rotMatrix;
-				for (BVHLoader::ChannelType channel = Channel_RotationX; channel <= Channel_RotationZ; channel = (BVHLoader::ChannelType)(channel + 1))
-				{
-					//Find channel in node
-					std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
-
-					if (mapIter == channelMap.end())
-						throw DeadlyImportError("Missing rotation channel in node " + nodeName);
-					else {
-						int channelIdx = mapIter->second;
-						// translate ZXY euler angels into a quaternion
-						const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
-
-						// Compute rotation transformations in the right order
-						switch (channel)
-						{
-							case Channel_RotationX: 
-                                aiMatrix4x4::RotationX(angle, temp); rotMatrix *= aiMatrix3x3(temp); 
-                                break;
-							case Channel_RotationY: 
-                                aiMatrix4x4::RotationY(angle, temp); rotMatrix *= aiMatrix3x3(temp);  
-                                break;
-							case Channel_RotationZ: aiMatrix4x4::RotationZ(angle, temp); rotMatrix *= aiMatrix3x3(temp); 
-                                break;
-                            default:
-                                break;
-						}
+				for (unsigned int channelIdx = 0; channelIdx < node.mChannels.size(); ++ channelIdx) {
+					switch (node.mChannels[channelIdx]) {
+                    case Channel_RotationX:
+                        {
+                        const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+                        aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+                        }
+                        break;
+                    case Channel_RotationY:
+                        {
+                        const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+                        aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+                        }
+                        break;
+                    case Channel_RotationZ:
+                        {
+                        const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+                        aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+                        }
+                        break;
+                    default:
+                        break;
 					}
-                }
-
-                rotkey->mTime = double( fr);
-                rotkey->mValue = aiQuaternion( rotMatrix);
+				}
+                rotkey->mTime = double(fr);
+                rotkey->mValue = aiQuaternion(rotMatrix);
                 ++rotkey;
             }
         }
@@ -570,7 +528,7 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
             nodeAnim->mNumScalingKeys = 1;
             nodeAnim->mScalingKeys = new aiVectorKey[1];
             nodeAnim->mScalingKeys[0].mTime = 0.0;
-            nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f);
+            nodeAnim->mScalingKeys[0].mValue.Set(1.0f, 1.0f, 1.0f);
         }
     }
 }

+ 22 - 29
code/BVH/BVHLoader.h → code/AssetLib/BVH/BVHLoader.h

@@ -53,8 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 struct aiNode;
 
-namespace Assimp
-{
+namespace Assimp {
 
 // --------------------------------------------------------------------------------
 /** Loader class to read Motion Capturing data from a .bvh file.
@@ -63,12 +62,10 @@ namespace Assimp
  * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh
  * inside the loader just to be able to see something.
 */
-class BVHLoader : public BaseImporter
-{
+class BVHLoader : public BaseImporter {
 
     /** Possible animation channels for which the motion data holds the values */
-    enum ChannelType
-    {
+    enum ChannelType {
         Channel_PositionX,
         Channel_PositionY,
         Channel_PositionZ,
@@ -78,61 +75,57 @@ class BVHLoader : public BaseImporter
     };
 
     /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */
-    struct Node
-    {
-        const aiNode* mNode;
+    struct Node {
+        const aiNode *mNode;
         std::vector<ChannelType> mChannels;
         std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
 
-        Node()
-        : mNode(nullptr)
-        { }
+        Node() :
+                mNode(nullptr) {}
 
-        explicit Node( const aiNode* pNode) : mNode( pNode) { }
+        explicit Node(const aiNode *pNode) :
+                mNode(pNode) {}
     };
 
 public:
-
     BVHLoader();
     ~BVHLoader();
 
 public:
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details. */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const;
 
-    void SetupProperties(const Importer* pImp);
-    const aiImporterDesc* GetInfo () const;
+    void SetupProperties(const Importer *pImp);
+    const aiImporterDesc *GetInfo() const;
 
 protected:
-
-
     /** 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);
 
 protected:
     /** Reads the file */
-    void ReadStructure( aiScene* pScene);
+    void ReadStructure(aiScene *pScene);
 
     /** Reads the hierarchy */
-    void ReadHierarchy( aiScene* pScene);
+    void ReadHierarchy(aiScene *pScene);
 
     /** Reads a node and recursively its childs and returns the created node. */
-    aiNode* ReadNode();
+    aiNode *ReadNode();
 
     /** Reads an end node and returns the created node. */
-    aiNode* ReadEndSite( const std::string& pParentName);
+    aiNode *ReadEndSite(const std::string &pParentName);
 
     /** Reads a node offset for the given node */
-    void ReadNodeOffset( aiNode* pNode);
+    void ReadNodeOffset(aiNode *pNode);
 
     /** Reads the animation channels into the given node */
-    void ReadNodeChannels( BVHLoader::Node& pNode);
+    void ReadNodeChannels(BVHLoader::Node &pNode);
 
     /** Reads the motion data */
-    void ReadMotion( aiScene* pScene);
+    void ReadMotion(aiScene *pScene);
 
     /** Retrieves the next token */
     std::string GetNextToken();
@@ -141,10 +134,10 @@ protected:
     float GetNextTokenAsFloat();
 
     /** Aborts the file reading with an exception */
-    AI_WONT_RETURN void ThrowException( const std::string& pError) AI_WONT_RETURN_SUFFIX;
+    AI_WONT_RETURN void ThrowException(const std::string &pError) AI_WONT_RETURN_SUFFIX;
 
     /** Constructs an animation for the motion data and stores it in the given scene */
-    void CreateAnimation( aiScene* pScene);
+    void CreateAnimation(aiScene *pScene);
 
 protected:
     /** Filename, for a verbose error message */

+ 62 - 84
code/Blender/BlenderBMesh.cpp → code/AssetLib/Blender/BlenderBMesh.cpp

@@ -42,165 +42,143 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Conversion of Blender's new BMesh stuff
  */
 
-
 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
 
+#include "BlenderBMesh.h"
 #include "BlenderDNA.h"
 #include "BlenderScene.h"
-#include "BlenderBMesh.h"
 #include "BlenderTessellator.h"
 
-namespace Assimp
-{
-    template< > const char* LogFunctions< BlenderBMeshConverter >::Prefix()
-    {
-        static auto prefix = "BLEND_BMESH: ";
-        return prefix;
-    }
+namespace Assimp {
+template <>
+const char *LogFunctions<BlenderBMeshConverter>::Prefix() {
+    static auto prefix = "BLEND_BMESH: ";
+    return prefix;
 }
+} // namespace Assimp
 
 using namespace Assimp;
 using namespace Assimp::Blender;
 using namespace Assimp::Formatter;
 
 // ------------------------------------------------------------------------------------------------
-BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ):
-    BMesh( mesh ),
-    triMesh( NULL )
-{
+BlenderBMeshConverter::BlenderBMeshConverter(const Mesh *mesh) :
+        BMesh(mesh),
+        triMesh(nullptr) {
+    ai_assert(nullptr != mesh);
 }
 
 // ------------------------------------------------------------------------------------------------
-BlenderBMeshConverter::~BlenderBMeshConverter( )
-{
-    DestroyTriMesh( );
+BlenderBMeshConverter::~BlenderBMeshConverter() {
+    DestroyTriMesh();
 }
 
 // ------------------------------------------------------------------------------------------------
-bool BlenderBMeshConverter::ContainsBMesh( ) const
-{
+bool BlenderBMeshConverter::ContainsBMesh() const {
     // TODO - Should probably do some additional verification here
     return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
 }
 
 // ------------------------------------------------------------------------------------------------
-const Mesh* BlenderBMeshConverter::TriangulateBMesh( )
-{
-    AssertValidMesh( );
-    AssertValidSizes( );
-    PrepareTriMesh( );
-
-    for ( int i = 0; i < BMesh->totpoly; ++i )
-    {
-        const MPoly& poly = BMesh->mpoly[ i ];
-        ConvertPolyToFaces( poly );
+const Mesh *BlenderBMeshConverter::TriangulateBMesh() {
+    AssertValidMesh();
+    AssertValidSizes();
+    PrepareTriMesh();
+
+    for (int i = 0; i < BMesh->totpoly; ++i) {
+        const MPoly &poly = BMesh->mpoly[i];
+        ConvertPolyToFaces(poly);
     }
 
     return triMesh;
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AssertValidMesh( )
-{
-    if ( !ContainsBMesh( ) )
-    {
-        ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" );
+void BlenderBMeshConverter::AssertValidMesh() {
+    if (!ContainsBMesh()) {
+        ThrowException("BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first");
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AssertValidSizes( )
-{
-    if ( BMesh->totpoly != static_cast<int>( BMesh->mpoly.size( ) ) )
-    {
-        ThrowException( "BMesh poly array has incorrect size" );
+void BlenderBMeshConverter::AssertValidSizes() {
+    if (BMesh->totpoly != static_cast<int>(BMesh->mpoly.size())) {
+        ThrowException("BMesh poly array has incorrect size");
     }
-    if ( BMesh->totloop != static_cast<int>( BMesh->mloop.size( ) ) )
-    {
-        ThrowException( "BMesh loop array has incorrect size" );
+    if (BMesh->totloop != static_cast<int>(BMesh->mloop.size())) {
+        ThrowException("BMesh loop array has incorrect size");
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::PrepareTriMesh( )
-{
-    if ( triMesh )
-    {
-        DestroyTriMesh( );
+void BlenderBMeshConverter::PrepareTriMesh() {
+    if (triMesh) {
+        DestroyTriMesh();
     }
 
-    triMesh = new Mesh( *BMesh );
+    triMesh = new Mesh(*BMesh);
     triMesh->totface = 0;
-    triMesh->mface.clear( );
+    triMesh->mface.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::DestroyTriMesh( )
-{
+void BlenderBMeshConverter::DestroyTriMesh() {
     delete triMesh;
-    triMesh = NULL;
+    triMesh = nullptr;
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::ConvertPolyToFaces( const MPoly& poly )
-{
-    const MLoop* polyLoop = &BMesh->mloop[ poly.loopstart ];
+void BlenderBMeshConverter::ConvertPolyToFaces(const MPoly &poly) {
+    const MLoop *polyLoop = &BMesh->mloop[poly.loopstart];
 
-    if ( poly.totloop == 3 || poly.totloop == 4 )
-    {
-        AddFace( polyLoop[ 0 ].v, polyLoop[ 1 ].v, polyLoop[ 2 ].v, poly.totloop == 4 ? polyLoop[ 3 ].v : 0 );
+    if (poly.totloop == 3 || poly.totloop == 4) {
+        AddFace(polyLoop[0].v, polyLoop[1].v, polyLoop[2].v, poly.totloop == 4 ? polyLoop[3].v : 0);
 
         // UVs are optional, so only convert when present.
-        if ( BMesh->mloopuv.size() )
-        {
-            if ( (poly.loopstart + poly.totloop ) > static_cast<int>( BMesh->mloopuv.size() ) )
-            {
-                ThrowException( "BMesh uv loop array has incorrect size" );
+        if (BMesh->mloopuv.size()) {
+            if ((poly.loopstart + poly.totloop) > static_cast<int>(BMesh->mloopuv.size())) {
+                ThrowException("BMesh uv loop array has incorrect size");
             }
-            const MLoopUV* loopUV = &BMesh->mloopuv[ poly.loopstart ];
-            AddTFace( loopUV[ 0 ].uv, loopUV[ 1 ].uv, loopUV[ 2 ].uv, poly.totloop == 4 ? loopUV[ 3 ].uv : 0 );
+            const MLoopUV *loopUV = &BMesh->mloopuv[poly.loopstart];
+            AddTFace(loopUV[0].uv, loopUV[1].uv, loopUV[2].uv, poly.totloop == 4 ? loopUV[3].uv : 0);
         }
-    }
-    else if ( poly.totloop > 4 )
-    {
+    } else if (poly.totloop > 4) {
 #if ASSIMP_BLEND_WITH_GLU_TESSELLATE
-        BlenderTessellatorGL tessGL( *this );
-        tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+        BlenderTessellatorGL tessGL(*this);
+        tessGL.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
 #elif ASSIMP_BLEND_WITH_POLY_2_TRI
-        BlenderTessellatorP2T tessP2T( *this );
-        tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+        BlenderTessellatorP2T tessP2T(*this);
+        tessP2T.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
 #endif
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
-{
+void BlenderBMeshConverter::AddFace(int v1, int v2, int v3, int v4) {
     MFace face;
     face.v1 = v1;
     face.v2 = v2;
     face.v3 = v3;
     face.v4 = v4;
+    face.flag = 0;
     // TODO - Work out how materials work
     face.mat_nr = 0;
-    triMesh->mface.push_back( face );
-    triMesh->totface = static_cast<int>(triMesh->mface.size( ));
+    triMesh->mface.push_back(face);
+    triMesh->totface = static_cast<int>(triMesh->mface.size());
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AddTFace( const float* uv1, const float *uv2, const float *uv3, const float* uv4 )
-{
+void BlenderBMeshConverter::AddTFace(const float *uv1, const float *uv2, const float *uv3, const float *uv4) {
     MTFace mtface;
-    memcpy( &mtface.uv[ 0 ], uv1, sizeof(float) * 2 );
-    memcpy( &mtface.uv[ 1 ], uv2, sizeof(float) * 2 );
-    memcpy( &mtface.uv[ 2 ], uv3, sizeof(float) * 2 );
+    memcpy(&mtface.uv[0], uv1, sizeof(float) * 2);
+    memcpy(&mtface.uv[1], uv2, sizeof(float) * 2);
+    memcpy(&mtface.uv[2], uv3, sizeof(float) * 2);
 
-    if ( uv4 )
-    {
-        memcpy( &mtface.uv[ 3 ], uv4, sizeof(float) * 2 );
+    if (uv4) {
+        memcpy(&mtface.uv[3], uv4, sizeof(float) * 2);
     }
 
-    triMesh->mtface.push_back( mtface );
+    triMesh->mtface.push_back(mtface);
 }
 
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 0 - 0
code/Blender/BlenderBMesh.h → code/AssetLib/Blender/BlenderBMesh.h


+ 181 - 0
code/AssetLib/Blender/BlenderCustomData.cpp

@@ -0,0 +1,181 @@
+#include "BlenderCustomData.h"
+#include "BlenderDNA.h"
+#include <array>
+#include <functional>
+
+namespace Assimp {
+namespace Blender {
+/**
+        *   @brief  read/convert of Structure array to memory
+        */
+template <typename T>
+bool read(const Structure &s, T *p, const size_t cnt, const FileDatabase &db) {
+    for (size_t i = 0; i < cnt; ++i) {
+        T read;
+        s.Convert(read, db);
+        *p = read;
+        p++;
+    }
+    return true;
+}
+
+/**
+        *   @brief  pointer to function read memory for n CustomData types
+        */
+typedef bool (*PRead)(ElemBase *pOut, const size_t cnt, const FileDatabase &db);
+typedef ElemBase *(*PCreate)(const size_t cnt);
+typedef void (*PDestroy)(ElemBase *);
+
+#define IMPL_STRUCT_READ(ty)                                               \
+    bool read##ty(ElemBase *v, const size_t cnt, const FileDatabase &db) { \
+        ty *ptr = dynamic_cast<ty *>(v);                                   \
+        if (nullptr == ptr) {                                              \
+            return false;                                                  \
+        }                                                                  \
+        return read<ty>(db.dna[#ty], ptr, cnt, db);                        \
+    }
+
+#define IMPL_STRUCT_CREATE(ty)               \
+    ElemBase *create##ty(const size_t cnt) { \
+        return new ty[cnt];                  \
+    }
+
+#define IMPL_STRUCT_DESTROY(ty)         \
+    void destroy##ty(ElemBase *pE) {    \
+        ty *p = dynamic_cast<ty *>(pE); \
+        delete[] p;                     \
+    }
+
+/**
+        *   @brief  helper macro to define Structure functions
+        */
+#define IMPL_STRUCT(ty)    \
+    IMPL_STRUCT_READ(ty)   \
+    IMPL_STRUCT_CREATE(ty) \
+    IMPL_STRUCT_DESTROY(ty)
+
+// supported structures for CustomData
+IMPL_STRUCT(MVert)
+IMPL_STRUCT(MEdge)
+IMPL_STRUCT(MFace)
+IMPL_STRUCT(MTFace)
+IMPL_STRUCT(MTexPoly)
+IMPL_STRUCT(MLoopUV)
+IMPL_STRUCT(MLoopCol)
+IMPL_STRUCT(MPoly)
+IMPL_STRUCT(MLoop)
+
+/**
+        *   @brief  describes the size of data and the read function to be used for single CustomerData.type
+        */
+struct CustomDataTypeDescription {
+    PRead Read; ///< function to read one CustomData type element
+    PCreate Create; ///< function to allocate n type elements
+    PDestroy Destroy;
+
+    CustomDataTypeDescription(PRead read, PCreate create, PDestroy destroy) :
+            Read(read), Create(create), Destroy(destroy) {}
+};
+
+/**
+        *   @brief  helper macro to define Structure type specific CustomDataTypeDescription
+        *   @note   IMPL_STRUCT_READ for same ty must be used earlier to implement the typespecific read function
+        */
+#define DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(ty) \
+    CustomDataTypeDescription { &read##ty, &create##ty, &destroy##ty }
+
+/**
+        *   @brief  helper macro to define CustomDataTypeDescription for UNSUPPORTED type
+        */
+#define DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION \
+    CustomDataTypeDescription { nullptr, nullptr, nullptr }
+
+/**
+        *   @brief  descriptors for data pointed to from CustomDataLayer.data
+        *   @note   some of the CustomData uses already well defined Structures
+        *           other (like CD_ORCO, ...) uses arrays of rawtypes or even arrays of Structures
+        *           use a special readfunction for that cases
+        */
+std::array<CustomDataTypeDescription, CD_NUMTYPES> customDataTypeDescriptions = { { DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MVert),
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MEdge),
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MFace),
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTFace),
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MTexPoly),
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopUV),
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoopCol),
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MPoly),
+        DECL_STRUCT_CUSTOMDATATYPEDESCRIPTION(MLoop),
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION,
+        DECL_UNSUPPORTED_CUSTOMDATATYPEDESCRIPTION } };
+
+bool isValidCustomDataType(const int cdtype) {
+    return cdtype >= 0 && cdtype < CD_NUMTYPES;
+}
+
+bool readCustomData(std::shared_ptr<ElemBase> &out, const int cdtype, const size_t cnt, const FileDatabase &db) {
+    if (!isValidCustomDataType(cdtype)) {
+        throw Error((Formatter::format(), "CustomData.type ", cdtype, " out of index"));
+    }
+
+    const CustomDataTypeDescription cdtd = customDataTypeDescriptions[cdtype];
+    if (cdtd.Read && cdtd.Create && cdtd.Destroy && cnt > 0) {
+        // allocate cnt elements and parse them from file
+        out.reset(cdtd.Create(cnt), cdtd.Destroy);
+        return cdtd.Read(out.get(), cnt, db);
+    }
+    return false;
+}
+
+std::shared_ptr<CustomDataLayer> getCustomDataLayer(const CustomData &customdata, const CustomDataType cdtype, const std::string &name) {
+    for (auto it = customdata.layers.begin(); it != customdata.layers.end(); ++it) {
+        if (it->get()->type == cdtype && name == it->get()->name) {
+            return *it;
+        }
+    }
+    return nullptr;
+}
+
+const ElemBase *getCustomDataLayerData(const CustomData &customdata, const CustomDataType cdtype, const std::string &name) {
+    const std::shared_ptr<CustomDataLayer> pLayer = getCustomDataLayer(customdata, cdtype, name);
+    if (pLayer && pLayer->data) {
+        return pLayer->data.get();
+    }
+    return nullptr;
+}
+} // namespace Blender
+} // namespace Assimp

+ 0 - 0
code/Blender/BlenderCustomData.h → code/AssetLib/Blender/BlenderCustomData.h


+ 75 - 90
code/Blender/BlenderDNA.cpp → code/AssetLib/Blender/BlenderDNA.cpp

@@ -45,25 +45,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *    serialized set of data structures.
  */
 
-
 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
 #include "BlenderDNA.h"
 #include <assimp/StreamReader.h>
-#include <assimp/fast_atof.h>
 #include <assimp/TinyFormatter.h>
+#include <assimp/fast_atof.h>
 
 using namespace Assimp;
 using namespace Assimp::Blender;
 using namespace Assimp::Formatter;
 
-static bool match4(StreamReaderAny& stream, const char* string) {
-    ai_assert( nullptr != string );
+static bool match4(StreamReaderAny &stream, const char *string) {
+    ai_assert(nullptr != string);
     char tmp[4];
-    tmp[ 0 ] = ( stream ).GetI1();
-    tmp[ 1 ] = ( stream ).GetI1();
-    tmp[ 2 ] = ( stream ).GetI1();
-    tmp[ 3 ] = ( stream ).GetI1();
-    return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]);
+    tmp[0] = (stream).GetI1();
+    tmp[1] = (stream).GetI1();
+    tmp[2] = (stream).GetI1();
+    tmp[3] = (stream).GetI1();
+    return (tmp[0] == string[0] && tmp[1] == string[1] && tmp[2] == string[2] && tmp[3] == string[3]);
 }
 
 struct Type {
@@ -72,75 +71,76 @@ struct Type {
 };
 
 // ------------------------------------------------------------------------------------------------
-void DNAParser::Parse ()
-{
-    StreamReaderAny& stream = *db.reader.get();
-    DNA& dna = db.dna;
+void DNAParser::Parse() {
+    StreamReaderAny &stream = *db.reader.get();
+    DNA &dna = db.dna;
 
-    if(!match4(stream,"SDNA")) {
+    if (!match4(stream, "SDNA")) {
         throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
     }
 
     // name dictionary
-    if(!match4(stream,"NAME")) {
+    if (!match4(stream, "NAME")) {
         throw DeadlyImportError("BlenderDNA: Expected NAME field");
     }
 
-    std::vector<std::string> names (stream.GetI4());
-    for(std::string& s : names) {
+    std::vector<std::string> names(stream.GetI4());
+    for (std::string &s : names) {
         while (char c = stream.GetI1()) {
             s += c;
         }
     }
 
     // type dictionary
-    for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
-    if(!match4(stream,"TYPE")) {
+    for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+        ;
+    if (!match4(stream, "TYPE")) {
         throw DeadlyImportError("BlenderDNA: Expected TYPE field");
     }
 
-    std::vector<Type> types (stream.GetI4());
-    for(Type& s : types) {
+    std::vector<Type> types(stream.GetI4());
+    for (Type &s : types) {
         while (char c = stream.GetI1()) {
             s.name += c;
         }
     }
 
     // type length dictionary
-    for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
-    if(!match4(stream,"TLEN")) {
+    for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+        ;
+    if (!match4(stream, "TLEN")) {
         throw DeadlyImportError("BlenderDNA: Expected TLEN field");
     }
 
-    for(Type& s : types) {
+    for (Type &s : types) {
         s.size = stream.GetI2();
     }
 
     // structures dictionary
-    for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
-    if(!match4(stream,"STRC")) {
+    for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+        ;
+    if (!match4(stream, "STRC")) {
         throw DeadlyImportError("BlenderDNA: Expected STRC field");
     }
 
     size_t end = stream.GetI4(), fields = 0;
 
     dna.structures.reserve(end);
-    for(size_t i = 0; i != end; ++i) {
+    for (size_t i = 0; i != end; ++i) {
 
         uint16_t n = stream.GetI2();
         if (n >= types.size()) {
             throw DeadlyImportError((format(),
-                "BlenderDNA: Invalid type index in structure name" ,n,
-                " (there are only ", types.size(), " entries)"
-            ));
+                    "BlenderDNA: Invalid type index in structure name", n,
+                    " (there are only ", types.size(), " entries)"));
         }
 
         // maintain separate indexes
         dna.indices[types[n].name] = dna.structures.size();
 
         dna.structures.push_back(Structure());
-        Structure& s = dna.structures.back();
-        s.name  = types[n].name;
+        Structure &s = dna.structures.back();
+        s.name = types[n].name;
         //s.index = dna.structures.size()-1;
 
         n = stream.GetI2();
@@ -152,12 +152,11 @@ void DNAParser::Parse ()
             uint16_t j = stream.GetI2();
             if (j >= types.size()) {
                 throw DeadlyImportError((format(),
-                    "BlenderDNA: Invalid type index in structure field ", j,
-                    " (there are only ", types.size(), " entries)"
-                ));
+                        "BlenderDNA: Invalid type index in structure field ", j,
+                        " (there are only ", types.size(), " entries)"));
             }
             s.fields.push_back(Field());
-            Field& f = s.fields.back();
+            Field &f = s.fields.back();
             f.offset = offset;
 
             f.type = types[j].name;
@@ -166,9 +165,8 @@ void DNAParser::Parse ()
             j = stream.GetI2();
             if (j >= names.size()) {
                 throw DeadlyImportError((format(),
-                    "BlenderDNA: Invalid name index in structure field ", j,
-                    " (there are only ", names.size(), " entries)"
-                ));
+                        "BlenderDNA: Invalid name index in structure field ", j,
+                        " (there are only ", names.size(), " entries)"));
             }
 
             f.name = names[j];
@@ -191,26 +189,25 @@ void DNAParser::Parse ()
                 const std::string::size_type rb = f.name.find('[');
                 if (rb == std::string::npos) {
                     throw DeadlyImportError((format(),
-                        "BlenderDNA: Encountered invalid array declaration ",
-                        f.name
-                    ));
+                            "BlenderDNA: Encountered invalid array declaration ",
+                            f.name));
                 }
 
                 f.flags |= FieldFlag_Array;
-                DNA::ExtractArraySize(f.name,f.array_sizes);
-                f.name = f.name.substr(0,rb);
+                DNA::ExtractArraySize(f.name, f.array_sizes);
+                f.name = f.name.substr(0, rb);
 
                 f.size *= f.array_sizes[0] * f.array_sizes[1];
             }
 
             // maintain separate indexes
-            s.indices[f.name] = s.fields.size()-1;
+            s.indices[f.name] = s.fields.size() - 1;
             offset += f.size;
         }
         s.size = offset;
     }
 
-    ASSIMP_LOG_DEBUG_F( "BlenderDNA: Got ", dna.structures.size()," structures with totally ",fields," fields");
+    ASSIMP_LOG_DEBUG_F("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields");
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
     dna.DumpToFile();
@@ -220,13 +217,11 @@ void DNAParser::Parse ()
     dna.RegisterConverters();
 }
 
-
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
 
 #include <fstream>
 // ------------------------------------------------------------------------------------------------
-void DNA :: DumpToFile()
-{
+void DNA ::DumpToFile() {
     // we don't bother using the VFS here for this is only for debugging.
     // (and all your bases are belong to us).
 
@@ -235,12 +230,14 @@ void DNA :: DumpToFile()
         ASSIMP_LOG_ERROR("Could not dump dna to dna.txt");
         return;
     }
-    f << "Field format: type name offset size" << "\n";
-    f << "Structure format: name size" << "\n";
+    f << "Field format: type name offset size"
+      << "\n";
+    f << "Structure format: name size"
+      << "\n";
 
-    for(const Structure& s : structures) {
+    for (const Structure &s : structures) {
         f << s.name << " " << s.size << "\n\n";
-        for(const Field& ff : s.fields) {
+        for (const Field &ff : s.fields) {
             f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << "\n";
         }
         f << "\n";
@@ -252,11 +249,9 @@ void DNA :: DumpToFile()
 #endif
 
 // ------------------------------------------------------------------------------------------------
-/*static*/ void  DNA :: ExtractArraySize(
-    const std::string& out,
-    size_t array_sizes[2]
-)
-{
+/*static*/ void DNA ::ExtractArraySize(
+        const std::string &out,
+        size_t array_sizes[2]) {
     array_sizes[0] = array_sizes[1] = 1;
     std::string::size_type pos = out.find('[');
     if (pos++ == std::string::npos) {
@@ -264,7 +259,7 @@ void DNA :: DumpToFile()
     }
     array_sizes[0] = strtoul10(&out[pos]);
 
-    pos = out.find('[',pos);
+    pos = out.find('[', pos);
     if (pos++ == std::string::npos) {
         return;
     }
@@ -272,36 +267,32 @@ void DNA :: DumpToFile()
 }
 
 // ------------------------------------------------------------------------------------------------
-std::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure(
-    const Structure& structure,
-    const FileDatabase& db
-) const
-{
-    std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name);
+std::shared_ptr<ElemBase> DNA ::ConvertBlobToStructure(
+        const Structure &structure,
+        const FileDatabase &db) const {
+    std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
     if (it == converters.end()) {
-        return std::shared_ptr< ElemBase >();
+        return std::shared_ptr<ElemBase>();
     }
 
-    std::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))();
-    (structure.*((*it).second.second))(ret,db);
+    std::shared_ptr<ElemBase> ret = (structure.*((*it).second.first))();
+    (structure.*((*it).second.second))(ret, db);
 
     return ret;
 }
 
 // ------------------------------------------------------------------------------------------------
-DNA::FactoryPair DNA :: GetBlobToStructureConverter(
-    const Structure& structure,
-    const FileDatabase& /*db*/
-) const
-{
-    std::map<std::string,  FactoryPair>::const_iterator it = converters.find(structure.name);
+DNA::FactoryPair DNA ::GetBlobToStructureConverter(
+        const Structure &structure,
+        const FileDatabase & /*db*/
+) const {
+    std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
     return it == converters.end() ? FactoryPair() : (*it).second;
 }
 
 // basing on http://www.blender.org/development/architecture/notes-on-sdna/
 // ------------------------------------------------------------------------------------------------
-void DNA :: AddPrimitiveStructures()
-{
+void DNA ::AddPrimitiveStructures() {
     // NOTE: these are just dummies. Their presence enforces
     // Structure::Convert<target_type> to be called on these
     // empty structures. These converters are special
@@ -311,30 +302,27 @@ void DNA :: AddPrimitiveStructures()
     // in question.
 
     indices["int"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "int";
     structures.back().size = 4;
 
     indices["short"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "short";
     structures.back().size = 2;
 
-
     indices["char"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "char";
     structures.back().size = 1;
 
-
     indices["float"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "float";
     structures.back().size = 4;
 
-
     indices["double"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "double";
     structures.back().size = 8;
 
@@ -342,8 +330,7 @@ void DNA :: AddPrimitiveStructures()
 }
 
 // ------------------------------------------------------------------------------------------------
-void SectionParser :: Next()
-{
+void SectionParser ::Next() {
     stream.SetCurrentPos(current.start + current.size);
 
     const char tmp[] = {
@@ -352,7 +339,7 @@ void SectionParser :: Next()
         (const char)stream.GetI1(),
         (const char)stream.GetI1()
     };
-    current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1);
+    current.id = std::string(tmp, tmp[3] ? 4 : tmp[2] ? 3 : tmp[1] ? 2 : 1);
 
     current.size = stream.GetI4();
     current.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
@@ -366,10 +353,8 @@ void SectionParser :: Next()
     }
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
-    ASSIMP_LOG_DEBUG(current.id);
+    ASSIMP_LOG_VERBOSE_DEBUG(current.id);
 #endif
 }
 
-
-
 #endif

+ 162 - 205
code/Blender/BlenderDNA.h → code/AssetLib/Blender/BlenderDNA.h

@@ -49,26 +49,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/BaseImporter.h>
 #include <assimp/StreamReader.h>
-#include <assimp/DefaultLogger.hpp>
 #include <stdint.h>
-#include <memory>
+#include <assimp/DefaultLogger.hpp>
 #include <map>
+#include <memory>
 
 // enable verbose log output. really verbose, so be careful.
 #ifdef ASSIMP_BUILD_DEBUG
-#   define ASSIMP_BUILD_BLENDER_DEBUG
+#define ASSIMP_BUILD_BLENDER_DEBUG
 #endif
 
 // #define ASSIMP_BUILD_BLENDER_NO_STATS
 
-namespace Assimp    {
+namespace Assimp {
 
-template <bool,bool> class StreamReader;
-typedef StreamReader<true,true> StreamReaderAny;
+template <bool, bool>
+class StreamReader;
+typedef StreamReader<true, true> StreamReaderAny;
 
 namespace Blender {
 
-class  FileDatabase;
+class FileDatabase;
 struct FileBlockHead;
 
 template <template <typename> class TOUT>
@@ -82,8 +83,8 @@ class ObjectCache;
  *  ancestry. */
 // -------------------------------------------------------------------------------
 struct Error : DeadlyImportError {
-    Error (const std::string& s)
-    : DeadlyImportError(s) {
+    Error(const std::string &s) :
+            DeadlyImportError(s) {
         // empty
     }
 };
@@ -93,9 +94,8 @@ struct Error : DeadlyImportError {
  *  descendents. It serves as base class for all data structure fields. */
 // -------------------------------------------------------------------------------
 struct ElemBase {
-    ElemBase()
-    : dna_type(nullptr)
-    {
+    ElemBase() :
+            dna_type(nullptr) {
         // empty
     }
 
@@ -110,7 +110,7 @@ struct ElemBase {
      * data type is not static, i.e. a std::shared_ptr<ElemBase>
      * in the scene description would have its type resolved
      * at runtime, so this member is always set. */
-    const char* dna_type;
+    const char *dna_type;
 };
 
 // -------------------------------------------------------------------------------
@@ -120,8 +120,8 @@ struct ElemBase {
  *  they used to point to.*/
 // -------------------------------------------------------------------------------
 struct Pointer {
-    Pointer()
-    : val() {
+    Pointer() :
+            val() {
         // empty
     }
     uint64_t val;
@@ -131,8 +131,8 @@ struct Pointer {
 /** Represents a generic offset within a BLEND file */
 // -------------------------------------------------------------------------------
 struct FileOffset {
-    FileOffset()
-    : val() {
+    FileOffset() :
+            val() {
         // empty
     }
     uint64_t val;
@@ -154,7 +154,7 @@ public:
         resize(0);
     }
 
-    operator bool () const {
+    operator bool() const {
         return !empty();
     }
 };
@@ -164,7 +164,7 @@ public:
 // -------------------------------------------------------------------------------
 enum FieldFlags {
     FieldFlag_Pointer = 0x1,
-    FieldFlag_Array   = 0x2
+    FieldFlag_Array = 0x2
 };
 
 // -------------------------------------------------------------------------------
@@ -200,7 +200,7 @@ enum ErrorPolicy {
 };
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
-#   define ErrorPolicy_Igno ErrorPolicy_Warn
+#define ErrorPolicy_Igno ErrorPolicy_Warn
 #endif
 
 // -------------------------------------------------------------------------------
@@ -212,47 +212,42 @@ enum ErrorPolicy {
  *  meaningful contents. */
 // -------------------------------------------------------------------------------
 class Structure {
-    template <template <typename> class> friend class ObjectCache;
+    template <template <typename> class>
+    friend class ObjectCache;
 
 public:
-    Structure()
-    : cache_idx(static_cast<size_t>(-1) ){
+    Structure() :
+            cache_idx(static_cast<size_t>(-1)) {
         // empty
     }
 
-public:
-
     // publicly accessible members
     std::string name;
-    vector< Field > fields;
+    vector<Field> fields;
     std::map<std::string, size_t> indices;
 
     size_t size;
 
-public:
-
     // --------------------------------------------------------
     /** Access a field of the structure by its canonical name. The pointer version
      *  returns NULL on failure while the reference version raises an import error. */
-    inline const Field& operator [] (const std::string& ss) const;
-    inline const Field* Get (const std::string& ss) const;
+    inline const Field &operator[](const std::string &ss) const;
+    inline const Field *Get(const std::string &ss) const;
 
     // --------------------------------------------------------
     /** Access a field of the structure by its index */
-    inline const Field& operator [] (const size_t i) const;
+    inline const Field &operator[](const size_t i) const;
 
     // --------------------------------------------------------
-    inline bool operator== (const Structure& other) const {
+    inline bool operator==(const Structure &other) const {
         return name == other.name; // name is meant to be an unique identifier
     }
 
     // --------------------------------------------------------
-    inline bool operator!= (const Structure& other) const {
+    inline bool operator!=(const Structure &other) const {
         return name != other.name;
     }
 
-public:
-
     // --------------------------------------------------------
     /** Try to read an instance of the structure from the stream
      *  and attempt to convert to `T`. This is done by
@@ -260,54 +255,54 @@ public:
      *  a compiler complain is the result.
      *  @param dest Destination value to be written
      *  @param db File database, including input stream. */
-    template <typename T> void Convert (T& dest, const FileDatabase& db) const;
+    template <typename T>
+    void Convert(T &dest, const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // generic converter
     template <typename T>
-    void Convert(std::shared_ptr<ElemBase> in,const FileDatabase& db) const;
+    void Convert(std::shared_ptr<ElemBase> in, const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // generic allocator
-    template <typename T> std::shared_ptr<ElemBase> Allocate() const;
-
-
+    template <typename T>
+    std::shared_ptr<ElemBase> Allocate() const;
 
     // --------------------------------------------------------
     // field parsing for 1d arrays
     template <int error_policy, typename T, size_t M>
-    void ReadFieldArray(T (& out)[M], const char* name,
-        const FileDatabase& db) const;
+    void ReadFieldArray(T (&out)[M], const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // field parsing for 2d arrays
     template <int error_policy, typename T, size_t M, size_t N>
-    void ReadFieldArray2(T (& out)[M][N], const char* name,
-        const FileDatabase& db) const;
+    void ReadFieldArray2(T (&out)[M][N], const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // field parsing for pointer or dynamic array types
     // (std::shared_ptr)
     // The return value indicates whether the data was already cached.
     template <int error_policy, template <typename> class TOUT, typename T>
-    bool ReadFieldPtr(TOUT<T>& out, const char* name,
-        const FileDatabase& db,
-        bool non_recursive = false) const;
+    bool ReadFieldPtr(TOUT<T> &out, const char *name,
+            const FileDatabase &db,
+            bool non_recursive = false) const;
 
     // --------------------------------------------------------
     // field parsing for static arrays of pointer or dynamic
     // array types (std::shared_ptr[])
     // The return value indicates whether the data was already cached.
     template <int error_policy, template <typename> class TOUT, typename T, size_t N>
-    bool ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
-        const FileDatabase& db) const;
+    bool ReadFieldPtr(TOUT<T> (&out)[N], const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // field parsing for `normal` values
     // The return value indicates whether the data was already cached.
     template <int error_policy, typename T>
-    void ReadField(T& out, const char* name,
-        const FileDatabase& db) const;
+    void ReadField(T &out, const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     /**
@@ -318,7 +313,7 @@ public:
     *   @return true when read was successful
     */
     template <int error_policy, template <typename> class TOUT, typename T>
-    bool ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const;
+    bool ReadFieldPtrVector(vector<TOUT<T>> &out, const char *name, const FileDatabase &db) const;
 
     /**
     *   @brief  parses raw customdata
@@ -329,40 +324,40 @@ public:
     *   @return true when read was successful
     */
     template <int error_policy>
-    bool ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const;
+    bool ReadCustomDataPtr(std::shared_ptr<ElemBase> &out, int cdtype, const char *name, const FileDatabase &db) const;
 
 private:
-
     // --------------------------------------------------------
     template <template <typename> class TOUT, typename T>
-    bool ResolvePointer(TOUT<T>& out, const Pointer & ptrval,
-        const FileDatabase& db, const Field& f,
-        bool non_recursive = false) const;
+    bool ResolvePointer(TOUT<T> &out, const Pointer &ptrval,
+            const FileDatabase &db, const Field &f,
+            bool non_recursive = false) const;
 
     // --------------------------------------------------------
     template <template <typename> class TOUT, typename T>
-    bool ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
-        const FileDatabase& db, const Field& f, bool) const;
+    bool ResolvePointer(vector<TOUT<T>> &out, const Pointer &ptrval,
+            const FileDatabase &db, const Field &f, bool) const;
 
     // --------------------------------------------------------
-    bool ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval,
-        const FileDatabase& db, const Field& f, bool) const;
+    bool ResolvePointer(std::shared_ptr<FileOffset> &out, const Pointer &ptrval,
+            const FileDatabase &db, const Field &f, bool) const;
 
     // --------------------------------------------------------
-    inline const FileBlockHead* LocateFileBlockForAddress(
-        const Pointer & ptrval,
-        const FileDatabase& db) const;
+    inline const FileBlockHead *LocateFileBlockForAddress(
+            const Pointer &ptrval,
+            const FileDatabase &db) const;
 
 private:
-
     // ------------------------------------------------------------------------------
-    template <typename T> T* _allocate(std::shared_ptr<T>& out, size_t& s) const {
+    template <typename T>
+    T *_allocate(std::shared_ptr<T> &out, size_t &s) const {
         out = std::shared_ptr<T>(new T());
         s = 1;
         return out.get();
     }
 
-    template <typename T> T* _allocate(vector<T>& out, size_t& s) const {
+    template <typename T>
+    T *_allocate(vector<T> &out, size_t &s) const {
         out.resize(s);
         return s ? &out.front() : NULL;
     }
@@ -372,14 +367,14 @@ private:
     struct _defaultInitializer {
 
         template <typename T, unsigned int N>
-        void operator ()(T (& out)[N], const char* = NULL) {
+        void operator()(T (&out)[N], const char * = NULL) {
             for (unsigned int i = 0; i < N; ++i) {
                 out[i] = T();
             }
         }
 
         template <typename T, unsigned int N, unsigned int M>
-        void operator ()(T (& out)[N][M], const char* = NULL) {
+        void operator()(T (&out)[N][M], const char * = NULL) {
             for (unsigned int i = 0; i < N; ++i) {
                 for (unsigned int j = 0; j < M; ++j) {
                     out[i][j] = T();
@@ -388,21 +383,21 @@ private:
         }
 
         template <typename T>
-        void operator ()(T& out, const char* = NULL) {
+        void operator()(T &out, const char * = NULL) {
             out = T();
         }
     };
 
 private:
-
     mutable size_t cache_idx;
 };
 
 // --------------------------------------------------------
-template <>  struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
+template <>
+struct Structure ::_defaultInitializer<ErrorPolicy_Warn> {
 
     template <typename T>
-    void operator ()(T& out, const char* reason = "<add reason>") {
+    void operator()(T &out, const char *reason = "<add reason>") {
         ASSIMP_LOG_WARN(reason);
 
         // ... and let the show go on
@@ -410,10 +405,11 @@ template <>  struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
     }
 };
 
-template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
+template <>
+struct Structure ::_defaultInitializer<ErrorPolicy_Fail> {
 
     template <typename T>
-    void operator ()(T& /*out*/,const char* = "") {
+    void operator()(T & /*out*/, const char * = "") {
         // obviously, it is crucial that _DefaultInitializer is used
         // only from within a catch clause.
         throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
@@ -421,13 +417,12 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
 };
 
 // -------------------------------------------------------------------------------------------------------
-template <> inline bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shared_ptr<ElemBase>& out,
-    const Pointer & ptrval,
-    const FileDatabase& db,
-    const Field& f,
-    bool
-    ) const;
-
+template <>
+inline bool Structure ::ResolvePointer<std::shared_ptr, ElemBase>(std::shared_ptr<ElemBase> &out,
+        const Pointer &ptrval,
+        const FileDatabase &db,
+        const Field &f,
+        bool) const;
 
 // -------------------------------------------------------------------------------
 /** Represents the full data structure information for a single BLEND file.
@@ -435,40 +430,34 @@ template <> inline bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(st
  *  #DNAParser does the reading and represents currently the only place where
  *  DNA is altered.*/
 // -------------------------------------------------------------------------------
-class DNA
-{
+class DNA {
 public:
-
-    typedef void (Structure::*ConvertProcPtr) (
-        std::shared_ptr<ElemBase> in,
-        const FileDatabase&
-    ) const;
+    typedef void (Structure::*ConvertProcPtr)(
+            std::shared_ptr<ElemBase> in,
+            const FileDatabase &) const;
 
     typedef std::shared_ptr<ElemBase> (
-        Structure::*AllocProcPtr) () const;
+            Structure::*AllocProcPtr)() const;
 
-    typedef std::pair< AllocProcPtr, ConvertProcPtr > FactoryPair;
+    typedef std::pair<AllocProcPtr, ConvertProcPtr> FactoryPair;
 
 public:
-
-    std::map<std::string, FactoryPair > converters;
-    vector<Structure > structures;
+    std::map<std::string, FactoryPair> converters;
+    vector<Structure> structures;
     std::map<std::string, size_t> indices;
 
 public:
-
     // --------------------------------------------------------
     /** Access a structure by its canonical name, the pointer version returns NULL on failure
       * while the reference version raises an error. */
-    inline const Structure& operator [] (const std::string& ss) const;
-    inline const Structure* Get (const std::string& ss) const;
+    inline const Structure &operator[](const std::string &ss) const;
+    inline const Structure *Get(const std::string &ss) const;
 
     // --------------------------------------------------------
     /** Access a structure by its index */
-    inline const Structure& operator [] (const size_t i) const;
+    inline const Structure &operator[](const size_t i) const;
 
 public:
-
     // --------------------------------------------------------
     /** Add structure definitions for all the primitive types,
      *  i.e. integer, short, char, float */
@@ -483,7 +472,6 @@ public:
      *  known at compile time (consier Object::data).*/
     void RegisterConverters();
 
-
     // --------------------------------------------------------
     /** Take an input blob from the stream, interpret it according to
      *  a its structure name and convert it to the intermediate
@@ -491,10 +479,9 @@ public:
      *  @param structure Destination structure definition
      *  @param db File database.
      *  @return A null pointer if no appropriate converter is available.*/
-    std::shared_ptr< ElemBase > ConvertBlobToStructure(
-        const Structure& structure,
-        const FileDatabase& db
-        ) const;
+    std::shared_ptr<ElemBase> ConvertBlobToStructure(
+            const Structure &structure,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     /** Find a suitable conversion function for a given Structure.
@@ -505,10 +492,8 @@ public:
      *  @param db File database.
      *  @return A null pointer in .first if no appropriate converter is available.*/
     FactoryPair GetBlobToStructureConverter(
-        const Structure& structure,
-        const FileDatabase& db
-        ) const;
-
+            const Structure &structure,
+            const FileDatabase &db) const;
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
     // --------------------------------------------------------
@@ -527,25 +512,29 @@ public:
      *  @throw DeadlyImportError if more than 2 dimensions are
      *    encountered. */
     static void ExtractArraySize(
-        const std::string& out,
-        size_t array_sizes[2]
-    );
+            const std::string &out,
+            size_t array_sizes[2]);
 };
 
 // special converters for primitive types
-template <> inline void Structure :: Convert<int>       (int& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<short>     (short& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<char>      (char& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<float>     (float& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<double>    (double& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<Pointer>   (Pointer& dest,const FileDatabase& db) const;
+template <>
+inline void Structure ::Convert<int>(int &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<short>(short &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<char>(char &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<float>(float &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<double>(double &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<Pointer>(Pointer &dest, const FileDatabase &db) const;
 
 // -------------------------------------------------------------------------------
 /** Describes a master file block header. Each master file sections holds n
  *  elements of a certain SDNA structure (or otherwise unspecified data). */
 // -------------------------------------------------------------------------------
-struct FileBlockHead
-{
+struct FileBlockHead {
     // points right after the header of the file block
     StreamReaderAny::pos start;
 
@@ -561,66 +550,55 @@ struct FileBlockHead
     // number of structure instances to follow
     size_t num;
 
-
-
     // file blocks are sorted by address to quickly locate specific memory addresses
-    bool operator < (const FileBlockHead& o) const {
+    bool operator<(const FileBlockHead &o) const {
         return address.val < o.address.val;
     }
 
     // for std::upper_bound
-    operator const Pointer& () const {
+    operator const Pointer &() const {
         return address;
     }
 };
 
 // for std::upper_bound
-inline bool operator< (const Pointer& a, const Pointer& b) {
+inline bool operator<(const Pointer &a, const Pointer &b) {
     return a.val < b.val;
 }
 
 // -------------------------------------------------------------------------------
 /** Utility to read all master file blocks in turn. */
 // -------------------------------------------------------------------------------
-class SectionParser
-{
+class SectionParser {
 public:
-
     // --------------------------------------------------------
     /** @param stream Inout stream, must point to the
      *  first section in the file. Call Next() once
      *  to have it read.
      *  @param ptr64 Pointer size in file is 64 bits? */
-    SectionParser(StreamReaderAny& stream,bool ptr64)
-        : stream(stream)
-        , ptr64(ptr64)
-    {
+    SectionParser(StreamReaderAny &stream, bool ptr64) :
+            stream(stream), ptr64(ptr64) {
         current.size = current.start = 0;
     }
 
 public:
-
     // --------------------------------------------------------
-    const FileBlockHead& GetCurrent() const {
+    const FileBlockHead &GetCurrent() const {
         return current;
     }
 
-
 public:
-
     // --------------------------------------------------------
     /** Advance to the next section.
      *  @throw DeadlyImportError if the last chunk was passed. */
     void Next();
 
 public:
-
     FileBlockHead current;
-    StreamReaderAny& stream;
+    StreamReaderAny &stream;
     bool ptr64;
 };
 
-
 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
 // -------------------------------------------------------------------------------
 /** Import statistics, i.e. number of file blocks read*/
@@ -628,17 +606,13 @@ public:
 class Statistics {
 
 public:
-
-    Statistics ()
-        : fields_read       ()
-        , pointers_resolved ()
-        , cache_hits        ()
-//      , blocks_read       ()
-        , cached_objects    ()
-    {}
+    Statistics() :
+            fields_read(), pointers_resolved(), cache_hits()
+            //      , blocks_read       ()
+            ,
+            cached_objects() {}
 
 public:
-
     /** total number of fields we read */
     unsigned int fields_read;
 
@@ -662,17 +636,13 @@ public:
  *  avoids circular references and avoids object duplication. */
 // -------------------------------------------------------------------------------
 template <template <typename> class TOUT>
-class ObjectCache
-{
+class ObjectCache {
 public:
-
-    typedef std::map< Pointer, TOUT<ElemBase> > StructureCache;
+    typedef std::map<Pointer, TOUT<ElemBase>> StructureCache;
 
 public:
-
-    ObjectCache(const FileDatabase& db)
-        : db(db)
-    {
+    ObjectCache(const FileDatabase &db) :
+            db(db) {
         // currently there are only ~400 structure records per blend file.
         // we read only a small part of them and don't cache objects
         // which we don't need, so this should suffice.
@@ -680,17 +650,17 @@ public:
     }
 
 public:
-
     // --------------------------------------------------------
     /** Check whether a specific item is in the cache.
      *  @param s Data type of the item
      *  @param out Output pointer. Unchanged if the
      *   cache doesn't know the item yet.
      *  @param ptr Item address to look for. */
-    template <typename T> void get (
-        const Structure& s,
-        TOUT<T>& out,
-        const Pointer& ptr) const;
+    template <typename T>
+    void get(
+            const Structure &s,
+            TOUT<T> &out,
+            const Pointer &ptr) const;
 
     // --------------------------------------------------------
     /** Add an item to the cache after the item has
@@ -700,47 +670,44 @@ public:
      *  @param s Data type of the item
      *  @param out Item to insert into the cache
      *  @param ptr address (cache key) of the item. */
-    template <typename T> void set
-        (const Structure& s,
-        const TOUT<T>& out,
-        const Pointer& ptr);
+    template <typename T>
+    void set(const Structure &s,
+            const TOUT<T> &out,
+            const Pointer &ptr);
 
 private:
-
     mutable vector<StructureCache> caches;
-    const FileDatabase& db;
+    const FileDatabase &db;
 };
 
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
-template <> class ObjectCache<Blender::vector>
-{
+template <>
+class ObjectCache<Blender::vector> {
 public:
+    ObjectCache(const FileDatabase &) {}
 
-    ObjectCache(const FileDatabase&) {}
-
-    template <typename T> void get(const Structure&, vector<T>&, const Pointer&) {}
-    template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
+    template <typename T>
+    void get(const Structure &, vector<T> &, const Pointer &) {}
+    template <typename T>
+    void set(const Structure &, const vector<T> &, const Pointer &) {}
 };
 
 #ifdef _MSC_VER
-#   pragma warning(disable:4355)
+#pragma warning(disable : 4355)
 #endif
 
 // -------------------------------------------------------------------------------
 /** Memory representation of a full BLEND file and all its dependencies. The
  *  output aiScene is constructed from an instance of this data structure. */
 // -------------------------------------------------------------------------------
-class FileDatabase
-{
-    template <template <typename> class TOUT> friend class ObjectCache;
+class FileDatabase {
+    template <template <typename> class TOUT>
+    friend class ObjectCache;
 
 public:
-    FileDatabase()
-        : _cacheArrays(*this)
-        , _cache(*this)
-        , next_cache_idx()
-    {}
+    FileDatabase() :
+            _cacheArrays(*this), _cache(*this), next_cache_idx() {}
 
 public:
     // publicly accessible fields
@@ -748,12 +715,11 @@ public:
     bool little;
 
     DNA dna;
-    std::shared_ptr< StreamReaderAny > reader;
-    vector< FileBlockHead > entries;
+    std::shared_ptr<StreamReaderAny> reader;
+    vector<FileBlockHead> entries;
 
 public:
-
-    Statistics& stats() const {
+    Statistics &stats() const {
         return _stats;
     }
 
@@ -762,18 +728,16 @@ public:
     // arrays of objects are never cached because we can't easily
     // ensure their proper destruction.
     template <typename T>
-    ObjectCache<std::shared_ptr>& cache(std::shared_ptr<T>& /*in*/) const {
+    ObjectCache<std::shared_ptr> &cache(std::shared_ptr<T> & /*in*/) const {
         return _cache;
     }
 
     template <typename T>
-    ObjectCache<vector>& cache(vector<T>& /*in*/) const {
+    ObjectCache<vector> &cache(vector<T> & /*in*/) const {
         return _cacheArrays;
     }
 
 private:
-
-
 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
     mutable Statistics _stats;
 #endif
@@ -785,24 +749,20 @@ private:
 };
 
 #ifdef _MSC_VER
-#   pragma warning(default:4355)
+#pragma warning(default : 4355)
 #endif
 
 // -------------------------------------------------------------------------------
 /** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
 // -------------------------------------------------------------------------------
-class DNAParser
-{
+class DNAParser {
 
 public:
-
     /** Bind the parser to a empty DNA and an input stream */
-    DNAParser(FileDatabase& db)
-        : db(db)
-    {}
+    DNAParser(FileDatabase &db) :
+            db(db) {}
 
 public:
-
     // --------------------------------------------------------
     /** Locate the DNA in the file and parse it. The input
      *  stream is expected to point to the beginning of the DN1
@@ -811,18 +771,16 @@ public:
      *  @throw DeadlyImportError if the DNA cannot be read.
      *  @note The position of the stream pointer is undefined
      *    afterwards.*/
-    void Parse ();
+    void Parse();
 
 public:
-
     /** Obtain a reference to the extracted DNA information */
-    const Blender::DNA& GetDNA() const {
+    const Blender::DNA &GetDNA() const {
         return db.dna;
     }
 
 private:
-
-    FileDatabase& db;
+    FileDatabase &db;
 };
 
 /**
@@ -835,9 +793,8 @@ private:
 */
 bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
 
-
-    } // end Blend
-} // end Assimp
+} // namespace Blender
+} // namespace Assimp
 
 #include "BlenderDNA.inl"
 

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

@@ -795,7 +795,7 @@ const Structure& DNA :: operator [] (const std::string& ss) const
 const Structure* DNA :: Get (const std::string& ss) const
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
-    return it == indices.end() ? NULL : &structures[(*it).second];
+    return it == indices.end() ? nullptr : &structures[(*it).second];
 }
 
 //--------------------------------------------------------------------------------

+ 0 - 0
code/Blender/BlenderIntermediate.h → code/AssetLib/Blender/BlenderIntermediate.h


+ 17 - 18
code/Blender/BlenderLoader.cpp → code/AssetLib/Blender/BlenderLoader.cpp

@@ -206,7 +206,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile,
         inflateInit2(&zstream, 16+MAX_WBITS);
 
         zstream.next_in   = reinterpret_cast<Bytef*>( reader->GetPtr() );
-        zstream.avail_in  = reader->GetRemainingSize();
+        zstream.avail_in  = (uInt) reader->GetRemainingSize();
 
         size_t total = 0l;
 
@@ -429,7 +429,7 @@ void BlenderImporter::ResolveImage(aiMaterial* out, const Material* mat, const M
         name.length = 1+ ASSIMP_itoa10(name.data+1,static_cast<unsigned int>(MAXLEN-1), static_cast<int32_t>(conv_data.textures->size()));
 
         conv_data.textures->push_back(new aiTexture());
-        aiTexture* tex = conv_data.textures->back();
+        aiTexture* curTex = conv_data.textures->back();
 
         // usually 'img->name' will be the original file name of the embedded textures,
         // so we can extract the file extension from it.
@@ -439,19 +439,19 @@ void BlenderImporter::ResolveImage(aiMaterial* out, const Material* mat, const M
             --s;
         }
 
-        tex->achFormatHint[0] = s+1>e ? '\0' : ::tolower( s[1] );
-        tex->achFormatHint[1] = s+2>e ? '\0' : ::tolower( s[2] );
-        tex->achFormatHint[2] = s+3>e ? '\0' : ::tolower( s[3] );
-        tex->achFormatHint[3] = '\0';
+        curTex->achFormatHint[0] = s + 1 > e ? '\0' : (char)::tolower(s[1]);
+        curTex->achFormatHint[1] = s + 2 > e ? '\0' : (char)::tolower(s[2]);
+        curTex->achFormatHint[2] = s + 3 > e ? '\0' : (char)::tolower(s[3]);
+        curTex->achFormatHint[3] = '\0';
 
         // tex->mHeight = 0;
-        tex->mWidth = img->packedfile->size;
-        uint8_t* ch = new uint8_t[tex->mWidth];
+        curTex->mWidth = img->packedfile->size;
+        uint8_t *ch = new uint8_t[curTex->mWidth];
 
         conv_data.db.reader->SetCurrentPos(static_cast<size_t>( img->packedfile->data->val));
-        conv_data.db.reader->CopyAndAdvance(ch,tex->mWidth);
+        conv_data.db.reader->CopyAndAdvance(ch, curTex->mWidth);
 
-        tex->pcData = reinterpret_cast<aiTexel*>(ch);
+        curTex->pcData = reinterpret_cast<aiTexel *>(ch);
 
         LogInfo("Reading embedded texture, original file was "+std::string(img->name));
     } else {
@@ -1078,9 +1078,9 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
             const aiFace& f = out->mFaces[out->mNumFaces++];
 
             aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
-            for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
-                vo->x = v->uv[i][0];
-                vo->y = v->uv[i][1];
+            for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
+                vo->x = v->uv[j][0];
+                vo->y = v->uv[j][1];
             }
         }
 
@@ -1098,8 +1098,7 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
                     vo->x = uv.uv[0];
                     vo->y = uv.uv[1];
                 }
-            }
-            else {
+            } else {
                 // create textureCoords for every mapped tex
                 for (uint32_t m = 0; m < itMatTexUvMapping->second.size(); ++m) {
                     const MLoopUV *tm = itMatTexUvMapping->second[m];
@@ -1139,9 +1138,9 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
             const aiFace& f = out->mFaces[out->mNumFaces++];
 
             aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
-            for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
-                vo->x = v->uv[i][0];
-                vo->y = v->uv[i][1];
+            for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
+                vo->x = v->uv[j][0];
+                vo->y = v->uv[j][1];
             }
         }
     }

+ 0 - 0
code/Blender/BlenderLoader.h → code/AssetLib/Blender/BlenderLoader.h


+ 86 - 86
code/Blender/BlenderModifier.cpp → code/AssetLib/Blender/BlenderModifier.cpp

@@ -57,52 +57,51 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace Assimp::Blender;
 
-template <typename T> BlenderModifier* god() {
+template <typename T>
+BlenderModifier *god() {
     return new T();
 }
 
 // add all available modifiers here
-typedef BlenderModifier* (*fpCreateModifier)();
+typedef BlenderModifier *(*fpCreateModifier)();
 static const fpCreateModifier creators[] = {
-        &god<BlenderModifier_Mirror>,
-        &god<BlenderModifier_Subdivision>,
+    &god<BlenderModifier_Mirror>,
+    &god<BlenderModifier_Subdivision>,
 
-        NULL // sentinel
+    nullptr // sentinel
 };
 
 // ------------------------------------------------------------------------------------------------
-struct SharedModifierData : ElemBase
-{
+struct SharedModifierData : ElemBase {
     ModifierData modifier;
 };
 
 // ------------------------------------------------------------------------------------------------
-void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_data, const Scene& in, const Object& orig_object )
-{
+void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_data, const Scene &in, const Object &orig_object) {
     size_t cnt = 0u, ful = 0u;
 
     // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before
     // we're allowed to dereference the pointers without risking to crash. We might still be
     // invoking UB btw - we're assuming that the ModifierData member of the respective modifier
     // structures is at offset sizeof(vftable) with no padding.
-    const SharedModifierData* cur = static_cast<const SharedModifierData *> ( orig_object.modifiers.first.get() );
-    for (; cur; cur =  static_cast<const SharedModifierData *> ( cur->modifier.next.get() ), ++ful) {
+    const SharedModifierData *cur = static_cast<const SharedModifierData *>(orig_object.modifiers.first.get());
+    for (; cur; cur = static_cast<const SharedModifierData *>(cur->modifier.next.get()), ++ful) {
         ai_assert(cur->dna_type);
 
-        const Structure* s = conv_data.db.dna.Get( cur->dna_type );
+        const Structure *s = conv_data.db.dna.Get(cur->dna_type);
         if (!s) {
-            ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: ",cur->dna_type);
+            ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: ", cur->dna_type);
             continue;
         }
 
         // this is a common trait of all XXXMirrorData structures in BlenderDNA
-        const Field* f = s->Get("modifier");
+        const Field *f = s->Get("modifier");
         if (!f || f->offset != 0) {
             ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0");
             continue;
         }
 
-        s = conv_data.db.dna.Get( f->type );
+        s = conv_data.db.dna.Get(f->type);
         if (!s || s->name != "ModifierData") {
             ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member");
             continue;
@@ -110,30 +109,30 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_d
 
         // now, we can be sure that we should be fine to dereference *cur* as
         // ModifierData (with the above note).
-        const ModifierData& dat = cur->modifier;
+        const ModifierData &dat = cur->modifier;
 
-        const fpCreateModifier* curgod = creators;
-        std::vector< BlenderModifier* >::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end();
+        const fpCreateModifier *curgod = creators;
+        std::vector<BlenderModifier *>::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end();
 
-        for (;*curgod;++curgod,++curmod) { // allocate modifiers on the fly
+        for (; *curgod; ++curgod, ++curmod) { // allocate modifiers on the fly
             if (curmod == endmod) {
                 cached_modifiers->push_back((*curgod)());
 
                 endmod = cached_modifiers->end();
-                curmod = endmod-1;
+                curmod = endmod - 1;
             }
 
-            BlenderModifier* const modifier = *curmod;
-            if(modifier->IsActive(dat)) {
-                modifier->DoIt(out,conv_data,*static_cast<const ElemBase *>(cur),in,orig_object);
+            BlenderModifier *const modifier = *curmod;
+            if (modifier->IsActive(dat)) {
+                modifier->DoIt(out, conv_data, *static_cast<const ElemBase *>(cur), in, orig_object);
                 cnt++;
 
-                curgod = NULL;
+                curgod = nullptr;
                 break;
             }
         }
         if (curgod) {
-            ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: ",dat.name);
+            ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: ", dat.name);
         }
     }
 
@@ -141,26 +140,22 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_d
     // object, we still can't say whether our modifier implementations were
     // able to fully do their job.
     if (ful) {
-        ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for ",cnt," of ",ful," modifiers on `",orig_object.id.name,
-            "`, check log messages above for errors");
+        ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for ", cnt, " of ", ful, " modifiers on `", orig_object.id.name,
+                "`, check log messages above for errors");
     }
 }
 
-
-
 // ------------------------------------------------------------------------------------------------
-bool BlenderModifier_Mirror :: IsActive (const ModifierData& modin)
-{
+bool BlenderModifier_Mirror ::IsActive(const ModifierData &modin) {
     return modin.type == ModifierData::eModifierType_Mirror;
 }
 
 // ------------------------------------------------------------------------------------------------
-void  BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data,  const ElemBase& orig_modifier,
-    const Scene& /*in*/,
-    const Object& orig_object )
-{
+void BlenderModifier_Mirror ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier,
+        const Scene & /*in*/,
+        const Object &orig_object) {
     // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
-    const MirrorModifierData& mir = static_cast<const MirrorModifierData&>(orig_modifier);
+    const MirrorModifierData &mir = static_cast<const MirrorModifierData &>(orig_modifier);
     ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror);
 
     conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes);
@@ -169,48 +164,55 @@ void  BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data,  co
 
     // take all input meshes and clone them
     for (unsigned int i = 0; i < out.mNumMeshes; ++i) {
-        aiMesh* mesh;
-        SceneCombiner::Copy(&mesh,conv_data.meshes[out.mMeshes[i]]);
+        aiMesh *mesh;
+        SceneCombiner::Copy(&mesh, conv_data.meshes[out.mMeshes[i]]);
 
         const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f;
         const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f;
         const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f;
 
         if (mir.mirror_ob) {
-            const aiVector3D center( mir.mirror_ob->obmat[3][0],mir.mirror_ob->obmat[3][1],mir.mirror_ob->obmat[3][2] );
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mVertices[i];
+            const aiVector3D center(mir.mirror_ob->obmat[3][0], mir.mirror_ob->obmat[3][1], mir.mirror_ob->obmat[3][2]);
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mVertices[j];
 
-                v.x = center.x + xs*(center.x - v.x);
-                v.y = center.y + ys*(center.y - v.y);
-                v.z = center.z + zs*(center.z - v.z);
+                v.x = center.x + xs * (center.x - v.x);
+                v.y = center.y + ys * (center.y - v.y);
+                v.z = center.z + zs * (center.z - v.z);
             }
-        }
-        else {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mVertices[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+        } else {
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mVertices[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
         if (mesh->mNormals) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mNormals[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mNormals[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
         if (mesh->mTangents) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mTangents[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mTangents[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
         if (mesh->mBitangents) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mBitangents[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mBitangents[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
@@ -218,55 +220,53 @@ void  BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data,  co
         const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f;
 
         for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mTextureCoords[n][i];
-                v.x *= us;v.y *= vs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mTextureCoords[n][j];
+                v.x *= us;
+                v.y *= vs;
             }
         }
 
         // Only reverse the winding order if an odd number of axes were mirrored.
         if (xs * ys * zs < 0) {
-            for( unsigned int i = 0; i < mesh->mNumFaces; i++) {
-                aiFace& face = mesh->mFaces[i];
-                for( unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
-                    std::swap( face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
+            for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {
+                aiFace &face = mesh->mFaces[j];
+                for (unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
+                    std::swap(face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
             }
         }
 
         conv_data.meshes->push_back(mesh);
     }
-    unsigned int* nind = new unsigned int[out.mNumMeshes*2];
+    unsigned int *nind = new unsigned int[out.mNumMeshes * 2];
 
-    std::copy(out.mMeshes,out.mMeshes+out.mNumMeshes,nind);
-    std::transform(out.mMeshes,out.mMeshes+out.mNumMeshes,nind+out.mNumMeshes,
-        [&out](unsigned int n) { return out.mNumMeshes + n; });
+    std::copy(out.mMeshes, out.mMeshes + out.mNumMeshes, nind);
+    std::transform(out.mMeshes, out.mMeshes + out.mNumMeshes, nind + out.mNumMeshes,
+            [&out](unsigned int n) { return out.mNumMeshes + n; });
 
     delete[] out.mMeshes;
     out.mMeshes = nind;
     out.mNumMeshes *= 2;
 
     ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Mirror` modifier to `",
-        orig_object.id.name,"`");
+            orig_object.id.name, "`");
 }
 
 // ------------------------------------------------------------------------------------------------
-bool BlenderModifier_Subdivision :: IsActive (const ModifierData& modin)
-{
+bool BlenderModifier_Subdivision ::IsActive(const ModifierData &modin) {
     return modin.type == ModifierData::eModifierType_Subsurf;
 }
 
 // ------------------------------------------------------------------------------------------------
-void  BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data,  const ElemBase& orig_modifier,
-    const Scene& /*in*/,
-    const Object& orig_object )
-{
+void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier,
+        const Scene & /*in*/,
+        const Object &orig_object) {
     // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
-    const SubsurfModifierData& mir = static_cast<const SubsurfModifierData&>(orig_modifier);
+    const SubsurfModifierData &mir = static_cast<const SubsurfModifierData &>(orig_modifier);
     ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf);
 
     Subdivider::Algorithm algo;
-    switch (mir.subdivType)
-    {
+    switch (mir.subdivType) {
     case SubsurfModifierData::TYPE_CatmullClarke:
         algo = Subdivider::CATMULL_CLARKE;
         break;
@@ -277,23 +277,23 @@ void  BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data
         break;
 
     default:
-        ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ",mir.subdivType);
+        ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType);
         return;
     };
 
     std::unique_ptr<Subdivider> subd(Subdivider::Create(algo));
     ai_assert(subd);
-    if ( conv_data.meshes->empty() ) {
+    if (conv_data.meshes->empty()) {
         return;
     }
-    aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
-    std::unique_ptr<aiMesh*[]> tempmeshes(new aiMesh*[out.mNumMeshes]());
+    aiMesh **const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
+    std::unique_ptr<aiMesh *[]> tempmeshes(new aiMesh *[out.mNumMeshes]());
 
-    subd->Subdivide(meshes,out.mNumMeshes,tempmeshes.get(),std::max( mir.renderLevels, mir.levels ),true);
-    std::copy(tempmeshes.get(),tempmeshes.get()+out.mNumMeshes,meshes);
+    subd->Subdivide(meshes, out.mNumMeshes, tempmeshes.get(), std::max(mir.renderLevels, mir.levels), true);
+    std::copy(tempmeshes.get(), tempmeshes.get() + out.mNumMeshes, meshes);
 
     ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Subdivision` modifier to `",
-        orig_object.id.name,"`");
+            orig_object.id.name, "`");
 }
 
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 0 - 0
code/Blender/BlenderModifier.h → code/AssetLib/Blender/BlenderModifier.h


+ 838 - 0
code/AssetLib/Blender/BlenderScene.cpp

@@ -0,0 +1,838 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderScene.cpp
+ *  @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderScene.h"
+#include "BlenderCustomData.h"
+#include "BlenderDNA.h"
+#include "BlenderSceneGen.h"
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Object>(
+        Object &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Object::Type>(temp);
+    ReadFieldArray2<ErrorPolicy_Warn>(dest.obmat, "obmat", db);
+    ReadFieldArray2<ErrorPolicy_Warn>(dest.parentinv, "parentinv", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.parsubstr, "parsubstr", db);
+    {
+        std::shared_ptr<Object> parent;
+        ReadFieldPtr<ErrorPolicy_Warn>(parent, "*parent", db);
+        dest.parent = parent.get();
+    }
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.track, "*track", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy, "*proxy", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_from, "*proxy_from", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_group, "*proxy_group", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.dup_group, "*dup_group", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.data, "*data", db);
+    ReadField<ErrorPolicy_Igno>(dest.modifiers, "modifiers", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Group>(
+        Group &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Igno>(dest.layer, "layer", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.gobject, "*gobject", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTex>(
+        MTex &dest,
+        const FileDatabase &db) const {
+
+    int temp_short = 0;
+    ReadField<ErrorPolicy_Igno>(temp_short, "mapto", db);
+    dest.mapto = static_cast<Assimp::Blender::MTex::MapType>(temp_short);
+    int temp = 0;
+    ReadField<ErrorPolicy_Igno>(temp, "blendtype", db);
+    dest.blendtype = static_cast<Assimp::Blender::MTex::BlendType>(temp);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.object, "*object", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.tex, "*tex", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.uvname, "uvname", db);
+    ReadField<ErrorPolicy_Igno>(temp, "projx", db);
+    dest.projx = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(temp, "projy", db);
+    dest.projy = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(temp, "projz", db);
+    dest.projz = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(dest.mapping, "mapping", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.ofs, "ofs", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.size, "size", db);
+    ReadField<ErrorPolicy_Igno>(dest.rot, "rot", db);
+    ReadField<ErrorPolicy_Igno>(dest.texflag, "texflag", db);
+    ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
+    ReadField<ErrorPolicy_Igno>(dest.pmapto, "pmapto", db);
+    ReadField<ErrorPolicy_Igno>(dest.pmaptoneg, "pmaptoneg", db);
+    ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
+    ReadField<ErrorPolicy_Igno>(dest.colspecfac, "colspecfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirrfac, "mirrfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.alphafac, "alphafac", db);
+    ReadField<ErrorPolicy_Igno>(dest.difffac, "difffac", db);
+    ReadField<ErrorPolicy_Igno>(dest.specfac, "specfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.emitfac, "emitfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.hardfac, "hardfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.norfac, "norfac", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<TFace>(
+        TFace &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
+    ReadFieldArray<ErrorPolicy_Fail>(dest.col, "col", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+    ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<SubsurfModifierData>(
+        SubsurfModifierData &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
+    ReadField<ErrorPolicy_Warn>(dest.subdivType, "subdivType", db);
+    ReadField<ErrorPolicy_Fail>(dest.levels, "levels", db);
+    ReadField<ErrorPolicy_Igno>(dest.renderLevels, "renderLevels", db);
+    ReadField<ErrorPolicy_Igno>(dest.flags, "flags", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MFace>(
+        MFace &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
+    ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
+    ReadField<ErrorPolicy_Fail>(dest.v3, "v3", db);
+    ReadField<ErrorPolicy_Fail>(dest.v4, "v4", db);
+    ReadField<ErrorPolicy_Fail>(dest.mat_nr, "mat_nr", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Lamp>(
+        Lamp &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Lamp::Type>(temp);
+    ReadField<ErrorPolicy_Igno>(dest.flags, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
+    ReadField<ErrorPolicy_Igno>(dest.totex, "totex", db);
+    ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
+    ReadField<ErrorPolicy_Igno>(dest.energy, "energy", db);
+    ReadField<ErrorPolicy_Warn>(dest.dist, "dist", db);
+    ReadField<ErrorPolicy_Igno>(dest.spotsize, "spotsize", db);
+    ReadField<ErrorPolicy_Igno>(dest.spotblend, "spotblend", db);
+    ReadField<ErrorPolicy_Warn>(dest.constant_coefficient, "coeff_const", db);
+    ReadField<ErrorPolicy_Warn>(dest.linear_coefficient, "coeff_lin", db);
+    ReadField<ErrorPolicy_Warn>(dest.quadratic_coefficient, "coeff_quad", db);
+    ReadField<ErrorPolicy_Igno>(dest.att1, "att1", db);
+    ReadField<ErrorPolicy_Igno>(dest.att2, "att2", db);
+    ReadField<ErrorPolicy_Igno>(temp, "falloff_type", db);
+    dest.falloff_type = static_cast<Assimp::Blender::Lamp::FalloffType>(temp);
+    ReadField<ErrorPolicy_Igno>(dest.sun_brightness, "sun_brightness", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_size, "area_size", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_sizey, "area_sizey", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_sizez, "area_sizez", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_shape, "area_shape", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MDeformWeight>(
+        MDeformWeight &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.def_nr, "def_nr", db);
+    ReadField<ErrorPolicy_Fail>(dest.weight, "weight", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<PackedFile>(
+        PackedFile &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Warn>(dest.size, "size", db);
+    ReadField<ErrorPolicy_Warn>(dest.seek, "seek", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.data, "*data", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Base>(
+        Base &dest,
+        const FileDatabase &db) const {
+    // note: as per https://github.com/assimp/assimp/issues/128,
+    // reading the Object linked list recursively is prone to stack overflow.
+    // This structure converter is therefore an hand-written exception that
+    // does it iteratively.
+
+    const int initial_pos = db.reader->GetCurrentPos();
+
+    std::pair<Base *, int> todo = std::make_pair(&dest, initial_pos);
+    for (;;) {
+
+        Base &cur_dest = *todo.first;
+        db.reader->SetCurrentPos(todo.second);
+
+        // we know that this is a double-linked, circular list which we never
+        // traverse backwards, so don't bother resolving the back links.
+        cur_dest.prev = nullptr;
+
+        ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.object, "*object", db);
+
+        // the return value of ReadFieldPtr indicates whether the object
+        // was already cached. In this case, we don't need to resolve
+        // it again.
+        if (!ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.next, "*next", db, true) && cur_dest.next) {
+            todo = std::make_pair(&*cur_dest.next, db.reader->GetCurrentPos());
+            continue;
+        }
+        break;
+    }
+
+    db.reader->SetCurrentPos(initial_pos + size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTFace>(
+        MTFace &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+    ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Material>(
+        Material &dest,
+        const FileDatabase &db) const {
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Warn>(dest.specr, "specr", db);
+    ReadField<ErrorPolicy_Warn>(dest.specg, "specg", db);
+    ReadField<ErrorPolicy_Warn>(dest.specb, "specb", db);
+    ReadField<ErrorPolicy_Igno>(dest.har, "har", db);
+    ReadField<ErrorPolicy_Warn>(dest.ambr, "ambr", db);
+    ReadField<ErrorPolicy_Warn>(dest.ambg, "ambg", db);
+    ReadField<ErrorPolicy_Warn>(dest.ambb, "ambb", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirr, "mirr", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirg, "mirg", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirb, "mirb", db);
+    ReadField<ErrorPolicy_Warn>(dest.emit, "emit", db);
+    ReadField<ErrorPolicy_Igno>(dest.ray_mirror, "ray_mirror", db);
+    ReadField<ErrorPolicy_Warn>(dest.alpha, "alpha", db);
+    ReadField<ErrorPolicy_Igno>(dest.ref, "ref", db);
+    ReadField<ErrorPolicy_Igno>(dest.translucency, "translucency", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.roughness, "roughness", db);
+    ReadField<ErrorPolicy_Igno>(dest.darkness, "darkness", db);
+    ReadField<ErrorPolicy_Igno>(dest.refrac, "refrac", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.group, "*group", db);
+    ReadField<ErrorPolicy_Warn>(dest.diff_shader, "diff_shader", db);
+    ReadField<ErrorPolicy_Warn>(dest.spec_shader, "spec_shader", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mtex, "*mtex", db);
+
+    ReadField<ErrorPolicy_Igno>(dest.amb, "amb", db);
+    ReadField<ErrorPolicy_Igno>(dest.ang, "ang", db);
+    ReadField<ErrorPolicy_Igno>(dest.spectra, "spectra", db);
+    ReadField<ErrorPolicy_Igno>(dest.spec, "spec", db);
+    ReadField<ErrorPolicy_Igno>(dest.zoffs, "zoffs", db);
+    ReadField<ErrorPolicy_Igno>(dest.add, "add", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_mir, "fresnel_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_mir_i, "fresnel_mir_i", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_tra, "fresnel_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_tra_i, "fresnel_tra_i", db);
+    ReadField<ErrorPolicy_Igno>(dest.filter, "filter", db);
+    ReadField<ErrorPolicy_Igno>(dest.tx_limit, "tx_limit", db);
+    ReadField<ErrorPolicy_Igno>(dest.tx_falloff, "tx_falloff", db);
+    ReadField<ErrorPolicy_Igno>(dest.gloss_mir, "gloss_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.gloss_tra, "gloss_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_mir, "adapt_thresh_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_tra, "adapt_thresh_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.aniso_gloss_mir, "aniso_gloss_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.dist_mir, "dist_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.hasize, "hasize", db);
+    ReadField<ErrorPolicy_Igno>(dest.flaresize, "flaresize", db);
+    ReadField<ErrorPolicy_Igno>(dest.subsize, "subsize", db);
+    ReadField<ErrorPolicy_Igno>(dest.flareboost, "flareboost", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_sta, "strand_sta", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_end, "strand_end", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_ease, "strand_ease", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_surfnor, "strand_surfnor", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_min, "strand_min", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_widthfade, "strand_widthfade", db);
+    ReadField<ErrorPolicy_Igno>(dest.sbias, "sbias", db);
+    ReadField<ErrorPolicy_Igno>(dest.lbias, "lbias", db);
+    ReadField<ErrorPolicy_Igno>(dest.shad_alpha, "shad_alpha", db);
+    ReadField<ErrorPolicy_Igno>(dest.param, "param", db);
+    ReadField<ErrorPolicy_Igno>(dest.rms, "rms", db);
+    ReadField<ErrorPolicy_Igno>(dest.rampfac_col, "rampfac_col", db);
+    ReadField<ErrorPolicy_Igno>(dest.rampfac_spec, "rampfac_spec", db);
+    ReadField<ErrorPolicy_Igno>(dest.friction, "friction", db);
+    ReadField<ErrorPolicy_Igno>(dest.fh, "fh", db);
+    ReadField<ErrorPolicy_Igno>(dest.reflect, "reflect", db);
+    ReadField<ErrorPolicy_Igno>(dest.fhdist, "fhdist", db);
+    ReadField<ErrorPolicy_Igno>(dest.xyfrict, "xyfrict", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_radius, "sss_radius", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_col, "sss_col", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_error, "sss_error", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_scale, "sss_scale", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_ior, "sss_ior", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_colfac, "sss_colfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_texfac, "sss_texfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_front, "sss_front", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_back, "sss_back", db);
+
+    ReadField<ErrorPolicy_Igno>(dest.material_type, "material_type", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.ray_depth, "ray_depth", db);
+    ReadField<ErrorPolicy_Igno>(dest.ray_depth_tra, "ray_depth_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.samp_gloss_mir, "samp_gloss_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.samp_gloss_tra, "samp_gloss_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.fadeto_mir, "fadeto_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.shade_flag, "shade_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.flarec, "flarec", db);
+    ReadField<ErrorPolicy_Igno>(dest.starc, "starc", db);
+    ReadField<ErrorPolicy_Igno>(dest.linec, "linec", db);
+    ReadField<ErrorPolicy_Igno>(dest.ringc, "ringc", db);
+    ReadField<ErrorPolicy_Igno>(dest.pr_lamp, "pr_lamp", db);
+    ReadField<ErrorPolicy_Igno>(dest.pr_texture, "pr_texture", db);
+    ReadField<ErrorPolicy_Igno>(dest.ml_flag, "ml_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.diff_shader, "diff_shader", db);
+    ReadField<ErrorPolicy_Igno>(dest.spec_shader, "spec_shader", db);
+    ReadField<ErrorPolicy_Igno>(dest.texco, "texco", db);
+    ReadField<ErrorPolicy_Igno>(dest.mapto, "mapto", db);
+    ReadField<ErrorPolicy_Igno>(dest.ramp_show, "ramp_show", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad3, "pad3", db);
+    ReadField<ErrorPolicy_Igno>(dest.dynamode, "dynamode", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad2, "pad2", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_flag, "sss_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_preset, "sss_preset", db);
+    ReadField<ErrorPolicy_Igno>(dest.shadowonly_flag, "shadowonly_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.index, "index", db);
+    ReadField<ErrorPolicy_Igno>(dest.vcol_alpha, "vcol_alpha", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad4, "pad4", db);
+
+    ReadField<ErrorPolicy_Igno>(dest.seed1, "seed1", db);
+    ReadField<ErrorPolicy_Igno>(dest.seed2, "seed2", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTexPoly>(
+        MTexPoly &dest,
+        const FileDatabase &db) const {
+
+    {
+        std::shared_ptr<Image> tpage;
+        ReadFieldPtr<ErrorPolicy_Igno>(tpage, "*tpage", db);
+        dest.tpage = tpage.get();
+    }
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.transp, "transp", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Mesh>(
+        Mesh &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Fail>(dest.totface, "totface", db);
+    ReadField<ErrorPolicy_Fail>(dest.totedge, "totedge", db);
+    ReadField<ErrorPolicy_Fail>(dest.totvert, "totvert", db);
+    ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
+    ReadField<ErrorPolicy_Igno>(dest.totpoly, "totpoly", db);
+    ReadField<ErrorPolicy_Igno>(dest.subdiv, "subdiv", db);
+    ReadField<ErrorPolicy_Igno>(dest.subdivr, "subdivr", db);
+    ReadField<ErrorPolicy_Igno>(dest.subsurftype, "subsurftype", db);
+    ReadField<ErrorPolicy_Igno>(dest.smoothresh, "smoothresh", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.mface, "*mface", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mtface, "*mtface", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.tface, "*tface", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.mvert, "*mvert", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.medge, "*medge", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloop, "*mloop", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopuv, "*mloopuv", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopcol, "*mloopcol", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mpoly, "*mpoly", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mtpoly, "*mtpoly", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.dvert, "*dvert", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol, "*mcol", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.mat, "**mat", db);
+
+    ReadField<ErrorPolicy_Igno>(dest.vdata, "vdata", db);
+    ReadField<ErrorPolicy_Igno>(dest.edata, "edata", db);
+    ReadField<ErrorPolicy_Igno>(dest.fdata, "fdata", db);
+    ReadField<ErrorPolicy_Igno>(dest.pdata, "pdata", db);
+    ReadField<ErrorPolicy_Warn>(dest.ldata, "ldata", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MDeformVert>(
+        MDeformVert &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.dw, "*dw", db);
+    ReadField<ErrorPolicy_Igno>(dest.totweight, "totweight", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<World>(
+        World &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoopCol>(
+        MLoopCol &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Igno>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Igno>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Igno>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Igno>(dest.a, "a", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MVert>(
+        MVert &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray<ErrorPolicy_Fail>(dest.co, "co", db);
+    ReadFieldArray<ErrorPolicy_Fail>(dest.no, "no", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    //ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
+    ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MEdge>(
+        MEdge &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
+    ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
+    ReadField<ErrorPolicy_Igno>(dest.crease, "crease", db);
+    ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoopUV>(
+        MLoopUV &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray<ErrorPolicy_Igno>(dest.uv, "uv", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<GroupObject>(
+        GroupObject &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.prev, "*prev", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.ob, "*ob", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ListBase>(
+        ListBase &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.first, "*first", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.last, "*last", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoop>(
+        MLoop &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Igno>(dest.v, "v", db);
+    ReadField<ErrorPolicy_Igno>(dest.e, "e", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ModifierData>(
+        ModifierData &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.next, "*next", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.prev, "*prev", db);
+    ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.name, "name", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ID>(
+        ID &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MCol>(
+        MCol &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Fail>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Fail>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Fail>(dest.a, "a", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MPoly>(
+        MPoly &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Igno>(dest.loopstart, "loopstart", db);
+    ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
+    ReadField<ErrorPolicy_Igno>(dest.mat_nr, "mat_nr", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Scene>(
+        Scene &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.camera, "*camera", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.world, "*world", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.basact, "*basact", db);
+    ReadField<ErrorPolicy_Igno>(dest.base, "base", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Library>(
+        Library &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadFieldArray<ErrorPolicy_Fail>(dest.filename, "filename", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.parent, "*parent", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Tex>(
+        Tex &dest,
+        const FileDatabase &db) const {
+    short temp_short = 0;
+    ReadField<ErrorPolicy_Igno>(temp_short, "imaflag", db);
+    dest.imaflag = static_cast<Assimp::Blender::Tex::ImageFlags>(temp_short);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Tex::Type>(temp);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.ima, "*ima", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Camera>(
+        Camera &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Warn>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Camera::Type>(temp);
+    ReadField<ErrorPolicy_Warn>(temp, "flag", db);
+    dest.flag = static_cast<Assimp::Blender::Camera::Type>(temp);
+    ReadField<ErrorPolicy_Warn>(dest.lens, "lens", db);
+    ReadField<ErrorPolicy_Warn>(dest.sensor_x, "sensor_x", db);
+    ReadField<ErrorPolicy_Igno>(dest.clipsta, "clipsta", db);
+    ReadField<ErrorPolicy_Igno>(dest.clipend, "clipend", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MirrorModifierData>(
+        MirrorModifierData &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
+    ReadField<ErrorPolicy_Igno>(dest.axis, "axis", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.tolerance, "tolerance", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mirror_ob, "*mirror_ob", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Image>(
+        Image &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadField<ErrorPolicy_Igno>(dest.ok, "ok", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.source, "source", db);
+    ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad1, "pad1", db);
+    ReadField<ErrorPolicy_Igno>(dest.lastframe, "lastframe", db);
+    ReadField<ErrorPolicy_Igno>(dest.tpageflag, "tpageflag", db);
+    ReadField<ErrorPolicy_Igno>(dest.totbind, "totbind", db);
+    ReadField<ErrorPolicy_Igno>(dest.xrep, "xrep", db);
+    ReadField<ErrorPolicy_Igno>(dest.yrep, "yrep", db);
+    ReadField<ErrorPolicy_Igno>(dest.twsta, "twsta", db);
+    ReadField<ErrorPolicy_Igno>(dest.twend, "twend", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.packedfile, "*packedfile", db);
+    ReadField<ErrorPolicy_Igno>(dest.lastupdate, "lastupdate", db);
+    ReadField<ErrorPolicy_Igno>(dest.lastused, "lastused", db);
+    ReadField<ErrorPolicy_Igno>(dest.animspeed, "animspeed", db);
+    ReadField<ErrorPolicy_Igno>(dest.gen_x, "gen_x", db);
+    ReadField<ErrorPolicy_Igno>(dest.gen_y, "gen_y", db);
+    ReadField<ErrorPolicy_Igno>(dest.gen_type, "gen_type", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CustomData>(
+        CustomData &dest,
+        const FileDatabase &db) const {
+    ReadFieldArray<ErrorPolicy_Warn>(dest.typemap, "typemap", db);
+    ReadField<ErrorPolicy_Warn>(dest.totlayer, "totlayer", db);
+    ReadField<ErrorPolicy_Warn>(dest.maxlayer, "maxlayer", db);
+    ReadField<ErrorPolicy_Warn>(dest.totsize, "totsize", db);
+    ReadFieldPtrVector<ErrorPolicy_Warn>(dest.layers, "*layers", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CustomDataLayer>(
+        CustomDataLayer &dest,
+        const FileDatabase &db) const {
+    ReadField<ErrorPolicy_Fail>(dest.type, "type", db);
+    ReadField<ErrorPolicy_Fail>(dest.offset, "offset", db);
+    ReadField<ErrorPolicy_Fail>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Fail>(dest.active, "active", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_rnd, "active_rnd", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_clone, "active_clone", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_mask, "active_mask", db);
+    ReadField<ErrorPolicy_Fail>(dest.uid, "uid", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadCustomDataPtr<ErrorPolicy_Fail>(dest.data, dest.type, "*data", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+void DNA::RegisterConverters() {
+
+    converters["Object"] = DNA::FactoryPair(&Structure::Allocate<Object>, &Structure::Convert<Object>);
+    converters["Group"] = DNA::FactoryPair(&Structure::Allocate<Group>, &Structure::Convert<Group>);
+    converters["MTex"] = DNA::FactoryPair(&Structure::Allocate<MTex>, &Structure::Convert<MTex>);
+    converters["TFace"] = DNA::FactoryPair(&Structure::Allocate<TFace>, &Structure::Convert<TFace>);
+    converters["SubsurfModifierData"] = DNA::FactoryPair(&Structure::Allocate<SubsurfModifierData>, &Structure::Convert<SubsurfModifierData>);
+    converters["MFace"] = DNA::FactoryPair(&Structure::Allocate<MFace>, &Structure::Convert<MFace>);
+    converters["Lamp"] = DNA::FactoryPair(&Structure::Allocate<Lamp>, &Structure::Convert<Lamp>);
+    converters["MDeformWeight"] = DNA::FactoryPair(&Structure::Allocate<MDeformWeight>, &Structure::Convert<MDeformWeight>);
+    converters["PackedFile"] = DNA::FactoryPair(&Structure::Allocate<PackedFile>, &Structure::Convert<PackedFile>);
+    converters["Base"] = DNA::FactoryPair(&Structure::Allocate<Base>, &Structure::Convert<Base>);
+    converters["MTFace"] = DNA::FactoryPair(&Structure::Allocate<MTFace>, &Structure::Convert<MTFace>);
+    converters["Material"] = DNA::FactoryPair(&Structure::Allocate<Material>, &Structure::Convert<Material>);
+    converters["MTexPoly"] = DNA::FactoryPair(&Structure::Allocate<MTexPoly>, &Structure::Convert<MTexPoly>);
+    converters["Mesh"] = DNA::FactoryPair(&Structure::Allocate<Mesh>, &Structure::Convert<Mesh>);
+    converters["MDeformVert"] = DNA::FactoryPair(&Structure::Allocate<MDeformVert>, &Structure::Convert<MDeformVert>);
+    converters["World"] = DNA::FactoryPair(&Structure::Allocate<World>, &Structure::Convert<World>);
+    converters["MLoopCol"] = DNA::FactoryPair(&Structure::Allocate<MLoopCol>, &Structure::Convert<MLoopCol>);
+    converters["MVert"] = DNA::FactoryPair(&Structure::Allocate<MVert>, &Structure::Convert<MVert>);
+    converters["MEdge"] = DNA::FactoryPair(&Structure::Allocate<MEdge>, &Structure::Convert<MEdge>);
+    converters["MLoopUV"] = DNA::FactoryPair(&Structure::Allocate<MLoopUV>, &Structure::Convert<MLoopUV>);
+    converters["GroupObject"] = DNA::FactoryPair(&Structure::Allocate<GroupObject>, &Structure::Convert<GroupObject>);
+    converters["ListBase"] = DNA::FactoryPair(&Structure::Allocate<ListBase>, &Structure::Convert<ListBase>);
+    converters["MLoop"] = DNA::FactoryPair(&Structure::Allocate<MLoop>, &Structure::Convert<MLoop>);
+    converters["ModifierData"] = DNA::FactoryPair(&Structure::Allocate<ModifierData>, &Structure::Convert<ModifierData>);
+    converters["ID"] = DNA::FactoryPair(&Structure::Allocate<ID>, &Structure::Convert<ID>);
+    converters["MCol"] = DNA::FactoryPair(&Structure::Allocate<MCol>, &Structure::Convert<MCol>);
+    converters["MPoly"] = DNA::FactoryPair(&Structure::Allocate<MPoly>, &Structure::Convert<MPoly>);
+    converters["Scene"] = DNA::FactoryPair(&Structure::Allocate<Scene>, &Structure::Convert<Scene>);
+    converters["Library"] = DNA::FactoryPair(&Structure::Allocate<Library>, &Structure::Convert<Library>);
+    converters["Tex"] = DNA::FactoryPair(&Structure::Allocate<Tex>, &Structure::Convert<Tex>);
+    converters["Camera"] = DNA::FactoryPair(&Structure::Allocate<Camera>, &Structure::Convert<Camera>);
+    converters["MirrorModifierData"] = DNA::FactoryPair(&Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData>);
+    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>);
+}
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 252 - 277
code/Blender/BlenderScene.h → code/AssetLib/Blender/BlenderScene.h

@@ -48,14 +48,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "BlenderDNA.h"
 
-namespace Assimp    {
+namespace Assimp {
 namespace Blender {
 
 // Minor parts of this file are extracts from blender data structures,
 // declared in the ./source/blender/makesdna directory.
 // Stuff that is not used by Assimp is commented.
 
-
 // NOTE
 // this file serves as input data to the `./scripts/genblenddna.py`
 // script. This script generates the actual binding code to read a
@@ -95,15 +94,15 @@ namespace Blender {
 
 // warn if field is missing, substitute default value
 #ifdef WARN
-#  undef WARN
+#undef WARN
 #endif
-#define WARN 
+#define WARN
 
 // fail the import if the field does not exist
 #ifdef FAIL
-#  undef FAIL
+#undef FAIL
 #endif
-#define FAIL 
+#define FAIL
 
 struct Object;
 struct MTex;
@@ -117,7 +116,7 @@ static const size_t MaxNameLen = 1024;
 
 // -------------------------------------------------------------------------------
 struct ID : ElemBase {
-    char name[ MaxNameLen ] WARN;
+    char name[MaxNameLen] WARN;
     short flag;
 };
 
@@ -127,17 +126,16 @@ struct ListBase : ElemBase {
     std::shared_ptr<ElemBase> last;
 };
 
-
 // -------------------------------------------------------------------------------
 struct PackedFile : ElemBase {
-     int size WARN;
-     int seek WARN;
-     std::shared_ptr< FileOffset > data WARN;
+    int size WARN;
+    int seek WARN;
+    std::shared_ptr<FileOffset> data WARN;
 };
 
 // -------------------------------------------------------------------------------
 struct GroupObject : ElemBase {
-    std::shared_ptr<GroupObject> prev,next FAIL;
+    std::shared_ptr<GroupObject> prev, next FAIL;
     std::shared_ptr<Object> ob;
 };
 
@@ -157,23 +155,20 @@ struct World : ElemBase {
 // -------------------------------------------------------------------------------
 struct MVert : ElemBase {
     float co[3] FAIL;
-    float no[3] FAIL;       // readed as short and divided through / 32767.f
+    float no[3] FAIL; // readed as short and divided through / 32767.f
     char flag;
     int mat_nr WARN;
     int bweight;
 
-    MVert() : ElemBase()
-        , flag(0)
-        , mat_nr(0)
-        , bweight(0)
-    {}
+    MVert() :
+            ElemBase(), flag(0), mat_nr(0), bweight(0) {}
 };
 
 // -------------------------------------------------------------------------------
 struct MEdge : ElemBase {
-      int v1, v2 FAIL;
-      char crease, bweight;
-      short flag;
+    int v1, v2 FAIL;
+    char crease, bweight;
+    short flag;
 };
 
 // -------------------------------------------------------------------------------
@@ -190,7 +185,7 @@ struct MLoopUV : ElemBase {
 // -------------------------------------------------------------------------------
 // Note that red and blue are not swapped, as with MCol
 struct MLoopCol : ElemBase {
-	unsigned char r, g, b, a;
+    unsigned char r, g, b, a;
 };
 
 // -------------------------------------------------------------------------------
@@ -203,19 +198,19 @@ struct MPoly : ElemBase {
 
 // -------------------------------------------------------------------------------
 struct MTexPoly : ElemBase {
-    Image* tpage;
+    Image *tpage;
     char flag, transp;
     short mode, tile, pad;
 };
 
 // -------------------------------------------------------------------------------
 struct MCol : ElemBase {
-    char r,g,b,a FAIL;
+    char r, g, b, a FAIL;
 };
 
 // -------------------------------------------------------------------------------
 struct MFace : ElemBase {
-    int v1,v2,v3,v4 FAIL;
+    int v1, v2, v3, v4 FAIL;
     int mat_nr FAIL;
     char flag;
 };
@@ -232,13 +227,9 @@ struct TFace : ElemBase {
 
 // -------------------------------------------------------------------------------
 struct MTFace : ElemBase {
-	MTFace()
-	: flag(0)
-	, mode(0)
-	, tile(0)
-	, unwrap(0)
-	{
-	}
+    MTFace() :
+            flag(0), mode(0), tile(0), unwrap(0) {
+    }
 
     float uv[4][2] FAIL;
     char flag;
@@ -250,31 +241,31 @@ struct MTFace : ElemBase {
 };
 
 // -------------------------------------------------------------------------------
-struct MDeformWeight : ElemBase  {
-      int    def_nr FAIL;
-      float  weight FAIL;
+struct MDeformWeight : ElemBase {
+    int def_nr FAIL;
+    float weight FAIL;
 };
 
 // -------------------------------------------------------------------------------
-struct MDeformVert : ElemBase  {
+struct MDeformVert : ElemBase {
     vector<MDeformWeight> dw WARN;
     int totweight;
 };
 
 // -------------------------------------------------------------------------------
-#define MA_RAYMIRROR    0x40000
+#define MA_RAYMIRROR 0x40000
 #define MA_TRANSPARENCY 0x10000
-#define MA_RAYTRANSP    0x20000
-#define MA_ZTRANSP      0x00040
+#define MA_RAYTRANSP 0x20000
+#define MA_ZTRANSP 0x00040
 
 struct Material : ElemBase {
     ID id FAIL;
 
-    float r,g,b WARN;
-    float specr,specg,specb WARN;
+    float r, g, b WARN;
+    float specr, specg, specb WARN;
     short har;
-    float ambr,ambg,ambb WARN;
-    float mirr,mirg,mirb;
+    float ambr, ambg, ambb WARN;
+    float mirr, mirg, mirb;
     float emit WARN;
     float ray_mirror;
     float alpha WARN;
@@ -399,20 +390,19 @@ struct CustomDataLayer : ElemBase {
     int active_mask;
     int uid;
     char name[64];
-    std::shared_ptr<ElemBase> data;     // must be converted to real type according type member
-
-    CustomDataLayer()
-        : ElemBase()
-        , type(0)
-        , offset(0)
-        , flag(0)
-        , active(0)
-        , active_rnd(0)
-        , active_clone(0)
-        , active_mask(0)
-        , uid(0)
-        , data(nullptr)
-    {
+    std::shared_ptr<ElemBase> data; // must be converted to real type according type member
+
+    CustomDataLayer() :
+            ElemBase(),
+            type(0),
+            offset(0),
+            flag(0),
+            active(0),
+            active_rnd(0),
+            active_clone(0),
+            active_mask(0),
+            uid(0),
+            data(nullptr) {
         memset(name, 0, sizeof name);
     }
 };
@@ -430,8 +420,8 @@ CustomData 208
     CustomDataExternal *external 200 8
 */
 struct CustomData : ElemBase {
-    vector<std::shared_ptr<struct CustomDataLayer> > layers;
-    int typemap[42];    // CD_NUMTYPES
+    vector<std::shared_ptr<struct CustomDataLayer>> layers;
+    int typemap[42]; // CD_NUMTYPES
     int totlayer;
     int maxlayer;
     int totsize;
@@ -469,7 +459,7 @@ struct Mesh : ElemBase {
     vector<MDeformVert> dvert;
     vector<MCol> mcol;
 
-    vector< std::shared_ptr<Material> > mat FAIL;
+    vector<std::shared_ptr<Material>> mat FAIL;
 
     struct CustomData vdata;
     struct CustomData edata;
@@ -490,142 +480,141 @@ struct Library : ElemBase {
 // -------------------------------------------------------------------------------
 struct Camera : ElemBase {
     enum Type {
-          Type_PERSP    =   0
-         ,Type_ORTHO    =   1
+        Type_PERSP = 0,
+        Type_ORTHO = 1
     };
 
     ID id FAIL;
 
-    Type type,flag WARN;
+    Type type, flag WARN;
     float lens WARN;
     float sensor_x WARN;
     float clipsta, clipend;
 };
 
-
 // -------------------------------------------------------------------------------
 struct Lamp : ElemBase {
 
     enum FalloffType {
-         FalloffType_Constant   = 0x0
-        ,FalloffType_InvLinear  = 0x1
-        ,FalloffType_InvSquare  = 0x2
+        FalloffType_Constant = 0x0,
+        FalloffType_InvLinear = 0x1,
+        FalloffType_InvSquare = 0x2
         //,FalloffType_Curve    = 0x3
         //,FalloffType_Sliders  = 0x4
     };
 
     enum Type {
-         Type_Local         = 0x0
-        ,Type_Sun           = 0x1
-        ,Type_Spot          = 0x2
-        ,Type_Hemi          = 0x3
-        ,Type_Area          = 0x4
+        Type_Local = 0x0,
+        Type_Sun = 0x1,
+        Type_Spot = 0x2,
+        Type_Hemi = 0x3,
+        Type_Area = 0x4
         //,Type_YFPhoton    = 0x5
     };
 
-      ID id FAIL;
-      //AnimData *adt;
+    ID id FAIL;
+    //AnimData *adt;
 
-      Type type FAIL;
-      short flags;
+    Type type FAIL;
+    short flags;
 
-      //int mode;
+    //int mode;
 
-      short colormodel, totex;
-      float r,g,b,k WARN;
-      //float shdwr, shdwg, shdwb;
+    short colormodel, totex;
+    float r, g, b, k WARN;
+    //float shdwr, shdwg, shdwb;
 
-      float energy, dist, spotsize, spotblend;
-      //float haint;
+    float energy, dist, spotsize, spotblend;
+    //float haint;
 
-      float constant_coefficient;
-      float linear_coefficient;
-      float quadratic_coefficient;
+    float constant_coefficient;
+    float linear_coefficient;
+    float quadratic_coefficient;
 
-      float att1, att2;
-      //struct CurveMapping *curfalloff;
-      FalloffType falloff_type;
+    float att1, att2;
+    //struct CurveMapping *curfalloff;
+    FalloffType falloff_type;
 
-      //float clipsta, clipend, shadspotsize;
-      //float bias, soft, compressthresh;
-      //short bufsize, samp, buffers, filtertype;
-      //char bufflag, buftype;
+    //float clipsta, clipend, shadspotsize;
+    //float bias, soft, compressthresh;
+    //short bufsize, samp, buffers, filtertype;
+    //char bufflag, buftype;
 
-      //short ray_samp, ray_sampy, ray_sampz;
-      //short ray_samp_type;
-      short area_shape;
-      float area_size, area_sizey, area_sizez;
-      //float adapt_thresh;
-      //short ray_samp_method;
+    //short ray_samp, ray_sampy, ray_sampz;
+    //short ray_samp_type;
+    short area_shape;
+    float area_size, area_sizey, area_sizez;
+    //float adapt_thresh;
+    //short ray_samp_method;
 
-      //short texact, shadhalostep;
+    //short texact, shadhalostep;
 
-      //short sun_effect_type;
-      //short skyblendtype;
-      //float horizon_brightness;
-      //float spread;
-      float sun_brightness;
-      //float sun_size;
-      //float backscattered_light;
-      //float sun_intensity;
-      //float atm_turbidity;
-      //float atm_inscattering_factor;
-      //float atm_extinction_factor;
-      //float atm_distance_factor;
-      //float skyblendfac;
-      //float sky_exposure;
-      //short sky_colorspace;
+    //short sun_effect_type;
+    //short skyblendtype;
+    //float horizon_brightness;
+    //float spread;
+    float sun_brightness;
+    //float sun_size;
+    //float backscattered_light;
+    //float sun_intensity;
+    //float atm_turbidity;
+    //float atm_inscattering_factor;
+    //float atm_extinction_factor;
+    //float atm_distance_factor;
+    //float skyblendfac;
+    //float sky_exposure;
+    //short sky_colorspace;
 
-      // int YF_numphotons, YF_numsearch;
-      // short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
-      // float YF_causticblur, YF_ltradius;
+    // int YF_numphotons, YF_numsearch;
+    // short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
+    // float YF_causticblur, YF_ltradius;
 
-      // float YF_glowint, YF_glowofs;
-      // short YF_glowtype, YF_pad2;
+    // float YF_glowint, YF_glowofs;
+    // short YF_glowtype, YF_pad2;
 
-      //struct Ipo *ipo;
-      //struct MTex *mtex[18];
-      // short pr_texture;
+    //struct Ipo *ipo;
+    //struct MTex *mtex[18];
+    // short pr_texture;
 
-      //struct PreviewImage *preview;
+    //struct PreviewImage *preview;
 };
 
 // -------------------------------------------------------------------------------
-struct ModifierData : ElemBase  {
+struct ModifierData : ElemBase {
     enum ModifierType {
-      eModifierType_None = 0,
-      eModifierType_Subsurf,
-      eModifierType_Lattice,
-      eModifierType_Curve,
-      eModifierType_Build,
-      eModifierType_Mirror,
-      eModifierType_Decimate,
-      eModifierType_Wave,
-      eModifierType_Armature,
-      eModifierType_Hook,
-      eModifierType_Softbody,
-      eModifierType_Boolean,
-      eModifierType_Array,
-      eModifierType_EdgeSplit,
-      eModifierType_Displace,
-      eModifierType_UVProject,
-      eModifierType_Smooth,
-      eModifierType_Cast,
-      eModifierType_MeshDeform,
-      eModifierType_ParticleSystem,
-      eModifierType_ParticleInstance,
-      eModifierType_Explode,
-      eModifierType_Cloth,
-      eModifierType_Collision,
-      eModifierType_Bevel,
-      eModifierType_Shrinkwrap,
-      eModifierType_Fluidsim,
-      eModifierType_Mask,
-      eModifierType_SimpleDeform,
-      eModifierType_Multires,
-      eModifierType_Surface,
-      eModifierType_Smoke,
-      eModifierType_ShapeKey
+        eModifierType_None = 0,
+        eModifierType_Subsurf,
+        eModifierType_Lattice,
+        eModifierType_Curve,
+        eModifierType_Build,
+        eModifierType_Mirror,
+        eModifierType_Decimate,
+        eModifierType_Wave,
+        eModifierType_Armature,
+        eModifierType_Hook,
+        eModifierType_Softbody,
+        eModifierType_Boolean,
+        eModifierType_Array,
+        eModifierType_EdgeSplit,
+        eModifierType_Displace,
+        eModifierType_UVProject,
+        eModifierType_Smooth,
+        eModifierType_Cast,
+        eModifierType_MeshDeform,
+        eModifierType_ParticleSystem,
+        eModifierType_ParticleInstance,
+        eModifierType_Explode,
+        eModifierType_Cloth,
+        eModifierType_Collision,
+        eModifierType_Bevel,
+        eModifierType_Shrinkwrap,
+        eModifierType_Fluidsim,
+        eModifierType_Mask,
+        eModifierType_SimpleDeform,
+        eModifierType_Multires,
+        eModifierType_Surface,
+        eModifierType_Smoke,
+        eModifierType_ShapeKey
     };
 
     std::shared_ptr<ElemBase> next WARN;
@@ -636,7 +625,7 @@ struct ModifierData : ElemBase  {
 };
 
 // -------------------------------------------------------------------------------
-struct SubsurfModifierData : ElemBase  {
+struct SubsurfModifierData : ElemBase {
 
     enum Type {
 
@@ -646,13 +635,13 @@ struct SubsurfModifierData : ElemBase  {
 
     enum Flags {
         // some omitted
-        FLAGS_SubsurfUV     =1<<3
+        FLAGS_SubsurfUV = 1 << 3
     };
 
     ModifierData modifier FAIL;
     short subdivType WARN;
     short levels FAIL;
-    short renderLevels ;
+    short renderLevels;
     short flags;
 };
 
@@ -660,13 +649,13 @@ struct SubsurfModifierData : ElemBase  {
 struct MirrorModifierData : ElemBase {
 
     enum Flags {
-        Flags_CLIPPING      =1<<0,
-        Flags_MIRROR_U      =1<<1,
-        Flags_MIRROR_V      =1<<2,
-        Flags_AXIS_X        =1<<3,
-        Flags_AXIS_Y        =1<<4,
-        Flags_AXIS_Z        =1<<5,
-        Flags_VGROUP        =1<<6
+        Flags_CLIPPING = 1 << 0,
+        Flags_MIRROR_U = 1 << 1,
+        Flags_MIRROR_V = 1 << 2,
+        Flags_AXIS_X = 1 << 3,
+        Flags_AXIS_Y = 1 << 4,
+        Flags_AXIS_Z = 1 << 5,
+        Flags_VGROUP = 1 << 6
     };
 
     ModifierData modifier FAIL;
@@ -677,22 +666,24 @@ struct MirrorModifierData : ElemBase {
 };
 
 // -------------------------------------------------------------------------------
-struct Object : ElemBase  {
+struct Object : ElemBase {
     ID id FAIL;
 
     enum Type {
-         Type_EMPTY     =   0
-        ,Type_MESH      =   1
-        ,Type_CURVE     =   2
-        ,Type_SURF      =   3
-        ,Type_FONT      =   4
-        ,Type_MBALL     =   5
-
-        ,Type_LAMP      =   10
-        ,Type_CAMERA    =   11
-
-        ,Type_WAVE      =   21
-        ,Type_LATTICE   =   22
+        Type_EMPTY = 0,
+        Type_MESH = 1,
+        Type_CURVE = 2,
+        Type_SURF = 3,
+        Type_FONT = 4,
+        Type_MBALL = 5
+
+        ,
+        Type_LAMP = 10,
+        Type_CAMERA = 11
+
+        ,
+        Type_WAVE = 21,
+        Type_LATTICE = 22
     };
 
     Type type FAIL;
@@ -700,39 +691,29 @@ struct Object : ElemBase  {
     float parentinv[4][4] WARN;
     char parsubstr[32] WARN;
 
-    Object* parent WARN;
+    Object *parent WARN;
     std::shared_ptr<Object> track WARN;
 
-    std::shared_ptr<Object> proxy,proxy_from,proxy_group WARN;
+    std::shared_ptr<Object> proxy, proxy_from, proxy_group WARN;
     std::shared_ptr<Group> dup_group WARN;
     std::shared_ptr<ElemBase> data FAIL;
 
     ListBase modifiers;
 
-    Object()
-    : ElemBase()
-    , type( Type_EMPTY )
-    , parent( nullptr )
-    , track()
-    , proxy()
-    , proxy_from()
-    , data() {
+    Object() :
+            ElemBase(), type(Type_EMPTY), parent(nullptr), track(), proxy(), proxy_from(), data() {
         // empty
     }
 };
 
-
 // -------------------------------------------------------------------------------
 struct Base : ElemBase {
-    Base* prev WARN;
+    Base *prev WARN;
     std::shared_ptr<Base> next WARN;
     std::shared_ptr<Object> object WARN;
 
-    Base() 
-    : ElemBase()
-    , prev( nullptr )
-    , next()
-    , object() {
+    Base() :
+            ElemBase(), prev(nullptr), next(), object() {
         // empty
         // empty
     }
@@ -748,11 +729,8 @@ struct Scene : ElemBase {
 
     ListBase base;
 
-    Scene()
-    : ElemBase()
-    , camera()
-    , world()
-    , basact() {
+    Scene() :
+            ElemBase(), camera(), world(), basact() {
         // empty
     }
 };
@@ -783,9 +761,9 @@ struct Image : ElemBase {
     short animspeed;
 
     short gen_x, gen_y, gen_type;
-    
-    Image()
-    : ElemBase() {
+
+    Image() :
+            ElemBase() {
         // empty
     }
 };
@@ -795,33 +773,33 @@ struct Tex : ElemBase {
 
     // actually, the only texture type we support is Type_IMAGE
     enum Type {
-         Type_CLOUDS        = 1
-        ,Type_WOOD          = 2
-        ,Type_MARBLE        = 3
-        ,Type_MAGIC         = 4
-        ,Type_BLEND         = 5
-        ,Type_STUCCI        = 6
-        ,Type_NOISE         = 7
-        ,Type_IMAGE         = 8
-        ,Type_PLUGIN        = 9
-        ,Type_ENVMAP        = 10
-        ,Type_MUSGRAVE      = 11
-        ,Type_VORONOI       = 12
-        ,Type_DISTNOISE     = 13
-        ,Type_POINTDENSITY  = 14
-        ,Type_VOXELDATA     = 15
+        Type_CLOUDS = 1,
+        Type_WOOD = 2,
+        Type_MARBLE = 3,
+        Type_MAGIC = 4,
+        Type_BLEND = 5,
+        Type_STUCCI = 6,
+        Type_NOISE = 7,
+        Type_IMAGE = 8,
+        Type_PLUGIN = 9,
+        Type_ENVMAP = 10,
+        Type_MUSGRAVE = 11,
+        Type_VORONOI = 12,
+        Type_DISTNOISE = 13,
+        Type_POINTDENSITY = 14,
+        Type_VOXELDATA = 15
     };
 
     enum ImageFlags {
-         ImageFlags_INTERPOL         = 1
-        ,ImageFlags_USEALPHA         = 2
-        ,ImageFlags_MIPMAP           = 4
-        ,ImageFlags_IMAROT           = 16
-        ,ImageFlags_CALCALPHA        = 32
-        ,ImageFlags_NORMALMAP        = 2048
-        ,ImageFlags_GAUSS_MIP        = 4096
-        ,ImageFlags_FILTER_MIN       = 8192
-        ,ImageFlags_DERIVATIVEMAP   = 16384
+        ImageFlags_INTERPOL = 1,
+        ImageFlags_USEALPHA = 2,
+        ImageFlags_MIPMAP = 4,
+        ImageFlags_IMAROT = 16,
+        ImageFlags_CALCALPHA = 32,
+        ImageFlags_NORMALMAP = 2048,
+        ImageFlags_GAUSS_MIP = 4096,
+        ImageFlags_FILTER_MIN = 8192,
+        ImageFlags_DERIVATIVEMAP = 16384
     };
 
     ID id FAIL;
@@ -876,11 +854,8 @@ struct Tex : ElemBase {
 
     //char use_nodes;
 
-    Tex()
-    : ElemBase()
-    , imaflag( ImageFlags_INTERPOL )
-    , type( Type_CLOUDS )
-    , ima() {
+    Tex() :
+            ElemBase(), imaflag(ImageFlags_INTERPOL), type(Type_CLOUDS), ima() {
         // empty
     }
 };
@@ -889,52 +864,52 @@ struct Tex : ElemBase {
 struct MTex : ElemBase {
 
     enum Projection {
-         Proj_N = 0
-        ,Proj_X = 1
-        ,Proj_Y = 2
-        ,Proj_Z = 3
+        Proj_N = 0,
+        Proj_X = 1,
+        Proj_Y = 2,
+        Proj_Z = 3
     };
 
     enum Flag {
-         Flag_RGBTOINT      = 0x1
-        ,Flag_STENCIL       = 0x2
-        ,Flag_NEGATIVE      = 0x4
-        ,Flag_ALPHAMIX      = 0x8
-        ,Flag_VIEWSPACE     = 0x10
+        Flag_RGBTOINT = 0x1,
+        Flag_STENCIL = 0x2,
+        Flag_NEGATIVE = 0x4,
+        Flag_ALPHAMIX = 0x8,
+        Flag_VIEWSPACE = 0x10
     };
 
     enum BlendType {
-         BlendType_BLEND            = 0
-        ,BlendType_MUL              = 1
-        ,BlendType_ADD              = 2
-        ,BlendType_SUB              = 3
-        ,BlendType_DIV              = 4
-        ,BlendType_DARK             = 5
-        ,BlendType_DIFF             = 6
-        ,BlendType_LIGHT            = 7
-        ,BlendType_SCREEN           = 8
-        ,BlendType_OVERLAY          = 9
-        ,BlendType_BLEND_HUE        = 10
-        ,BlendType_BLEND_SAT        = 11
-        ,BlendType_BLEND_VAL        = 12
-        ,BlendType_BLEND_COLOR      = 13
+        BlendType_BLEND = 0,
+        BlendType_MUL = 1,
+        BlendType_ADD = 2,
+        BlendType_SUB = 3,
+        BlendType_DIV = 4,
+        BlendType_DARK = 5,
+        BlendType_DIFF = 6,
+        BlendType_LIGHT = 7,
+        BlendType_SCREEN = 8,
+        BlendType_OVERLAY = 9,
+        BlendType_BLEND_HUE = 10,
+        BlendType_BLEND_SAT = 11,
+        BlendType_BLEND_VAL = 12,
+        BlendType_BLEND_COLOR = 13
     };
 
     enum MapType {
-         MapType_COL         = 1
-        ,MapType_NORM        = 2
-        ,MapType_COLSPEC     = 4
-        ,MapType_COLMIR      = 8
-        ,MapType_REF         = 16
-        ,MapType_SPEC        = 32
-        ,MapType_EMIT        = 64
-        ,MapType_ALPHA       = 128
-        ,MapType_HAR         = 256
-        ,MapType_RAYMIRR     = 512
-        ,MapType_TRANSLU     = 1024
-        ,MapType_AMB         = 2048
-        ,MapType_DISPLACE    = 4096
-        ,MapType_WARP        = 8192
+        MapType_COL = 1,
+        MapType_NORM = 2,
+        MapType_COLSPEC = 4,
+        MapType_COLMIR = 8,
+        MapType_REF = 16,
+        MapType_SPEC = 32,
+        MapType_EMIT = 64,
+        MapType_ALPHA = 128,
+        MapType_HAR = 256,
+        MapType_RAYMIRR = 512,
+        MapType_TRANSLU = 1024,
+        MapType_AMB = 2048,
+        MapType_DISPLACE = 4096,
+        MapType_WARP = 8192
     };
 
     // short texco, maptoneg;
@@ -945,7 +920,7 @@ struct MTex : ElemBase {
     std::shared_ptr<Tex> tex;
     char uvname[32];
 
-    Projection projx,projy,projz;
+    Projection projx, projy, projz;
     char mapping;
     float ofs[3], size[3], rot;
 
@@ -953,7 +928,7 @@ struct MTex : ElemBase {
     short colormodel, pmapto, pmaptoneg;
     //short normapspace, which_output;
     //char brush_map_mode;
-    float r,g,b,k WARN;
+    float r, g, b, k WARN;
     //float def_var, rt;
 
     //float colfac, varfac;
@@ -972,12 +947,12 @@ struct MTex : ElemBase {
     //float shadowfac;
     //float zenupfac, zendownfac, blendfac;
 
-    MTex()
-    : ElemBase() {
+    MTex() :
+            ElemBase() {
         // empty
     }
 };
 
-}
-}
+} // namespace Blender
+} // namespace Assimp
 #endif

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


+ 0 - 0
code/Blender/BlenderTessellator.cpp → code/AssetLib/Blender/BlenderTessellator.cpp


+ 0 - 0
code/Blender/BlenderTessellator.h → code/AssetLib/Blender/BlenderTessellator.h


+ 0 - 0
code/C4D/C4DImporter.cpp → code/AssetLib/C4D/C4DImporter.cpp


+ 0 - 0
code/C4D/C4DImporter.h → code/AssetLib/C4D/C4DImporter.h


文件差异内容过多而无法显示
+ 248 - 287
code/AssetLib/COB/COBLoader.cpp


+ 0 - 0
code/COB/COBLoader.h → code/AssetLib/COB/COBLoader.h


+ 2 - 2
code/COB/COBScene.h → code/AssetLib/COB/COBScene.h

@@ -75,10 +75,10 @@ struct Face
 
 // ------------------
 /** COB chunk header information */
+const unsigned int NO_SIZE = UINT_MAX;
+
 struct ChunkInfo
 {
-    enum {NO_SIZE=UINT_MAX};
-
     ChunkInfo ()
         :   id        (0)
         ,   parent_id (0)

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

@@ -178,7 +178,7 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                         *ot++ = *buffer++;
 
                     *ot = '\0';
-                    nda->mNodeName.length = (ai_uint32)(ot-nda->mNodeName.data);
+                    nda->mNodeName.length = static_cast<ai_uint32>(ot-nda->mNodeName.data);
                 }
 
                 anim->mNumChannels = static_cast<unsigned int>(anims_temp.size());

+ 0 - 0
code/CSM/CSMLoader.h → code/AssetLib/CSM/CSMLoader.h


+ 1748 - 0
code/AssetLib/Collada/ColladaExporter.cpp

@@ -0,0 +1,1748 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
+
+#include "ColladaExporter.h"
+
+#include <assimp/Bitmap.h>
+#include <assimp/ColladaMetaData.h>
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/Exceptional.h>
+#include <assimp/MathFunctions.h>
+#include <assimp/SceneCombiner.h>
+#include <assimp/StringUtils.h>
+#include <assimp/XMLTools.h>
+#include <assimp/commonMetaData.h>
+#include <assimp/fast_atof.h>
+#include <assimp/scene.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <ctime>
+#include <memory>
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
+void ExportSceneCollada(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
+    std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
+
+    // invoke the exporter
+    ColladaExporter iDoTheExportThing(pScene, pIOSystem, path, file);
+
+    if (iDoTheExportThing.mOutput.fail()) {
+        throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
+    }
+
+    // we're still here - export successfully completed. Write result to the given IOSYstem
+    std::unique_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wt"));
+    if (outfile == nullptr) {
+        throw DeadlyExportError("could not open output .dae file: " + std::string(pFile));
+    }
+
+    // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy.
+    outfile->Write(iDoTheExportThing.mOutput.str().c_str(), static_cast<size_t>(iDoTheExportThing.mOutput.tellp()), 1);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Encodes a string into a valid XML ID using the xsd:ID schema qualifications.
+static const std::string XMLIDEncode(const std::string &name) {
+    const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
+    const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char);
+
+    if (name.length() == 0) {
+        return name;
+    }
+
+    std::stringstream idEncoded;
+
+    // xsd:ID must start with letter or underscore
+    if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) {
+        idEncoded << '_';
+    }
+
+    for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) {
+        // xsd:ID can only contain letters, digits, underscores, hyphens and periods
+        if (strchr(XML_ID_CHARS, *it) != nullptr) {
+            idEncoded << *it;
+        } else {
+            // Select placeholder character based on invalid character to reduce ID collisions
+            idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT];
+        }
+    }
+
+    return idEncoded.str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Helper functions to create unique ids
+inline bool IsUniqueId(const std::unordered_set<std::string> &idSet, const std::string &idStr) {
+    return (idSet.find(idStr) == idSet.end());
+}
+
+inline std::string MakeUniqueId(const std::unordered_set<std::string> &idSet, const std::string &idPrefix, const std::string &postfix) {
+    std::string result(idPrefix + postfix);
+    if (!IsUniqueId(idSet, result)) {
+        // Select a number to append
+        size_t idnum = 1;
+        do {
+            result = idPrefix + '_' + to_string(idnum) + postfix;
+            ++idnum;
+        } while (!IsUniqueId(idSet, result));
+    }
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor for a specific scene to export
+ColladaExporter::ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file) :
+        mIOSystem(pIOSystem),
+        mPath(path),
+        mFile(file),
+        mScene(pScene),
+        endstr("\n") {
+    // make sure that all formatting happens using the standard, C locale and not the user's current locale
+    mOutput.imbue(std::locale("C"));
+    mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
+
+    // start writing the file
+    WriteFile();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor
+ColladaExporter::~ColladaExporter() {
+}
+
+// ------------------------------------------------------------------------------------------------
+// Starts writing the contents
+void ColladaExporter::WriteFile() {
+    // write the DTD
+    mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
+    // COLLADA element start
+    mOutput << "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">" << endstr;
+    PushTag();
+
+    WriteTextures();
+    WriteHeader();
+
+    // Add node names to the unique id database first so they are most likely to use their names as unique ids
+    CreateNodeIds(mScene->mRootNode);
+
+    WriteCamerasLibrary();
+    WriteLightsLibrary();
+    WriteMaterials();
+    WriteGeometryLibrary();
+    WriteControllerLibrary();
+
+    WriteSceneLibrary();
+
+    // customized, Writes the animation library
+    WriteAnimationsLibrary();
+
+    // instantiate the scene(s)
+    // For Assimp there will only ever be one
+    mOutput << startstr << "<scene>" << endstr;
+    PushTag();
+    mOutput << startstr << "<instance_visual_scene url=\"#" + mSceneId + "\" />" << endstr;
+    PopTag();
+    mOutput << startstr << "</scene>" << endstr;
+    PopTag();
+    mOutput << "</COLLADA>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the asset header
+void ColladaExporter::WriteHeader() {
+    static const ai_real epsilon = Math::getEpsilon<ai_real>();
+    static const aiQuaternion x_rot(aiMatrix3x3(
+            0, -1, 0,
+            1, 0, 0,
+            0, 0, 1));
+    static const aiQuaternion y_rot(aiMatrix3x3(
+            1, 0, 0,
+            0, 1, 0,
+            0, 0, 1));
+    static const aiQuaternion z_rot(aiMatrix3x3(
+            1, 0, 0,
+            0, 0, 1,
+            0, -1, 0));
+
+    static const unsigned int date_nb_chars = 20;
+    char date_str[date_nb_chars];
+    std::time_t date = std::time(nullptr);
+    std::strftime(date_str, date_nb_chars, "%Y-%m-%dT%H:%M:%S", std::localtime(&date));
+
+    aiVector3D scaling;
+    aiQuaternion rotation;
+    aiVector3D position;
+    mScene->mRootNode->mTransformation.Decompose(scaling, rotation, position);
+    rotation.Normalize();
+
+    mAdd_root_node = false;
+
+    ai_real scale = 1.0;
+    if (std::abs(scaling.x - scaling.y) <= epsilon && std::abs(scaling.x - scaling.z) <= epsilon && std::abs(scaling.y - scaling.z) <= epsilon) {
+        scale = (ai_real)((((double)scaling.x) + ((double)scaling.y) + ((double)scaling.z)) / 3.0);
+    } else {
+        mAdd_root_node = true;
+    }
+
+    std::string up_axis = "Y_UP";
+    if (rotation.Equal(x_rot, epsilon)) {
+        up_axis = "X_UP";
+    } else if (rotation.Equal(y_rot, epsilon)) {
+        up_axis = "Y_UP";
+    } else if (rotation.Equal(z_rot, epsilon)) {
+        up_axis = "Z_UP";
+    } else {
+        mAdd_root_node = true;
+    }
+
+    if (!position.Equal(aiVector3D(0, 0, 0))) {
+        mAdd_root_node = true;
+    }
+
+    // Assimp root nodes can have meshes, Collada Scenes cannot
+    if (mScene->mRootNode->mNumChildren == 0 || mScene->mRootNode->mMeshes != 0) {
+        mAdd_root_node = true;
+    }
+
+    if (mAdd_root_node) {
+        up_axis = "Y_UP";
+        scale = 1.0;
+    }
+
+    mOutput << startstr << "<asset>" << endstr;
+    PushTag();
+    mOutput << startstr << "<contributor>" << endstr;
+    PushTag();
+
+    // If no Scene metadata, use root node metadata
+    aiMetadata *meta = mScene->mMetaData;
+    if (nullptr == meta) {
+        meta = mScene->mRootNode->mMetaData;
+    }
+
+    aiString value;
+    if (!meta || !meta->Get("Author", value)) {
+        mOutput << startstr << "<author>"
+                << "Assimp"
+                << "</author>" << endstr;
+    } else {
+        mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
+    }
+
+    if (nullptr == meta || !meta->Get(AI_METADATA_SOURCE_GENERATOR, value)) {
+        mOutput << startstr << "<authoring_tool>"
+                << "Assimp Exporter"
+                << "</authoring_tool>" << endstr;
+    } else {
+        mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
+    }
+
+    if (meta) {
+        if (meta->Get("Comments", value)) {
+            mOutput << startstr << "<comments>" << XMLEscape(value.C_Str()) << "</comments>" << endstr;
+        }
+        if (meta->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) {
+            mOutput << startstr << "<copyright>" << XMLEscape(value.C_Str()) << "</copyright>" << endstr;
+        }
+        if (meta->Get("SourceData", value)) {
+            mOutput << startstr << "<source_data>" << XMLEscape(value.C_Str()) << "</source_data>" << endstr;
+        }
+    }
+
+    PopTag();
+    mOutput << startstr << "</contributor>" << endstr;
+
+    if (nullptr == meta || !meta->Get("Created", value)) {
+        mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+    } else {
+        mOutput << startstr << "<created>" << XMLEscape(value.C_Str()) << "</created>" << endstr;
+    }
+
+    // Modified date is always the date saved
+    mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
+
+    if (meta) {
+        if (meta->Get("Keywords", value)) {
+            mOutput << startstr << "<keywords>" << XMLEscape(value.C_Str()) << "</keywords>" << endstr;
+        }
+        if (meta->Get("Revision", value)) {
+            mOutput << startstr << "<revision>" << XMLEscape(value.C_Str()) << "</revision>" << endstr;
+        }
+        if (meta->Get("Subject", value)) {
+            mOutput << startstr << "<subject>" << XMLEscape(value.C_Str()) << "</subject>" << endstr;
+        }
+        if (meta->Get("Title", value)) {
+            mOutput << startstr << "<title>" << XMLEscape(value.C_Str()) << "</title>" << endstr;
+        }
+    }
+
+    mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
+    mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
+    PopTag();
+    mOutput << startstr << "</asset>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write the embedded textures
+void ColladaExporter::WriteTextures() {
+    static const unsigned int buffer_size = 1024;
+    char str[buffer_size];
+
+    if (mScene->HasTextures()) {
+        for (unsigned int i = 0; i < mScene->mNumTextures; i++) {
+            // It would be great to be able to create a directory in portable standard C++, but it's not the case,
+            // so we just write the textures in the current directory.
+
+            aiTexture *texture = mScene->mTextures[i];
+            if (nullptr == texture) {
+                continue;
+            }
+
+            ASSIMP_itoa10(str, buffer_size, i + 1);
+
+            std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char *)texture->achFormatHint);
+
+            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
+            if (outfile == nullptr) {
+                throw DeadlyExportError("could not open output texture file: " + mPath + name);
+            }
+
+            if (texture->mHeight == 0) {
+                outfile->Write((void *)texture->pcData, texture->mWidth, 1);
+            } else {
+                Bitmap::Save(texture, outfile.get());
+            }
+
+            outfile->Flush();
+
+            textures.insert(std::make_pair(i, name));
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write the embedded textures
+void ColladaExporter::WriteCamerasLibrary() {
+    if (mScene->HasCameras()) {
+
+        mOutput << startstr << "<library_cameras>" << endstr;
+        PushTag();
+
+        for (size_t a = 0; a < mScene->mNumCameras; ++a)
+            WriteCamera(a);
+
+        PopTag();
+        mOutput << startstr << "</library_cameras>" << endstr;
+    }
+}
+
+void ColladaExporter::WriteCamera(size_t pIndex) {
+
+    const aiCamera *cam = mScene->mCameras[pIndex];
+    const std::string cameraId = GetObjectUniqueId(AiObjectType::Camera, pIndex);
+    const std::string cameraName = GetObjectName(AiObjectType::Camera, pIndex);
+
+    mOutput << startstr << "<camera id=\"" << cameraId << "\" name=\"" << cameraName << "\" >" << endstr;
+    PushTag();
+    mOutput << startstr << "<optics>" << endstr;
+    PushTag();
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+    //assimp doesn't support the import of orthographic cameras! se we write
+    //always perspective
+    mOutput << startstr << "<perspective>" << endstr;
+    PushTag();
+    mOutput << startstr << "<xfov sid=\"xfov\">" << AI_RAD_TO_DEG(cam->mHorizontalFOV)
+            << "</xfov>" << endstr;
+    mOutput << startstr << "<aspect_ratio>"
+            << cam->mAspect
+            << "</aspect_ratio>" << endstr;
+    mOutput << startstr << "<znear sid=\"znear\">"
+            << cam->mClipPlaneNear
+            << "</znear>" << endstr;
+    mOutput << startstr << "<zfar sid=\"zfar\">"
+            << cam->mClipPlaneFar
+            << "</zfar>" << endstr;
+    PopTag();
+    mOutput << startstr << "</perspective>" << endstr;
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+    PopTag();
+    mOutput << startstr << "</optics>" << endstr;
+    PopTag();
+    mOutput << startstr << "</camera>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write the embedded textures
+void ColladaExporter::WriteLightsLibrary() {
+    if (mScene->HasLights()) {
+
+        mOutput << startstr << "<library_lights>" << endstr;
+        PushTag();
+
+        for (size_t a = 0; a < mScene->mNumLights; ++a)
+            WriteLight(a);
+
+        PopTag();
+        mOutput << startstr << "</library_lights>" << endstr;
+    }
+}
+
+void ColladaExporter::WriteLight(size_t pIndex) {
+
+    const aiLight *light = mScene->mLights[pIndex];
+    const std::string lightId = GetObjectUniqueId(AiObjectType::Light, pIndex);
+    const std::string lightName = GetObjectName(AiObjectType::Light, pIndex);
+
+    mOutput << startstr << "<light id=\"" << lightId << "\" name=\""
+            << lightName << "\" >" << endstr;
+    PushTag();
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+    switch (light->mType) {
+    case aiLightSource_AMBIENT:
+        WriteAmbienttLight(light);
+        break;
+    case aiLightSource_DIRECTIONAL:
+        WriteDirectionalLight(light);
+        break;
+    case aiLightSource_POINT:
+        WritePointLight(light);
+        break;
+    case aiLightSource_SPOT:
+        WriteSpotLight(light);
+        break;
+    case aiLightSource_AREA:
+    case aiLightSource_UNDEFINED:
+    case _aiLightSource_Force32Bit:
+        break;
+    }
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</light>" << endstr;
+}
+
+void ColladaExporter::WritePointLight(const aiLight *const light) {
+    const aiColor3D &color = light->mColorDiffuse;
+    mOutput << startstr << "<point>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+    mOutput << startstr << "<constant_attenuation>"
+            << light->mAttenuationConstant
+            << "</constant_attenuation>" << endstr;
+    mOutput << startstr << "<linear_attenuation>"
+            << light->mAttenuationLinear
+            << "</linear_attenuation>" << endstr;
+    mOutput << startstr << "<quadratic_attenuation>"
+            << light->mAttenuationQuadratic
+            << "</quadratic_attenuation>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</point>" << endstr;
+}
+
+void ColladaExporter::WriteDirectionalLight(const aiLight *const light) {
+    const aiColor3D &color = light->mColorDiffuse;
+    mOutput << startstr << "<directional>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</directional>" << endstr;
+}
+
+void ColladaExporter::WriteSpotLight(const aiLight *const light) {
+
+    const aiColor3D &color = light->mColorDiffuse;
+    mOutput << startstr << "<spot>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+    mOutput << startstr << "<constant_attenuation>"
+            << light->mAttenuationConstant
+            << "</constant_attenuation>" << endstr;
+    mOutput << startstr << "<linear_attenuation>"
+            << light->mAttenuationLinear
+            << "</linear_attenuation>" << endstr;
+    mOutput << startstr << "<quadratic_attenuation>"
+            << light->mAttenuationQuadratic
+            << "</quadratic_attenuation>" << endstr;
+    /*
+    out->mAngleOuterCone = AI_DEG_TO_RAD (std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+
+                            srcLight->mFalloffAngle);
+    */
+
+    const ai_real fallOffAngle = AI_RAD_TO_DEG(light->mAngleInnerCone);
+    mOutput << startstr << "<falloff_angle sid=\"fall_off_angle\">"
+            << fallOffAngle
+            << "</falloff_angle>" << endstr;
+    double temp = light->mAngleOuterCone - light->mAngleInnerCone;
+
+    temp = std::cos(temp);
+    temp = std::log(temp) / std::log(0.1);
+    temp = 1 / temp;
+    mOutput << startstr << "<falloff_exponent sid=\"fall_off_exponent\">"
+            << temp
+            << "</falloff_exponent>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</spot>" << endstr;
+}
+
+void ColladaExporter::WriteAmbienttLight(const aiLight *const light) {
+
+    const aiColor3D &color = light->mColorAmbient;
+    mOutput << startstr << "<ambient>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</ambient>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single surface entry from the given material keys
+bool ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex) {
+    if (pSrcMat.GetTextureCount(pTexture) > 0) {
+        aiString texfile;
+        unsigned int uvChannel = 0;
+        pSrcMat.GetTexture(pTexture, 0, &texfile, nullptr, &uvChannel);
+
+        std::string index_str(texfile.C_Str());
+
+        if (index_str.size() != 0 && index_str[0] == '*') {
+            unsigned int index;
+
+            index_str = index_str.substr(1, std::string::npos);
+
+            try {
+                index = (unsigned int)strtoul10_64(index_str.c_str());
+            } catch (std::exception &error) {
+                throw DeadlyExportError(error.what());
+            }
+
+            std::map<unsigned int, std::string>::const_iterator name = textures.find(index);
+
+            if (name != textures.end()) {
+                poSurface.texture = name->second;
+            } else {
+                throw DeadlyExportError("could not find embedded texture at index " + index_str);
+            }
+        } else {
+            poSurface.texture = texfile.C_Str();
+        }
+
+        poSurface.channel = uvChannel;
+        poSurface.exist = true;
+    } else {
+        if (pKey)
+            poSurface.exist = pSrcMat.Get(pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
+    }
+    return poSurface.exist;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
+static bool isalnum_C(char c) {
+    return (nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", c));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes an image entry for the given surface
+void ColladaExporter::WriteImageEntry(const Surface &pSurface, const std::string &imageId) {
+    if (!pSurface.texture.empty()) {
+        mOutput << startstr << "<image id=\"" << imageId << "\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<init_from>";
+
+        // URL encode image file name first, then XML encode on top
+        std::stringstream imageUrlEncoded;
+        for (std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it) {
+            if (isalnum_C((unsigned char)*it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\')
+                imageUrlEncoded << *it;
+            else
+                imageUrlEncoded << '%' << std::hex << size_t((unsigned char)*it) << std::dec;
+        }
+        mOutput << XMLEscape(imageUrlEncoded.str());
+        mOutput << "</init_from>" << endstr;
+        PopTag();
+        mOutput << startstr << "</image>" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a color-or-texture entry into an effect definition
+void ColladaExporter::WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId) {
+    if (pSurface.exist) {
+        mOutput << startstr << "<" << pTypeName << ">" << endstr;
+        PushTag();
+        if (pSurface.texture.empty()) {
+            mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << "   " << pSurface.color.g << "   " << pSurface.color.b << "   " << pSurface.color.a << "</color>" << endstr;
+        } else {
+            mOutput << startstr << "<texture texture=\"" << imageId << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+        }
+        PopTag();
+        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the two parameters necessary for referencing a texture in an effect entry
+void ColladaExporter::WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId) {
+    // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
+    if (!pSurface.texture.empty()) {
+        mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-surface\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<surface type=\"2D\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<init_from>" << materialId << "-" << pTypeName << "-image</init_from>" << endstr;
+        PopTag();
+        mOutput << startstr << "</surface>" << endstr;
+        PopTag();
+        mOutput << startstr << "</newparam>" << endstr;
+
+        mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-sampler\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<sampler2D>" << endstr;
+        PushTag();
+        mOutput << startstr << "<source>" << materialId << "-" << pTypeName << "-surface</source>" << endstr;
+        PopTag();
+        mOutput << startstr << "</sampler2D>" << endstr;
+        PopTag();
+        mOutput << startstr << "</newparam>" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a scalar property
+void ColladaExporter::WriteFloatEntry(const Property &pProperty, const std::string &pTypeName) {
+    if (pProperty.exist) {
+        mOutput << startstr << "<" << pTypeName << ">" << endstr;
+        PushTag();
+        mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr;
+        PopTag();
+        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the material setup
+void ColladaExporter::WriteMaterials() {
+    std::vector<Material> materials;
+    materials.resize(mScene->mNumMaterials);
+
+    /// collect all materials from the scene
+    size_t numTextures = 0;
+    for (size_t a = 0; a < mScene->mNumMaterials; ++a) {
+        Material &material = materials[a];
+        material.id = GetObjectUniqueId(AiObjectType::Material, a);
+        material.name = GetObjectName(AiObjectType::Material, a);
+
+        const aiMaterial &mat = *(mScene->mMaterials[a]);
+        aiShadingMode shading = aiShadingMode_Flat;
+        material.shading_model = "phong";
+        if (mat.Get(AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) {
+            if (shading == aiShadingMode_Phong) {
+                material.shading_model = "phong";
+            } else if (shading == aiShadingMode_Blinn) {
+                material.shading_model = "blinn";
+            } else if (shading == aiShadingMode_NoShading) {
+                material.shading_model = "constant";
+            } else if (shading == aiShadingMode_Gouraud) {
+                material.shading_model = "lambert";
+            }
+        }
+
+        if (ReadMaterialSurface(material.ambient, mat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT))
+            ++numTextures;
+        if (ReadMaterialSurface(material.diffuse, mat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE))
+            ++numTextures;
+        if (ReadMaterialSurface(material.specular, mat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR))
+            ++numTextures;
+        if (ReadMaterialSurface(material.emissive, mat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE))
+            ++numTextures;
+        if (ReadMaterialSurface(material.reflective, mat, aiTextureType_REFLECTION, AI_MATKEY_COLOR_REFLECTIVE))
+            ++numTextures;
+        if (ReadMaterialSurface(material.transparent, mat, aiTextureType_OPACITY, AI_MATKEY_COLOR_TRANSPARENT))
+            ++numTextures;
+        if (ReadMaterialSurface(material.normal, mat, aiTextureType_NORMALS, nullptr, 0, 0))
+            ++numTextures;
+
+        material.shininess.exist = mat.Get(AI_MATKEY_SHININESS, material.shininess.value) == aiReturn_SUCCESS;
+        material.transparency.exist = mat.Get(AI_MATKEY_OPACITY, material.transparency.value) == aiReturn_SUCCESS;
+        material.index_refraction.exist = mat.Get(AI_MATKEY_REFRACTI, material.index_refraction.value) == aiReturn_SUCCESS;
+    }
+
+    // output textures if present
+    if (numTextures > 0) {
+        mOutput << startstr << "<library_images>" << endstr;
+        PushTag();
+        for (const Material &mat : materials) {
+            WriteImageEntry(mat.ambient, mat.id + "-ambient-image");
+            WriteImageEntry(mat.diffuse, mat.id + "-diffuse-image");
+            WriteImageEntry(mat.specular, mat.id + "-specular-image");
+            WriteImageEntry(mat.emissive, mat.id + "-emission-image");
+            WriteImageEntry(mat.reflective, mat.id + "-reflective-image");
+            WriteImageEntry(mat.transparent, mat.id + "-transparent-image");
+            WriteImageEntry(mat.normal, mat.id + "-normal-image");
+        }
+        PopTag();
+        mOutput << startstr << "</library_images>" << endstr;
+    }
+
+    // output effects - those are the actual carriers of information
+    if (!materials.empty()) {
+        mOutput << startstr << "<library_effects>" << endstr;
+        PushTag();
+        for (const Material &mat : materials) {
+            // this is so ridiculous it must be right
+            mOutput << startstr << "<effect id=\"" << mat.id << "-fx\" name=\"" << mat.name << "\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<profile_COMMON>" << endstr;
+            PushTag();
+
+            // write sampler- and surface params for the texture entries
+            WriteTextureParamEntry(mat.emissive, "emission", mat.id);
+            WriteTextureParamEntry(mat.ambient, "ambient", mat.id);
+            WriteTextureParamEntry(mat.diffuse, "diffuse", mat.id);
+            WriteTextureParamEntry(mat.specular, "specular", mat.id);
+            WriteTextureParamEntry(mat.reflective, "reflective", mat.id);
+            WriteTextureParamEntry(mat.transparent, "transparent", mat.id);
+            WriteTextureParamEntry(mat.normal, "normal", mat.id);
+
+            mOutput << startstr << "<technique sid=\"standard\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<" << mat.shading_model << ">" << endstr;
+            PushTag();
+
+            WriteTextureColorEntry(mat.emissive, "emission", mat.id + "-emission-sampler");
+            WriteTextureColorEntry(mat.ambient, "ambient", mat.id + "-ambient-sampler");
+            WriteTextureColorEntry(mat.diffuse, "diffuse", mat.id + "-diffuse-sampler");
+            WriteTextureColorEntry(mat.specular, "specular", mat.id + "-specular-sampler");
+            WriteFloatEntry(mat.shininess, "shininess");
+            WriteTextureColorEntry(mat.reflective, "reflective", mat.id + "-reflective-sampler");
+            WriteTextureColorEntry(mat.transparent, "transparent", mat.id + "-transparent-sampler");
+            WriteFloatEntry(mat.transparency, "transparency");
+            WriteFloatEntry(mat.index_refraction, "index_of_refraction");
+
+            if (!mat.normal.texture.empty()) {
+                WriteTextureColorEntry(mat.normal, "bump", mat.id + "-normal-sampler");
+            }
+
+            PopTag();
+            mOutput << startstr << "</" << mat.shading_model << ">" << endstr;
+            PopTag();
+            mOutput << startstr << "</technique>" << endstr;
+            PopTag();
+            mOutput << startstr << "</profile_COMMON>" << endstr;
+            PopTag();
+            mOutput << startstr << "</effect>" << endstr;
+        }
+        PopTag();
+        mOutput << startstr << "</library_effects>" << endstr;
+
+        // write materials - they're just effect references
+        mOutput << startstr << "<library_materials>" << endstr;
+        PushTag();
+        for (std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it) {
+            const Material &mat = *it;
+            mOutput << startstr << "<material id=\"" << mat.id << "\" name=\"" << mat.name << "\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<instance_effect url=\"#" << mat.id << "-fx\"/>" << endstr;
+            PopTag();
+            mOutput << startstr << "</material>" << endstr;
+        }
+        PopTag();
+        mOutput << startstr << "</library_materials>" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the controller library
+void ColladaExporter::WriteControllerLibrary() {
+    mOutput << startstr << "<library_controllers>" << endstr;
+    PushTag();
+
+    for (size_t a = 0; a < mScene->mNumMeshes; ++a) {
+        WriteController(a);
+    }
+
+    PopTag();
+    mOutput << startstr << "</library_controllers>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a skin controller of the given mesh
+void ColladaExporter::WriteController(size_t pIndex) {
+    const aiMesh *mesh = mScene->mMeshes[pIndex];
+    // Is there a skin controller?
+    if (mesh->mNumBones == 0 || mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+        return;
+
+    const std::string idstr = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
+    const std::string namestr = GetObjectName(AiObjectType::Mesh, pIndex);
+
+    mOutput << startstr << "<controller id=\"" << idstr << "-skin\" ";
+    mOutput << "name=\"skinCluster" << pIndex << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<skin source=\"#" << idstr << "\">" << endstr;
+    PushTag();
+
+    // bind pose matrix
+    mOutput << startstr << "<bind_shape_matrix>" << endstr;
+    PushTag();
+
+    // I think it is identity in general cases.
+    aiMatrix4x4 mat;
+    mOutput << startstr << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << endstr;
+    mOutput << startstr << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << endstr;
+    mOutput << startstr << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << endstr;
+    mOutput << startstr << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4 << endstr;
+
+    PopTag();
+    mOutput << startstr << "</bind_shape_matrix>" << endstr;
+
+    mOutput << startstr << "<source id=\"" << idstr << "-skin-joints\" name=\"" << namestr << "-skin-joints\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<Name_array id=\"" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
+
+    for (size_t i = 0; i < mesh->mNumBones; ++i)
+        mOutput << GetBoneUniqueId(mesh->mBones[i]) << ' ';
+
+    mOutput << "</Name_array>" << endstr;
+
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<accessor source=\"#" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\" stride=\"" << 1 << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<param name=\"JOINT\" type=\"Name\"></param>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</accessor>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</source>" << endstr;
+
+    std::vector<ai_real> bind_poses;
+    bind_poses.reserve(mesh->mNumBones * 16);
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
+        for (unsigned int j = 0; j < 4; ++j)
+            bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4);
+
+    WriteFloatArray(idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real *)bind_poses.data(), bind_poses.size() / 16);
+
+    bind_poses.clear();
+
+    std::vector<ai_real> skin_weights;
+    skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones);
+    for (size_t i = 0; i < mesh->mNumBones; ++i)
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+            skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight);
+
+    WriteFloatArray(idstr + "-skin-weights", FloatType_Weight, (const ai_real *)skin_weights.data(), skin_weights.size());
+
+    skin_weights.clear();
+
+    mOutput << startstr << "<joints>" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstr << "-skin-joints\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"INV_BIND_MATRIX\" source=\"#" << idstr << "-skin-bind_poses\"></input>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</joints>" << endstr;
+
+    mOutput << startstr << "<vertex_weights count=\"" << mesh->mNumVertices << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstr << "-skin-joints\" offset=\"0\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"WEIGHT\" source=\"#" << idstr << "-skin-weights\" offset=\"1\"></input>" << endstr;
+
+    mOutput << startstr << "<vcount>";
+
+    std::vector<ai_uint> num_influences(mesh->mNumVertices, (ai_uint)0);
+    for (size_t i = 0; i < mesh->mNumBones; ++i)
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+            ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId];
+
+    for (size_t i = 0; i < mesh->mNumVertices; ++i)
+        mOutput << num_influences[i] << " ";
+
+    mOutput << "</vcount>" << endstr;
+
+    mOutput << startstr << "<v>";
+
+    ai_uint joint_weight_indices_length = 0;
+    std::vector<ai_uint> accum_influences;
+    accum_influences.reserve(num_influences.size());
+    for (size_t i = 0; i < num_influences.size(); ++i) {
+        accum_influences.push_back(joint_weight_indices_length);
+        joint_weight_indices_length += num_influences[i];
+    }
+
+    ai_uint weight_index = 0;
+    std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1);
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
+        for (unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
+            unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId;
+            for (ai_uint k = 0; k < num_influences[vId]; ++k) {
+                if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1) {
+                    joint_weight_indices[2 * (accum_influences[vId] + k)] = i;
+                    joint_weight_indices[2 * (accum_influences[vId] + k) + 1] = weight_index;
+                    break;
+                }
+            }
+            ++weight_index;
+        }
+
+    for (size_t i = 0; i < joint_weight_indices.size(); ++i)
+        mOutput << joint_weight_indices[i] << " ";
+
+    num_influences.clear();
+    accum_influences.clear();
+    joint_weight_indices.clear();
+
+    mOutput << "</v>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</vertex_weights>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</skin>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</controller>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the geometry library
+void ColladaExporter::WriteGeometryLibrary() {
+    mOutput << startstr << "<library_geometries>" << endstr;
+    PushTag();
+
+    for (size_t a = 0; a < mScene->mNumMeshes; ++a)
+        WriteGeometry(a);
+
+    PopTag();
+    mOutput << startstr << "</library_geometries>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the given mesh
+void ColladaExporter::WriteGeometry(size_t pIndex) {
+    const aiMesh *mesh = mScene->mMeshes[pIndex];
+    const std::string geometryId = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
+    const std::string geometryName = GetObjectName(AiObjectType::Mesh, pIndex);
+
+    if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+        return;
+
+    // opening tag
+    mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<mesh>" << endstr;
+    PushTag();
+
+    // Positions
+    WriteFloatArray(geometryId + "-positions", FloatType_Vector, (ai_real *)mesh->mVertices, mesh->mNumVertices);
+    // Normals, if any
+    if (mesh->HasNormals())
+        WriteFloatArray(geometryId + "-normals", FloatType_Vector, (ai_real *)mesh->mNormals, mesh->mNumVertices);
+
+    // texture coords
+    for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+        if (mesh->HasTextureCoords(static_cast<unsigned int>(a))) {
+            WriteFloatArray(geometryId + "-tex" + to_string(a), mesh->mNumUVComponents[a] == 3 ? FloatType_TexCoord3 : FloatType_TexCoord2,
+                    (ai_real *)mesh->mTextureCoords[a], mesh->mNumVertices);
+        }
+    }
+
+    // vertex colors
+    for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+        if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
+            WriteFloatArray(geometryId + "-color" + to_string(a), FloatType_Color, (ai_real *)mesh->mColors[a], mesh->mNumVertices);
+    }
+
+    // assemble vertex structure
+    // Only write input for POSITION since we will write other as shared inputs in polygon definition
+    mOutput << startstr << "<vertices id=\"" << geometryId << "-vertices"
+            << "\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr;
+    PopTag();
+    mOutput << startstr << "</vertices>" << endstr;
+
+    // count the number of lines, triangles and polygon meshes
+    int countLines = 0;
+    int countPoly = 0;
+    for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+        if (mesh->mFaces[a].mNumIndices == 2)
+            countLines++;
+        else if (mesh->mFaces[a].mNumIndices >= 3)
+            countPoly++;
+    }
+
+    // lines
+    if (countLines) {
+        mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
+        if (mesh->HasNormals())
+            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+            if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+            if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+
+        mOutput << startstr << "<p>";
+        for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+            const aiFace &face = mesh->mFaces[a];
+            if (face.mNumIndices != 2) continue;
+            for (size_t b = 0; b < face.mNumIndices; ++b)
+                mOutput << face.mIndices[b] << " ";
+        }
+        mOutput << "</p>" << endstr;
+        PopTag();
+        mOutput << startstr << "</lines>" << endstr;
+    }
+
+    // triangle - don't use it, because compatibility problems
+
+    // polygons
+    if (countPoly) {
+        mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
+        if (mesh->HasNormals())
+            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+            if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+            if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+
+        mOutput << startstr << "<vcount>";
+        for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+            if (mesh->mFaces[a].mNumIndices < 3) continue;
+            mOutput << mesh->mFaces[a].mNumIndices << " ";
+        }
+        mOutput << "</vcount>" << endstr;
+
+        mOutput << startstr << "<p>";
+        for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+            const aiFace &face = mesh->mFaces[a];
+            if (face.mNumIndices < 3) continue;
+            for (size_t b = 0; b < face.mNumIndices; ++b)
+                mOutput << face.mIndices[b] << " ";
+        }
+        mOutput << "</p>" << endstr;
+        PopTag();
+        mOutput << startstr << "</polylist>" << endstr;
+    }
+
+    // closing tags
+    PopTag();
+    mOutput << startstr << "</mesh>" << endstr;
+    PopTag();
+    mOutput << startstr << "</geometry>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a float array of the given type
+void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount) {
+    size_t floatsPerElement = 0;
+    switch (pType) {
+    case FloatType_Vector: floatsPerElement = 3; break;
+    case FloatType_TexCoord2: floatsPerElement = 2; break;
+    case FloatType_TexCoord3: floatsPerElement = 3; break;
+    case FloatType_Color: floatsPerElement = 3; break;
+    case FloatType_Mat4x4: floatsPerElement = 16; break;
+    case FloatType_Weight: floatsPerElement = 1; break;
+    case FloatType_Time: floatsPerElement = 1; break;
+    default:
+        return;
+    }
+
+    std::string arrayId = XMLIDEncode(pIdString) + "-array";
+
+    mOutput << startstr << "<source id=\"" << XMLIDEncode(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
+    PushTag();
+
+    // source array
+    mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
+    PushTag();
+
+    if (pType == FloatType_TexCoord2) {
+        for (size_t a = 0; a < pElementCount; ++a) {
+            mOutput << pData[a * 3 + 0] << " ";
+            mOutput << pData[a * 3 + 1] << " ";
+        }
+    } else if (pType == FloatType_Color) {
+        for (size_t a = 0; a < pElementCount; ++a) {
+            mOutput << pData[a * 4 + 0] << " ";
+            mOutput << pData[a * 4 + 1] << " ";
+            mOutput << pData[a * 4 + 2] << " ";
+        }
+    } else {
+        for (size_t a = 0; a < pElementCount * floatsPerElement; ++a)
+            mOutput << pData[a] << " ";
+    }
+    mOutput << "</float_array>" << endstr;
+    PopTag();
+
+    // the usual Collada fun. Let's bloat it even more!
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+    mOutput << startstr << "<accessor count=\"" << pElementCount << "\" offset=\"0\" source=\"#" << arrayId << "\" stride=\"" << floatsPerElement << "\">" << endstr;
+    PushTag();
+
+    switch (pType) {
+    case FloatType_Vector:
+        mOutput << startstr << "<param name=\"X\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"Y\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"Z\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_TexCoord2:
+        mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_TexCoord3:
+        mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"P\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_Color:
+        mOutput << startstr << "<param name=\"R\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"G\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"B\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_Mat4x4:
+        mOutput << startstr << "<param name=\"TRANSFORM\" type=\"float4x4\" />" << endstr;
+        break;
+
+    case FloatType_Weight:
+        mOutput << startstr << "<param name=\"WEIGHT\" type=\"float\" />" << endstr;
+        break;
+
+    // customized, add animation related
+    case FloatType_Time:
+        mOutput << startstr << "<param name=\"TIME\" type=\"float\" />" << endstr;
+        break;
+    }
+
+    PopTag();
+    mOutput << startstr << "</accessor>" << endstr;
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+    PopTag();
+    mOutput << startstr << "</source>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the scene library
+void ColladaExporter::WriteSceneLibrary() {
+    // Determine if we are using the aiScene root or our own
+    std::string sceneName("Scene");
+    if (mAdd_root_node) {
+        mSceneId = MakeUniqueId(mUniqueIds, sceneName, std::string());
+        mUniqueIds.insert(mSceneId);
+    } else {
+        mSceneId = GetNodeUniqueId(mScene->mRootNode);
+        sceneName = GetNodeName(mScene->mRootNode);
+    }
+
+    mOutput << startstr << "<library_visual_scenes>" << endstr;
+    PushTag();
+    mOutput << startstr << "<visual_scene id=\"" + mSceneId + "\" name=\"" + sceneName + "\">" << endstr;
+    PushTag();
+
+    if (mAdd_root_node) {
+        // Export the root node
+        WriteNode(mScene->mRootNode);
+    } else {
+        // Have already exported the root node
+        for (size_t a = 0; a < mScene->mRootNode->mNumChildren; ++a)
+            WriteNode(mScene->mRootNode->mChildren[a]);
+    }
+
+    PopTag();
+    mOutput << startstr << "</visual_scene>" << endstr;
+    PopTag();
+    mOutput << startstr << "</library_visual_scenes>" << endstr;
+}
+// ------------------------------------------------------------------------------------------------
+void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
+    const aiAnimation *anim = mScene->mAnimations[pIndex];
+
+    if (anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels == 0)
+        return;
+
+    const std::string animationNameEscaped = GetObjectName(AiObjectType::Animation, pIndex);
+    const std::string idstrEscaped = GetObjectUniqueId(AiObjectType::Animation, pIndex);
+
+    mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animationNameEscaped + "\">" << endstr;
+    PushTag();
+
+    std::string cur_node_idstr;
+    for (size_t a = 0; a < anim->mNumChannels; ++a) {
+        const aiNodeAnim *nodeAnim = anim->mChannels[a];
+
+        // sanity check
+        if (nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys) {
+            continue;
+        }
+
+        {
+            cur_node_idstr.clear();
+            cur_node_idstr += nodeAnim->mNodeName.data;
+            cur_node_idstr += std::string("_matrix-input");
+
+            std::vector<ai_real> frames;
+            for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+                frames.push_back(static_cast<ai_real>(nodeAnim->mPositionKeys[i].mTime));
+            }
+
+            WriteFloatArray(cur_node_idstr, FloatType_Time, (const ai_real *)frames.data(), frames.size());
+            frames.clear();
+        }
+
+        {
+            cur_node_idstr.clear();
+
+            cur_node_idstr += nodeAnim->mNodeName.data;
+            cur_node_idstr += std::string("_matrix-output");
+
+            std::vector<ai_real> keyframes;
+            keyframes.reserve(nodeAnim->mNumPositionKeys * 16);
+            for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+                aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue;
+                aiMatrix4x4 ScalingM; // identity
+                ScalingM[0][0] = Scaling.x;
+                ScalingM[1][1] = Scaling.y;
+                ScalingM[2][2] = Scaling.z;
+
+                aiQuaternion RotationQ = nodeAnim->mRotationKeys[i].mValue;
+                aiMatrix4x4 s = aiMatrix4x4(RotationQ.GetMatrix());
+                aiMatrix4x4 RotationM(s.a1, s.a2, s.a3, 0, s.b1, s.b2, s.b3, 0, s.c1, s.c2, s.c3, 0, 0, 0, 0, 1);
+
+                aiVector3D Translation = nodeAnim->mPositionKeys[i].mValue;
+                aiMatrix4x4 TranslationM; // identity
+                TranslationM[0][3] = Translation.x;
+                TranslationM[1][3] = Translation.y;
+                TranslationM[2][3] = Translation.z;
+
+                // Combine the above transformations
+                aiMatrix4x4 mat = TranslationM * RotationM * ScalingM;
+
+                for (unsigned int j = 0; j < 4; ++j) {
+                    keyframes.insert(keyframes.end(), mat[j], mat[j] + 4);
+                }
+            }
+
+            WriteFloatArray(cur_node_idstr, FloatType_Mat4x4, (const ai_real *)keyframes.data(), keyframes.size() / 16);
+        }
+
+        {
+            std::vector<std::string> names;
+            for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+                if (nodeAnim->mPreState == aiAnimBehaviour_DEFAULT || nodeAnim->mPreState == aiAnimBehaviour_LINEAR || nodeAnim->mPreState == aiAnimBehaviour_REPEAT) {
+                    names.push_back("LINEAR");
+                } else if (nodeAnim->mPostState == aiAnimBehaviour_CONSTANT) {
+                    names.push_back("STEP");
+                }
+            }
+
+            const std::string cur_node_idstr2 = nodeAnim->mNodeName.data + std::string("_matrix-interpolation");
+            std::string arrayId = XMLIDEncode(cur_node_idstr2) + "-array";
+
+            mOutput << startstr << "<source id=\"" << XMLIDEncode(cur_node_idstr2) << "\">" << endstr;
+            PushTag();
+
+            // source array
+            mOutput << startstr << "<Name_array id=\"" << arrayId << "\" count=\"" << names.size() << "\"> ";
+            for (size_t aa = 0; aa < names.size(); ++aa) {
+                mOutput << names[aa] << " ";
+            }
+            mOutput << "</Name_array>" << endstr;
+
+            mOutput << startstr << "<technique_common>" << endstr;
+            PushTag();
+
+            mOutput << startstr << "<accessor source=\"#" << arrayId << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
+            PushTag();
+
+            mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</accessor>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</technique_common>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</source>" << endstr;
+        }
+    }
+
+    for (size_t a = 0; a < anim->mNumChannels; ++a) {
+        const aiNodeAnim *nodeAnim = anim->mChannels[a];
+
+        {
+            // samplers
+            const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler");
+            mOutput << startstr << "<sampler id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
+            PushTag();
+
+            mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-input")) << "\"/>" << endstr;
+            mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-output")) << "\"/>" << endstr;
+            mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-interpolation")) << "\"/>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</sampler>" << endstr;
+        }
+    }
+
+    for (size_t a = 0; a < anim->mNumChannels; ++a) {
+        const aiNodeAnim *nodeAnim = anim->mChannels[a];
+
+        {
+            // channels
+            mOutput << startstr << "<channel source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-sampler")) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+        }
+    }
+
+    PopTag();
+    mOutput << startstr << "</animation>" << endstr;
+}
+// ------------------------------------------------------------------------------------------------
+void ColladaExporter::WriteAnimationsLibrary() {
+    if (mScene->mNumAnimations > 0) {
+        mOutput << startstr << "<library_animations>" << endstr;
+        PushTag();
+
+        // start recursive write at the root node
+        for (size_t a = 0; a < mScene->mNumAnimations; ++a)
+            WriteAnimationLibrary(a);
+
+        PopTag();
+        mOutput << startstr << "</library_animations>" << endstr;
+    }
+}
+// ------------------------------------------------------------------------------------------------
+// Helper to find a bone by name in the scene
+aiBone *findBone(const aiScene *scene, const aiString &name) {
+    for (size_t m = 0; m < scene->mNumMeshes; m++) {
+        aiMesh *mesh = scene->mMeshes[m];
+        for (size_t b = 0; b < mesh->mNumBones; b++) {
+            aiBone *bone = mesh->mBones[b];
+            if (name == bone->mName) {
+                return bone;
+            }
+        }
+    }
+    return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Helper to find the node associated with a bone in the scene
+const aiNode *findBoneNode(const aiNode *aNode, const aiBone *bone) {
+    if (aNode && bone && aNode->mName == bone->mName) {
+        return aNode;
+    }
+
+    if (aNode && bone) {
+        for (unsigned int i = 0; i < aNode->mNumChildren; ++i) {
+            aiNode *aChild = aNode->mChildren[i];
+            const aiNode *foundFromChild = nullptr;
+            if (aChild) {
+                foundFromChild = findBoneNode(aChild, bone);
+                if (foundFromChild) {
+                    return foundFromChild;
+                }
+            }
+        }
+    }
+
+    return nullptr;
+}
+
+const aiNode *findSkeletonRootNode(const aiScene *scene, const aiMesh *mesh) {
+    std::set<const aiNode *> topParentBoneNodes;
+    if (mesh && mesh->mNumBones > 0) {
+        for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
+            aiBone *bone = mesh->mBones[i];
+
+            const aiNode *node = findBoneNode(scene->mRootNode, bone);
+            if (node) {
+                while (node->mParent && findBone(scene, node->mParent->mName) != nullptr) {
+                    node = node->mParent;
+                }
+                topParentBoneNodes.insert(node);
+            }
+        }
+    }
+
+    if (!topParentBoneNodes.empty()) {
+        const aiNode *parentBoneNode = *topParentBoneNodes.begin();
+        if (topParentBoneNodes.size() == 1) {
+            return parentBoneNode;
+        } else {
+            for (auto it : topParentBoneNodes) {
+                if (it->mParent) return it->mParent;
+            }
+            return parentBoneNode;
+        }
+    }
+
+    return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively writes the given node
+void ColladaExporter::WriteNode(const aiNode *pNode) {
+    // If the node is associated with a bone, it is a joint node (JOINT)
+    // otherwise it is a normal node (NODE)
+    // Assimp-specific: nodes with no name cannot be associated with bones
+    const char *node_type;
+    bool is_joint, is_skeleton_root = false;
+    if (pNode->mName.length == 0 || nullptr == findBone(mScene, pNode->mName)) {
+        node_type = "NODE";
+        is_joint = false;
+    } else {
+        node_type = "JOINT";
+        is_joint = true;
+        if (!pNode->mParent || nullptr == findBone(mScene, pNode->mParent->mName)) {
+            is_skeleton_root = true;
+        }
+    }
+
+    const std::string node_id = GetNodeUniqueId(pNode);
+    const std::string node_name = GetNodeName(pNode);
+    mOutput << startstr << "<node ";
+    if (is_skeleton_root) {
+        mFoundSkeletonRootNodeID = node_id; // For now, only support one skeleton in a scene.
+    }
+    mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id + "\" " : "");
+    mOutput << "name=\"" << node_name
+            << "\" type=\"" << node_type
+            << "\">" << endstr;
+    PushTag();
+
+    // write transformation - we can directly put the matrix there
+    // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards
+    aiMatrix4x4 mat = pNode->mTransformation;
+
+    // If this node is a Camera node, the camera coordinate system needs to be multiplied in.
+    // When importing from Collada, the mLookAt is set to 0, 0, -1, and the node transform is unchanged.
+    // When importing from a different format, mLookAt is set to 0, 0, 1. Therefore, the local camera
+    // coordinate system must be changed to matche the Collada specification.
+    for (size_t i = 0; i < mScene->mNumCameras; i++) {
+        if (mScene->mCameras[i]->mName == pNode->mName) {
+            aiMatrix4x4 sourceView;
+            mScene->mCameras[i]->GetCameraMatrix(sourceView);
+
+            aiMatrix4x4 colladaView;
+            colladaView.a1 = colladaView.c3 = -1; // move into -z space.
+            mat *= (sourceView * colladaView);
+            break;
+        }
+    }
+
+    // customized, sid should be 'matrix' to match with loader code.
+    //mOutput << startstr << "<matrix sid=\"transform\">";
+    mOutput << startstr << "<matrix sid=\"matrix\">";
+
+    mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
+    mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " ";
+    mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " ";
+    mOutput << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4;
+    mOutput << "</matrix>" << endstr;
+
+    if (pNode->mNumMeshes == 0) {
+        //check if it is a camera node
+        for (size_t i = 0; i < mScene->mNumCameras; i++) {
+            if (mScene->mCameras[i]->mName == pNode->mName) {
+                mOutput << startstr << "<instance_camera url=\"#" << GetObjectUniqueId(AiObjectType::Camera, i) << "\"/>" << endstr;
+                break;
+            }
+        }
+        //check if it is a light node
+        for (size_t i = 0; i < mScene->mNumLights; i++) {
+            if (mScene->mLights[i]->mName == pNode->mName) {
+                mOutput << startstr << "<instance_light url=\"#" << GetObjectUniqueId(AiObjectType::Light, i) << "\"/>" << endstr;
+                break;
+            }
+        }
+
+    } else
+        // instance every geometry
+        for (size_t a = 0; a < pNode->mNumMeshes; ++a) {
+            const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[a]];
+            // do not instantiate mesh if empty. I wonder how this could happen
+            if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+                continue;
+
+            const std::string meshId = GetObjectUniqueId(AiObjectType::Mesh, pNode->mMeshes[a]);
+
+            if (mesh->mNumBones == 0) {
+                mOutput << startstr << "<instance_geometry url=\"#" << meshId << "\">" << endstr;
+                PushTag();
+            } else {
+                mOutput << startstr
+                        << "<instance_controller url=\"#" << meshId << "-skin\">"
+                        << endstr;
+                PushTag();
+
+                // note! this mFoundSkeletonRootNodeID some how affects animation, it makes the mesh attaches to armature skeleton root node.
+                // use the first bone to find skeleton root
+                const aiNode *skeletonRootBoneNode = findSkeletonRootNode(mScene, mesh);
+                if (skeletonRootBoneNode) {
+                    mFoundSkeletonRootNodeID = GetNodeUniqueId(skeletonRootBoneNode);
+                }
+                mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
+            }
+            mOutput << startstr << "<bind_material>" << endstr;
+            PushTag();
+            mOutput << startstr << "<technique_common>" << endstr;
+            PushTag();
+            mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << GetObjectUniqueId(AiObjectType::Material, mesh->mMaterialIndex) << "\">" << endstr;
+            PushTag();
+            for (size_t aa = 0; aa < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++aa) {
+                if (mesh->HasTextureCoords(static_cast<unsigned int>(aa)))
+                    // semantic       as in <texture texcoord=...>
+                    // input_semantic as in <input semantic=...>
+                    // input_set      as in <input set=...>
+                    mOutput << startstr << "<bind_vertex_input semantic=\"CHANNEL" << aa << "\" input_semantic=\"TEXCOORD\" input_set=\"" << aa << "\"/>" << endstr;
+            }
+            PopTag();
+            mOutput << startstr << "</instance_material>" << endstr;
+            PopTag();
+            mOutput << startstr << "</technique_common>" << endstr;
+            PopTag();
+            mOutput << startstr << "</bind_material>" << endstr;
+
+            PopTag();
+            if (mesh->mNumBones == 0)
+                mOutput << startstr << "</instance_geometry>" << endstr;
+            else
+                mOutput << startstr << "</instance_controller>" << endstr;
+        }
+
+    // recurse into subnodes
+    for (size_t a = 0; a < pNode->mNumChildren; ++a)
+        WriteNode(pNode->mChildren[a]);
+
+    PopTag();
+    mOutput << startstr << "</node>" << endstr;
+}
+
+void ColladaExporter::CreateNodeIds(const aiNode *node) {
+    GetNodeUniqueId(node);
+    for (size_t a = 0; a < node->mNumChildren; ++a)
+        CreateNodeIds(node->mChildren[a]);
+}
+
+std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) {
+    // Use the pointer as the key. This is safe because the scene is immutable.
+    auto idIt = mNodeIdMap.find(node);
+    if (idIt != mNodeIdMap.cend())
+        return idIt->second;
+
+    // Prefer the requested Collada Id if extant
+    std::string idStr;
+    aiString origId;
+    if (node->mMetaData && node->mMetaData->Get(AI_METADATA_COLLADA_ID, origId)) {
+        idStr = origId.C_Str();
+    } else {
+        idStr = node->mName.C_Str();
+    }
+    // Make sure the requested id is valid
+    if (idStr.empty())
+        idStr = "node";
+    else
+        idStr = XMLIDEncode(idStr);
+
+    // Ensure it's unique
+    idStr = MakeUniqueId(mUniqueIds, idStr, std::string());
+    mUniqueIds.insert(idStr);
+    mNodeIdMap.insert(std::make_pair(node, idStr));
+    return idStr;
+}
+
+std::string ColladaExporter::GetNodeName(const aiNode *node) {
+
+    return XMLEscape(node->mName.C_Str());
+}
+
+std::string ColladaExporter::GetBoneUniqueId(const aiBone *bone) {
+    // Find the Node that is this Bone
+    const aiNode *boneNode = findBoneNode(mScene->mRootNode, bone);
+    if (boneNode == nullptr)
+        return std::string();
+
+    return GetNodeUniqueId(boneNode);
+}
+
+std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex) {
+    auto idIt = GetObjectIdMap(type).find(pIndex);
+    if (idIt != GetObjectIdMap(type).cend())
+        return idIt->second;
+
+    // Not seen this object before, create and add
+    NameIdPair result = AddObjectIndexToMaps(type, pIndex);
+    return result.second;
+}
+
+std::string ColladaExporter::GetObjectName(AiObjectType type, size_t pIndex) {
+    auto objectName = GetObjectNameMap(type).find(pIndex);
+    if (objectName != GetObjectNameMap(type).cend())
+        return objectName->second;
+
+    // Not seen this object before, create and add
+    NameIdPair result = AddObjectIndexToMaps(type, pIndex);
+    return result.first;
+}
+
+// Determine unique id and add the name and id to the maps
+// @param type object type
+// @param index object index
+// @param name in/out. Caller to set the original name if known.
+// @param idStr in/out. Caller to set the preferred id if known.
+ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType type, size_t index) {
+
+    std::string name;
+    std::string idStr;
+    std::string idPostfix;
+
+    // Get the name and id postfix
+    switch (type) {
+    case AiObjectType::Mesh: name = mScene->mMeshes[index]->mName.C_Str(); break;
+    case AiObjectType::Material: name = mScene->mMaterials[index]->GetName().C_Str(); break;
+    case AiObjectType::Animation: name = mScene->mAnimations[index]->mName.C_Str(); break;
+    case AiObjectType::Light:
+        name = mScene->mLights[index]->mName.C_Str();
+        idPostfix = "-light";
+        break;
+    case AiObjectType::Camera:
+        name = mScene->mCameras[index]->mName.C_Str();
+        idPostfix = "-camera";
+        break;
+    case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
+    }
+
+    if (name.empty()) {
+        // Default ids if empty name
+        switch (type) {
+        case AiObjectType::Mesh: idStr = std::string("mesh_"); break;
+        case AiObjectType::Material: idStr = std::string("material_"); break; // This one should never happen
+        case AiObjectType::Animation: idStr = std::string("animation_"); break;
+        case AiObjectType::Light: idStr = std::string("light_"); break;
+        case AiObjectType::Camera: idStr = std::string("camera_"); break;
+        case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
+        }
+        idStr.append(to_string(index));
+    } else {
+        idStr = XMLIDEncode(name);
+    }
+
+    if (!name.empty())
+        name = XMLEscape(name);
+
+    idStr = MakeUniqueId(mUniqueIds, idStr, idPostfix);
+
+    // Add to maps
+    mUniqueIds.insert(idStr);
+    GetObjectIdMap(type).insert(std::make_pair(index, idStr));
+    GetObjectNameMap(type).insert(std::make_pair(index, name));
+
+    return std::make_pair(name, idStr);
+}
+
+} // end of namespace Assimp
+
+#endif
+#endif

+ 257 - 0
code/AssetLib/Collada/ColladaExporter.h

@@ -0,0 +1,257 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ColladaExporter.h
+ * Declares the exporter class to write a scene to a Collada file
+ */
+#ifndef AI_COLLADAEXPORTER_H_INC
+#define AI_COLLADAEXPORTER_H_INC
+
+#include <assimp/ai_assert.h>
+#include <assimp/material.h>
+
+#include <array>
+#include <map>
+#include <sstream>
+#include <unordered_set>
+#include <vector>
+
+struct aiScene;
+struct aiNode;
+struct aiLight;
+struct aiBone;
+
+namespace Assimp {
+
+class IOSystem;
+
+/// Helper class to export a given scene to a Collada file. Just for my personal
+/// comfort when implementing it.
+class ColladaExporter {
+public:
+    /// Constructor for a specific scene to export
+    ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file);
+
+    /// Destructor
+    virtual ~ColladaExporter();
+
+protected:
+    /// Starts writing the contents
+    void WriteFile();
+
+    /// Writes the asset header
+    void WriteHeader();
+
+    /// Writes the embedded textures
+    void WriteTextures();
+
+    /// Writes the material setup
+    void WriteMaterials();
+
+    /// Writes the cameras library
+    void WriteCamerasLibrary();
+
+    // Write a camera entry
+    void WriteCamera(size_t pIndex);
+
+    /// Writes the cameras library
+    void WriteLightsLibrary();
+
+    // Write a camera entry
+    void WriteLight(size_t pIndex);
+    void WritePointLight(const aiLight *const light);
+    void WriteDirectionalLight(const aiLight *const light);
+    void WriteSpotLight(const aiLight *const light);
+    void WriteAmbienttLight(const aiLight *const light);
+
+    /// Writes the controller library
+    void WriteControllerLibrary();
+
+    /// Writes a skin controller of the given mesh
+    void WriteController(size_t pIndex);
+
+    /// Writes the geometry library
+    void WriteGeometryLibrary();
+
+    /// Writes the given mesh
+    void WriteGeometry(size_t pIndex);
+
+    //enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight };
+    // customized to add animation related type
+    enum FloatDataType { FloatType_Vector,
+        FloatType_TexCoord2,
+        FloatType_TexCoord3,
+        FloatType_Color,
+        FloatType_Mat4x4,
+        FloatType_Weight,
+        FloatType_Time };
+
+    /// Writes a float array of the given type
+    void WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount);
+
+    /// Writes the scene library
+    void WriteSceneLibrary();
+
+    // customized, Writes the animation library
+    void WriteAnimationsLibrary();
+    void WriteAnimationLibrary(size_t pIndex);
+    std::string mFoundSkeletonRootNodeID = "skeleton_root"; // will be replaced by found node id in the WriteNode call.
+
+    /// Recursively writes the given node
+    void WriteNode(const aiNode *pNode);
+
+    /// Enters a new xml element, which increases the indentation
+    void PushTag() { startstr.append("  "); }
+    /// Leaves an element, decreasing the indentation
+    void PopTag() {
+        ai_assert(startstr.length() > 1);
+        startstr.erase(startstr.length() - 2);
+    }
+
+    void CreateNodeIds(const aiNode *node);
+
+    /// Get or Create a unique Node ID string for the given Node
+    std::string GetNodeUniqueId(const aiNode *node);
+    std::string GetNodeName(const aiNode *node);
+
+    std::string GetBoneUniqueId(const aiBone *bone);
+
+    enum class AiObjectType {
+        Mesh,
+        Material,
+        Animation,
+        Light,
+        Camera,
+        Count,
+    };
+    /// Get or Create a unique ID string for the given scene object index
+    std::string GetObjectUniqueId(AiObjectType type, size_t pIndex);
+    /// Get or Create a name string for the given scene object index
+    std::string GetObjectName(AiObjectType type, size_t pIndex);
+
+    typedef std::map<size_t, std::string> IndexIdMap;
+    typedef std::pair<std::string, std::string> NameIdPair;
+    NameIdPair AddObjectIndexToMaps(AiObjectType type, size_t pIndex);
+
+    // Helpers
+    inline IndexIdMap &GetObjectIdMap(AiObjectType type) { return mObjectIdMap[static_cast<size_t>(type)]; }
+    inline IndexIdMap &GetObjectNameMap(AiObjectType type) { return mObjectNameMap[static_cast<size_t>(type)]; }
+
+private:
+    std::unordered_set<std::string> mUniqueIds; // Cache of used unique ids
+    std::map<const void *, std::string> mNodeIdMap; // Cache of encoded node and bone ids
+    std::array<IndexIdMap, static_cast<size_t>(AiObjectType::Count)> mObjectIdMap; // Cache of encoded unique IDs
+    std::array<IndexIdMap, static_cast<size_t>(AiObjectType::Count)> mObjectNameMap; // Cache of encoded names
+
+public:
+    /// Stringstream to write all output into
+    std::stringstream mOutput;
+
+    /// The IOSystem for output
+    IOSystem *mIOSystem;
+
+    /// Path of the directory where the scene will be exported
+    const std::string mPath;
+
+    /// Name of the file (without extension) where the scene will be exported
+    const std::string mFile;
+
+    /// The scene to be written
+    const aiScene *const mScene;
+    std::string mSceneId;
+    bool mAdd_root_node = false;
+
+    /// current line start string, contains the current indentation for simple stream insertion
+    std::string startstr;
+    /// current line end string for simple stream insertion
+    const std::string endstr;
+
+    // pair of color and texture - texture precedences color
+    struct Surface {
+        bool exist;
+        aiColor4D color;
+        std::string texture;
+        size_t channel;
+        Surface() {
+            exist = false;
+            channel = 0;
+        }
+    };
+
+    struct Property {
+        bool exist;
+        ai_real value;
+        Property() :
+                exist(false),
+                value(0.0) {}
+    };
+
+    // summarize a material in an convenient way.
+    struct Material {
+        std::string id;
+        std::string name;
+        std::string shading_model;
+        Surface ambient, diffuse, specular, emissive, reflective, transparent, normal;
+        Property shininess, transparency, index_refraction;
+
+        Material() {}
+    };
+
+    std::map<unsigned int, std::string> textures;
+
+public:
+    /// Dammit C++ - y u no compile two-pass? No I have to add all methods below the struct definitions
+    /// Reads a single surface entry from the given material keys
+    bool ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex);
+    /// Writes an image entry for the given surface
+    void WriteImageEntry(const Surface &pSurface, const std::string &imageId);
+    /// Writes the two parameters necessary for referencing a texture in an effect entry
+    void WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId);
+    /// Writes a color-or-texture entry into an effect definition
+    void WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId);
+    /// Writes a scalar property
+    void WriteFloatEntry(const Property &pProperty, const std::string &pTypeName);
+};
+
+} // namespace Assimp
+
+#endif // !! AI_COLLADAEXPORTER_H_INC

+ 8 - 15
code/Collada/ColladaHelper.cpp → code/AssetLib/Collada/ColladaHelper.cpp

@@ -43,8 +43,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "ColladaHelper.h"
 
-#include <assimp/commonMetaData.h>
 #include <assimp/ParsingUtils.h>
+#include <assimp/commonMetaData.h>
 
 namespace Assimp {
 namespace Collada {
@@ -63,42 +63,35 @@ const MetaKeyPairVector &GetColladaAssimpMetaKeys() {
 
 const MetaKeyPairVector MakeColladaAssimpMetaKeysCamelCase() {
     MetaKeyPairVector result = MakeColladaAssimpMetaKeys();
-    for (auto &val : result)
-    {
+    for (auto &val : result) {
         ToCamelCase(val.first);
     }
     return result;
 };
 
-const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase()
-{
+const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase() {
     static const MetaKeyPairVector result = MakeColladaAssimpMetaKeysCamelCase();
     return result;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Convert underscore_separated to CamelCase: "authoring_tool" becomes "AuthoringTool"
-void ToCamelCase(std::string &text)
-{
+void ToCamelCase(std::string &text) {
     if (text.empty())
         return;
     // Capitalise first character
     auto it = text.begin();
     (*it) = ToUpper(*it);
     ++it;
-    for (/*started above*/ ; it != text.end(); /*iterated below*/)
-    {
-        if ((*it) == '_')
-        {
+    for (/*started above*/; it != text.end(); /*iterated below*/) {
+        if ((*it) == '_') {
             it = text.erase(it);
             if (it != text.end())
                 (*it) = ToUpper(*it);
-        }
-        else
-        {
+        } else {
             // Make lower case
             (*it) = ToLower(*it);
-            ++it;               
+            ++it;
         }
     }
 }

+ 185 - 224
code/Collada/ColladaHelper.h → code/AssetLib/Collada/ColladaHelper.h

@@ -45,31 +45,28 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_COLLADAHELPER_H_INC
 #define AI_COLLADAHELPER_H_INC
 
-#include <map>
-#include <vector>
-#include <set>
-#include <stdint.h>
 #include <assimp/light.h>
-#include <assimp/mesh.h>
 #include <assimp/material.h>
+#include <assimp/mesh.h>
+#include <stdint.h>
+#include <map>
+#include <set>
+#include <vector>
 
 struct aiMaterial;
 
-namespace Assimp    {
-namespace Collada       {
+namespace Assimp {
+namespace Collada {
 
 /** Collada file versions which evolved during the years ... */
-enum FormatVersion
-{
+enum FormatVersion {
     FV_1_5_n,
     FV_1_4_n,
     FV_1_3_n
 };
 
-
 /** Transformation types that can be applied to a node */
-enum TransformType
-{
+enum TransformType {
     TF_LOOKAT,
     TF_ROTATE,
     TF_TRANSLATE,
@@ -79,10 +76,9 @@ enum TransformType
 };
 
 /** Different types of input data to a vertex or face */
-enum InputType
-{
+enum InputType {
     IT_Invalid,
-    IT_Vertex,  // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
+    IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
     IT_Position,
     IT_Normal,
     IT_Texcoord,
@@ -92,15 +88,13 @@ enum InputType
 };
 
 /** Supported controller types */
-enum ControllerType
-{
+enum ControllerType {
     Skin,
     Morph
 };
 
 /** Supported morph methods */
-enum MorphMethod
-{
+enum MorphMethod {
     Normalized,
     Relative
 };
@@ -118,24 +112,21 @@ const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase();
 void ToCamelCase(std::string &text);
 
 /** Contains all data for one of the different transformation types */
-struct Transform
-{
-    std::string mID;  ///< SID of the transform step, by which anim channels address their target node
+struct Transform {
+    std::string mID; ///< SID of the transform step, by which anim channels address their target node
     TransformType mType;
     ai_real f[16]; ///< Interpretation of data depends on the type of the transformation
 };
 
 /** A collada camera. */
-struct Camera
-{
-    Camera()
-        :   mOrtho  (false)
-        ,   mHorFov (10e10f)
-        ,   mVerFov (10e10f)
-        ,   mAspect (10e10f)
-        ,   mZNear  (0.1f)
-        ,   mZFar   (1000.f)
-    {}
+struct Camera {
+    Camera() :
+            mOrtho(false),
+            mHorFov(10e10f),
+            mVerFov(10e10f),
+            mAspect(10e10f),
+            mZNear(0.1f),
+            mZFar(1000.f) {}
 
     // Name of camera
     std::string mName;
@@ -159,19 +150,17 @@ struct Camera
 #define ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET 1e9f
 
 /** A collada light source. */
-struct Light
-{
-    Light()
-        :   mType            (aiLightSource_UNDEFINED)
-        ,   mAttConstant     (1.f)
-        ,   mAttLinear       (0.f)
-        ,   mAttQuadratic    (0.f)
-        ,   mFalloffAngle    (180.f)
-        ,   mFalloffExponent (0.f)
-        ,   mPenumbraAngle   (ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET)
-        ,   mOuterAngle      (ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET)
-        ,   mIntensity       (1.f)
-    {}
+struct Light {
+    Light() :
+            mType(aiLightSource_UNDEFINED),
+            mAttConstant(1.f),
+            mAttLinear(0.f),
+            mAttQuadratic(0.f),
+            mFalloffAngle(180.f),
+            mFalloffExponent(0.f),
+            mPenumbraAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET),
+            mOuterAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET),
+            mIntensity(1.f) {}
 
     //! Type of the light source aiLightSourceType + ambient
     unsigned int mType;
@@ -180,7 +169,7 @@ struct Light
     aiColor3D mColor;
 
     //! Light attenuation
-    ai_real mAttConstant,mAttLinear,mAttQuadratic;
+    ai_real mAttConstant, mAttLinear, mAttQuadratic;
 
     //! Spot light falloff
     ai_real mFalloffAngle;
@@ -198,12 +187,10 @@ struct Light
 };
 
 /** Short vertex index description */
-struct InputSemanticMapEntry
-{
-    InputSemanticMapEntry()
-        :   mSet(0)
-        ,   mType(IT_Invalid)
-    {}
+struct InputSemanticMapEntry {
+    InputSemanticMapEntry() :
+            mSet(0),
+            mType(IT_Invalid) {}
 
     //! Index of set, optional
     unsigned int mSet;
@@ -213,8 +200,7 @@ struct InputSemanticMapEntry
 };
 
 /** Table to map from effect to vertex input semantics */
-struct SemanticMappingTable
-{
+struct SemanticMappingTable {
     //! Name of material
     std::string mMatName;
 
@@ -222,7 +208,7 @@ struct SemanticMappingTable
     std::map<std::string, InputSemanticMapEntry> mMap;
 
     //! For std::find
-    bool operator == (const std::string& s) const {
+    bool operator==(const std::string &s) const {
         return s == mMatName;
     }
 };
@@ -230,8 +216,7 @@ struct SemanticMappingTable
 /** A reference to a mesh inside a node, including materials assigned to the various subgroups.
  * The ID refers to either a mesh or a controller which specifies the mesh
  */
-struct MeshInstance
-{
+struct MeshInstance {
     ///< ID of the mesh or controller to be instanced
     std::string mMeshOrController;
 
@@ -240,34 +225,30 @@ struct MeshInstance
 };
 
 /** A reference to a camera inside a node*/
-struct CameraInstance
-{
-     ///< ID of the camera
+struct CameraInstance {
+    ///< ID of the camera
     std::string mCamera;
 };
 
 /** A reference to a light inside a node*/
-struct LightInstance
-{
-     ///< ID of the camera
+struct LightInstance {
+    ///< ID of the camera
     std::string mLight;
 };
 
 /** A reference to a node inside a node*/
-struct NodeInstance
-{
-     ///< ID of the node
+struct NodeInstance {
+    ///< ID of the node
     std::string mNode;
 };
 
 /** A node in a scene hierarchy */
-struct Node
-{
+struct Node {
     std::string mName;
     std::string mID;
     std::string mSID;
-    Node* mParent;
-    std::vector<Node*> mChildren;
+    Node *mParent;
+    std::vector<Node *> mChildren;
 
     /** Operations in order to calculate the resulting transformation to parent. */
     std::vector<Transform> mTransforms;
@@ -288,80 +269,83 @@ struct Node
     std::string mPrimaryCamera;
 
     //! Constructor. Begin with a zero parent
-    Node()
-    : mParent( nullptr ){
+    Node() :
+            mParent(nullptr) {
         // empty
     }
 
     //! Destructor: delete all children subsequently
     ~Node() {
-        for( std::vector<Node*>::iterator it = mChildren.begin(); it != mChildren.end(); ++it)
+        for (std::vector<Node *>::iterator it = mChildren.begin(); it != mChildren.end(); ++it)
             delete *it;
     }
 };
 
 /** Data source array: either floats or strings */
-struct Data
-{
+struct Data {
     bool mIsStringArray;
     std::vector<ai_real> mValues;
     std::vector<std::string> mStrings;
 };
 
 /** Accessor to a data array */
-struct Accessor
-{
-    size_t mCount;   // in number of objects
-    size_t mSize;    // size of an object, in elements (floats or strings, mostly 1)
-    size_t mOffset;  // in number of values
-    size_t mStride;  // Stride in number of values
+struct Accessor {
+    size_t mCount; // in number of objects
+    size_t mSize; // size of an object, in elements (floats or strings, mostly 1)
+    size_t mOffset; // in number of values
+    size_t mStride; // Stride in number of values
     std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
     size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, that's XYZ, for a color RGBA and so on.
-                          // For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
-    std::string mSource;   // URL of the source array
-    mutable const Data* mData; // Pointer to the source array, if resolved. NULL else
-
-    Accessor()
-    {
-        mCount = 0; mSize = 0; mOffset = 0; mStride = 0; mData = NULL;
+            // For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
+    std::string mSource; // URL of the source array
+    mutable const Data *mData; // Pointer to the source array, if resolved. NULL else
+
+    Accessor() {
+        mCount = 0;
+        mSize = 0;
+        mOffset = 0;
+        mStride = 0;
+        mData = NULL;
         mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0;
     }
 };
 
 /** A single face in a mesh */
-struct Face
-{
+struct Face {
     std::vector<size_t> mIndices;
 };
 
 /** An input channel for mesh data, referring to a single accessor */
-struct InputChannel
-{
-    InputType mType;      // Type of the data
-    size_t mIndex;        // Optional index, if multiple sets of the same data type are given
-    size_t mOffset;       // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
+struct InputChannel {
+    InputType mType; // Type of the data
+    size_t mIndex; // Optional index, if multiple sets of the same data type are given
+    size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
     std::string mAccessor; // ID of the accessor where to read the actual values from.
-    mutable const Accessor* mResolved; // Pointer to the accessor, if resolved. NULL else
+    mutable const Accessor *mResolved; // Pointer to the accessor, if resolved. NULL else
 
-    InputChannel() { mType = IT_Invalid; mIndex = 0; mOffset = 0; mResolved = NULL; }
+    InputChannel() {
+        mType = IT_Invalid;
+        mIndex = 0;
+        mOffset = 0;
+        mResolved = NULL;
+    }
 };
 
 /** Subset of a mesh with a certain material */
-struct SubMesh
-{
+struct SubMesh {
     std::string mMaterial; ///< subgroup identifier
     size_t mNumFaces; ///< number of faces in this submesh
 };
 
 /** Contains data for a single mesh */
-struct Mesh
-{
-    Mesh()
-    {
-        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+struct Mesh {
+    Mesh(const std::string &id) :
+            mId(id) {
+        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i)
             mNumUVComponents[i] = 2;
     }
 
+    const std::string mId;
     std::string mName;
 
     // just to check if there's some sophisticated addressing involved...
@@ -377,7 +361,7 @@ struct Mesh
     std::vector<aiVector3D> mTangents;
     std::vector<aiVector3D> mBitangents;
     std::vector<aiVector3D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
-    std::vector<aiColor4D>  mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+    std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
 
     unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 
@@ -394,8 +378,7 @@ struct Mesh
 };
 
 /** Which type of primitives the ReadPrimitives() function is going to read */
-enum PrimitiveType
-{
+enum PrimitiveType {
     Prim_Invalid,
     Prim_Lines,
     Prim_LineStrip,
@@ -407,8 +390,7 @@ enum PrimitiveType
 };
 
 /** A skeleton controller to deform a mesh with the use of joints */
-struct Controller
-{
+struct Controller {
     // controller type
     ControllerType mType;
 
@@ -436,36 +418,32 @@ struct Controller
     std::vector<size_t> mWeightCounts;
 
     // JointIndex-WeightIndex pairs for all vertices
-    std::vector< std::pair<size_t, size_t> > mWeights;
+    std::vector<std::pair<size_t, size_t>> mWeights;
 
     std::string mMorphTarget;
     std::string mMorphWeight;
 };
 
 /** A collada material. Pretty much the only member is a reference to an effect. */
-struct Material
-{
+struct Material {
     std::string mName;
     std::string mEffect;
 };
 
 /** Type of the effect param */
-enum ParamType
-{
+enum ParamType {
     Param_Sampler,
     Param_Surface
 };
 
 /** A param for an effect. Might be of several types, but they all just refer to each other, so I summarize them */
-struct EffectParam
-{
+struct EffectParam {
     ParamType mType;
     std::string mReference; // to which other thing the param is referring to.
 };
 
 /** Shading type supported by the standard effect spec of Collada */
-enum ShadeType
-{
+enum ShadeType {
     Shade_Invalid,
     Shade_Constant,
     Shade_Lambert,
@@ -474,18 +452,16 @@ enum ShadeType
 };
 
 /** Represents a texture sampler in collada */
-struct Sampler
-{
-    Sampler()
-        :   mWrapU      (true)
-        ,   mWrapV      (true)
-        ,   mMirrorU    ()
-        ,   mMirrorV    ()
-        ,   mOp         (aiTextureOp_Multiply)
-        ,   mUVId       (UINT_MAX)
-        ,   mWeighting  (1.f)
-        ,   mMixWithPrevious (1.f)
-    {}
+struct Sampler {
+    Sampler() :
+            mWrapU(true),
+            mWrapV(true),
+            mMirrorU(),
+            mMirrorV(),
+            mOp(aiTextureOp_Multiply),
+            mUVId(UINT_MAX),
+            mWeighting(1.f),
+            mMixWithPrevious(1.f) {}
 
     /** Name of image reference
      */
@@ -537,18 +513,17 @@ struct Sampler
 
 /** A collada effect. Can contain about anything according to the Collada spec,
     but we limit our version to a reasonable subset. */
-struct Effect
-{
+struct Effect {
     // Shading mode
     ShadeType mShadeType;
 
     // Colors
     aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular,
-        mTransparent, mReflective;
+            mTransparent, mReflective;
 
     // Textures
     Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular,
-        mTexTransparent, mTexBump, mTexReflective;
+            mTexTransparent, mTexBump, mTexReflective;
 
     // Scalar factory
     ai_real mShininess, mRefractIndex, mReflectivity;
@@ -566,30 +541,28 @@ struct Effect
     // Double-sided?
     bool mDoubleSided, mWireframe, mFaceted;
 
-    Effect()
-        : mShadeType    (Shade_Phong)
-        , mEmissive     ( 0, 0, 0, 1)
-        , mAmbient      ( 0.1f, 0.1f, 0.1f, 1)
-        , mDiffuse      ( 0.6f, 0.6f, 0.6f, 1)
-        , mSpecular     ( 0.4f, 0.4f, 0.4f, 1)
-        , mTransparent  ( 0, 0, 0, 1)
-        , mShininess    (10.0f)
-        , mRefractIndex (1.f)
-        , mReflectivity (0.f)
-        , mTransparency (1.f)
-        , mHasTransparency (false)
-        , mRGBTransparency(false)
-        , mInvertTransparency(false)
-        , mDoubleSided  (false)
-        , mWireframe    (false)
-        , mFaceted      (false)
-    {
+    Effect() :
+            mShadeType(Shade_Phong),
+            mEmissive(0, 0, 0, 1),
+            mAmbient(0.1f, 0.1f, 0.1f, 1),
+            mDiffuse(0.6f, 0.6f, 0.6f, 1),
+            mSpecular(0.4f, 0.4f, 0.4f, 1),
+            mTransparent(0, 0, 0, 1),
+            mShininess(10.0f),
+            mRefractIndex(1.f),
+            mReflectivity(0.f),
+            mTransparency(1.f),
+            mHasTransparency(false),
+            mRGBTransparency(false),
+            mInvertTransparency(false),
+            mDoubleSided(false),
+            mWireframe(false),
+            mFaceted(false) {
     }
 };
 
 /** An image, meaning texture */
-struct Image
-{
+struct Image {
     std::string mFileName;
 
     /** Embedded image data */
@@ -600,8 +573,7 @@ struct Image
 };
 
 /** An animation channel. */
-struct AnimationChannel
-{
+struct AnimationChannel {
     /** URL of the data to animate. Could be about anything, but we support only the
      * "NodeID/TransformID.SubElement" notation
      */
@@ -620,8 +592,7 @@ struct AnimationChannel
 };
 
 /** An animation. Container for 0-x animation channels or 0-x animations */
-struct Animation
-{
+struct Animation {
     /** Anim name */
     std::string mName;
 
@@ -629,96 +600,86 @@ struct Animation
     std::vector<AnimationChannel> mChannels;
 
     /** the sub-animations, if any */
-    std::vector<Animation*> mSubAnims;
+    std::vector<Animation *> mSubAnims;
 
     /** Destructor */
-    ~Animation()
-    {
-        for( std::vector<Animation*>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it)
+    ~Animation() {
+        for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it)
             delete *it;
     }
 
-	/** Collect all channels in the animation hierarchy into a single channel list. */
-	void CollectChannelsRecursively(std::vector<AnimationChannel> &channels)
-	{
-		channels.insert(channels.end(), mChannels.begin(), mChannels.end());
+    /** Collect all channels in the animation hierarchy into a single channel list. */
+    void CollectChannelsRecursively(std::vector<AnimationChannel> &channels) {
+        channels.insert(channels.end(), mChannels.begin(), mChannels.end());
 
-		for (std::vector<Animation*>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it)
-		{
-			Animation *pAnim = (*it);
+        for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) {
+            Animation *pAnim = (*it);
 
-			pAnim->CollectChannelsRecursively(channels);
-		}
-	}
+            pAnim->CollectChannelsRecursively(channels);
+        }
+    }
 
-	/** Combine all single-channel animations' channel into the same (parent) animation channel list. */
-	void CombineSingleChannelAnimations()
-	{
-		CombineSingleChannelAnimationsRecursively(this);
-	}
+    /** Combine all single-channel animations' channel into the same (parent) animation channel list. */
+    void CombineSingleChannelAnimations() {
+        CombineSingleChannelAnimationsRecursively(this);
+    }
 
-	void CombineSingleChannelAnimationsRecursively(Animation *pParent)
-	{
-		std::set<std::string> childrenTargets;
-		bool childrenAnimationsHaveDifferentChannels = true;
+    void CombineSingleChannelAnimationsRecursively(Animation *pParent) {
+        std::set<std::string> childrenTargets;
+        bool childrenAnimationsHaveDifferentChannels = true;
 
-		for (std::vector<Animation*>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();)
-		{
-			Animation *anim = *it;
-			CombineSingleChannelAnimationsRecursively(anim);
+        for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
+            Animation *anim = *it;
+            CombineSingleChannelAnimationsRecursively(anim);
 
-			if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 &&
-				childrenTargets.find(anim->mChannels[0].mTarget) == childrenTargets.end()) {
-				childrenTargets.insert(anim->mChannels[0].mTarget);
-			} else {
-				childrenAnimationsHaveDifferentChannels = false;
-			}
+            if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 &&
+                    childrenTargets.find(anim->mChannels[0].mTarget) == childrenTargets.end()) {
+                childrenTargets.insert(anim->mChannels[0].mTarget);
+            } else {
+                childrenAnimationsHaveDifferentChannels = false;
+            }
 
-			++it;
-		}
+            ++it;
+        }
 
-		// We only want to combine animations if they have different channels
-		if (childrenAnimationsHaveDifferentChannels)
-		{
-			for (std::vector<Animation*>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();)
-			{
-				Animation *anim = *it;
+        // We only want to combine animations if they have different channels
+        if (childrenAnimationsHaveDifferentChannels) {
+            for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
+                Animation *anim = *it;
 
-				pParent->mChannels.push_back(anim->mChannels[0]);
+                pParent->mChannels.push_back(anim->mChannels[0]);
 
-				it = pParent->mSubAnims.erase(it);
+                it = pParent->mSubAnims.erase(it);
 
-				delete anim;
-				continue;
-			}
-		}
-	}
+                delete anim;
+                continue;
+            }
+        }
+    }
 };
 
 /** Description of a collada animation channel which has been determined to affect the current node */
-struct ChannelEntry
-{
-    const Collada::AnimationChannel* mChannel; ///> the source channel
+struct ChannelEntry {
+    const Collada::AnimationChannel *mChannel; ///> the source channel
     std::string mTargetId;
-    std::string mTransformId;   // the ID of the transformation step of the node which is influenced
+    std::string mTransformId; // the ID of the transformation step of the node which is influenced
     size_t mTransformIndex; // Index into the node's transform chain to apply the channel to
     size_t mSubElement; // starting index inside the transform data
 
     // resolved data references
-    const Collada::Accessor* mTimeAccessor; ///> Collada accessor to the time values
-    const Collada::Data* mTimeData; ///> Source data array for the time values
-    const Collada::Accessor* mValueAccessor; ///> Collada accessor to the key value values
-    const Collada::Data* mValueData; ///> Source datat array for the key value values
-
-    ChannelEntry()
-      : mChannel()
-      , mTransformIndex()
-      , mSubElement()
-      , mTimeAccessor()
-      , mTimeData()
-      , mValueAccessor()
-      , mValueData()
-   {}
+    const Collada::Accessor *mTimeAccessor; ///> Collada accessor to the time values
+    const Collada::Data *mTimeData; ///> Source data array for the time values
+    const Collada::Accessor *mValueAccessor; ///> Collada accessor to the key value values
+    const Collada::Data *mValueData; ///> Source datat array for the key value values
+
+    ChannelEntry() :
+            mChannel(),
+            mTransformIndex(),
+            mSubElement(),
+            mTimeAccessor(),
+            mTimeData(),
+            mValueAccessor(),
+            mValueData() {}
 };
 
 } // end of namespace Collada

文件差异内容过多而无法显示
+ 230 - 249
code/AssetLib/Collada/ColladaLoader.cpp


+ 0 - 0
code/Collada/ColladaLoader.h → code/AssetLib/Collada/ColladaLoader.h


文件差异内容过多而无法显示
+ 179 - 328
code/AssetLib/Collada/ColladaParser.cpp


+ 392 - 0
code/AssetLib/Collada/ColladaParser.h

@@ -0,0 +1,392 @@
+/*
+ Open Asset Import Library (assimp)
+ ----------------------------------------------------------------------
+
+ Copyright (c) 2006-2020, assimp team
+
+
+ All rights reserved.
+
+ Redistribution and use of this software in source and binary forms,
+ with or without modification, are permitted provided that the
+ following conditions are met:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ * Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------------
+ */
+
+/** @file ColladaParser.h
+ *  @brief Defines the parser helper class for the collada loader
+ */
+
+#ifndef AI_COLLADAPARSER_H_INC
+#define AI_COLLADAPARSER_H_INC
+
+#include "ColladaHelper.h"
+#include <assimp/TinyFormatter.h>
+#include <assimp/ai_assert.h>
+#include <assimp/irrXMLWrapper.h>
+
+namespace Assimp {
+class ZipArchiveIOSystem;
+
+// ------------------------------------------------------------------------------------------
+/** Parser helper class for the Collada loader.
+     *
+     *  Does all the XML reading and builds internal data structures from it,
+     *  but leaves the resolving of all the references to the loader.
+     */
+class ColladaParser {
+    friend class ColladaLoader;
+
+    /** Converts a path read from a collada file to the usual representation */
+    static void UriDecodePath(aiString &ss);
+
+protected:
+    /** Map for generic metadata as aiString */
+    typedef std::map<std::string, aiString> StringMetaData;
+
+    /** Constructor from XML file */
+    ColladaParser(IOSystem *pIOHandler, const std::string &pFile);
+
+    /** Destructor */
+    ~ColladaParser();
+
+    /** Attempts to read the ZAE manifest and returns the DAE to open */
+    static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
+
+    /** Reads the contents of the file */
+    void ReadContents();
+
+    /** Reads the structure of the file */
+    void ReadStructure();
+
+    /** Reads asset information such as coordinate system information and legal blah */
+    void ReadAssetInfo();
+
+    /** Reads contributor information such as author and legal blah */
+    void ReadContributorInfo();
+
+    /** Reads generic metadata into provided map and renames keys for Assimp */
+    void ReadMetaDataItem(StringMetaData &metadata);
+
+    /** Reads the animation library */
+    void ReadAnimationLibrary();
+
+    /** Reads the animation clip library */
+    void ReadAnimationClipLibrary();
+
+    /** Unwrap controllers dependency hierarchy */
+    void PostProcessControllers();
+
+    /** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */
+    void PostProcessRootAnimations();
+
+    /** Reads an animation into the given parent structure */
+    void ReadAnimation(Collada::Animation *pParent);
+
+    /** Reads an animation sampler into the given anim channel */
+    void ReadAnimationSampler(Collada::AnimationChannel &pChannel);
+
+    /** Reads the skeleton controller library */
+    void ReadControllerLibrary();
+
+    /** Reads a controller into the given mesh structure */
+    void ReadController(Collada::Controller &pController);
+
+    /** Reads the joint definitions for the given controller */
+    void ReadControllerJoints(Collada::Controller &pController);
+
+    /** Reads the joint weights for the given controller */
+    void ReadControllerWeights(Collada::Controller &pController);
+
+    /** Reads the image library contents */
+    void ReadImageLibrary();
+
+    /** Reads an image entry into the given image */
+    void ReadImage(Collada::Image &pImage);
+
+    /** Reads the material library */
+    void ReadMaterialLibrary();
+
+    /** Reads a material entry into the given material */
+    void ReadMaterial(Collada::Material &pMaterial);
+
+    /** Reads the camera library */
+    void ReadCameraLibrary();
+
+    /** Reads a camera entry into the given camera */
+    void ReadCamera(Collada::Camera &pCamera);
+
+    /** Reads the light library */
+    void ReadLightLibrary();
+
+    /** Reads a light entry into the given light */
+    void ReadLight(Collada::Light &pLight);
+
+    /** Reads the effect library */
+    void ReadEffectLibrary();
+
+    /** Reads an effect entry into the given effect*/
+    void ReadEffect(Collada::Effect &pEffect);
+
+    /** Reads an COMMON effect profile */
+    void ReadEffectProfileCommon(Collada::Effect &pEffect);
+
+    /** Read sampler properties */
+    void ReadSamplerProperties(Collada::Sampler &pSampler);
+
+    /** Reads an effect entry containing a color or a texture defining that color */
+    void ReadEffectColor(aiColor4D &pColor, Collada::Sampler &pSampler);
+
+    /** Reads an effect entry containing a float */
+    void ReadEffectFloat(ai_real &pFloat);
+
+    /** Reads an effect parameter specification of any kind */
+    void ReadEffectParam(Collada::EffectParam &pParam);
+
+    /** Reads the geometry library contents */
+    void ReadGeometryLibrary();
+
+    /** Reads a geometry from the geometry library. */
+    void ReadGeometry(Collada::Mesh &pMesh);
+
+    /** Reads a mesh from the geometry library */
+    void ReadMesh(Collada::Mesh &pMesh);
+
+    /** Reads a source element - a combination of raw data and an accessor defining
+         * things that should not be redefinable. Yes, that's another rant.
+         */
+    void ReadSource();
+
+    /** Reads a data array holding a number of elements, and stores it in the global library.
+         * Currently supported are array of floats and arrays of strings.
+         */
+    void ReadDataArray();
+
+    /** Reads an accessor and stores it in the global library under the given ID -
+         * accessors use the ID of the parent <source> element
+         */
+    void ReadAccessor(const std::string &pID);
+
+    /** Reads input declarations of per-vertex mesh data into the given mesh */
+    void ReadVertexData(Collada::Mesh &pMesh);
+
+    /** Reads input declarations of per-index mesh data into the given mesh */
+    void ReadIndexData(Collada::Mesh &pMesh);
+
+    /** Reads a single input channel element and stores it in the given array, if valid */
+    void ReadInputChannel(std::vector<Collada::InputChannel> &poChannels);
+
+    /** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
+    size_t ReadPrimitives(Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
+            size_t pNumPrimitives, const std::vector<size_t> &pVCount, Collada::PrimitiveType pPrimType);
+
+    /** Copies the data for a single primitive into the mesh, based on the InputChannels */
+    void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset,
+            Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
+            size_t currentPrimitive, const std::vector<size_t> &indices);
+
+    /** Reads one triangle of a tristrip into the mesh */
+    void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh &pMesh,
+            std::vector<Collada::InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices);
+
+    /** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
+    void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh);
+
+    /** Reads the library of node hierarchies and scene parts */
+    void ReadSceneLibrary();
+
+    /** Reads a scene node's contents including children and stores it in the given node */
+    void ReadSceneNode(Collada::Node *pNode);
+
+    /** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */
+    void ReadNodeTransformation(Collada::Node *pNode, Collada::TransformType pType);
+
+    /** Reads a mesh reference in a node and adds it to the node's mesh list */
+    void ReadNodeGeometry(Collada::Node *pNode);
+
+    /** Reads the collada scene */
+    void ReadScene();
+
+    // Processes bind_vertex_input and bind elements
+    void ReadMaterialVertexInputBinding(Collada::SemanticMappingTable &tbl);
+
+    /** Reads embedded textures from a ZAE archive*/
+    void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
+
+protected:
+    /** Aborts the file reading with an exception */
+    AI_WONT_RETURN void ThrowException(const std::string &pError) const AI_WONT_RETURN_SUFFIX;
+    void ReportWarning(const char *msg, ...);
+
+    /** Skips all data until the end node of the current element */
+    void SkipElement();
+
+    /** Skips all data until the end node of the given element */
+    void SkipElement(const char *pElement);
+
+    /** Compares the current xml element name to the given string and returns true if equal */
+    bool IsElement(const char *pName) const;
+
+    /** Tests for the opening tag of the given element, throws an exception if not found */
+    void TestOpening(const char *pName);
+
+    /** Tests for the closing tag of the given element, throws an exception if not found */
+    void TestClosing(const char *pName);
+
+    /** Checks the present element for the presence of the attribute, returns its index
+         or throws an exception if not found */
+    int GetAttribute(const char *pAttr) const;
+
+    /** Returns the index of the named attribute or -1 if not found. Does not throw,
+         therefore useful for optional attributes */
+    int TestAttribute(const char *pAttr) const;
+
+    /** Reads the text contents of an element, throws an exception if not given.
+         Skips leading whitespace. */
+    const char *GetTextContent();
+
+    /** Reads the text contents of an element, returns NULL if not given.
+         Skips leading whitespace. */
+    const char *TestTextContent();
+
+    /** Reads a single bool from current text content */
+    bool ReadBoolFromTextContent();
+
+    /** Reads a single float from current text content */
+    ai_real ReadFloatFromTextContent();
+
+    /** Calculates the resulting transformation from all the given transform steps */
+    aiMatrix4x4 CalculateResultTransform(const std::vector<Collada::Transform> &pTransforms) const;
+
+    /** Determines the input data type for the given semantic string */
+    Collada::InputType GetTypeForSemantic(const std::string &pSemantic);
+
+    /** Finds the item in the given library by its reference, throws if not found */
+    template <typename Type>
+    const Type &ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const;
+
+protected:
+    /** Filename, for a verbose error message */
+    std::string mFileName;
+
+    /** XML reader, member for everyday use */
+    irr::io::IrrXMLReader *mReader;
+
+    /** All data arrays found in the file by ID. Might be referred to by actually
+         everyone. Collada, you are a steaming pile of indirection. */
+    typedef std::map<std::string, Collada::Data> DataLibrary;
+    DataLibrary mDataLibrary;
+
+    /** Same for accessors which define how the data in a data array is accessed. */
+    typedef std::map<std::string, Collada::Accessor> AccessorLibrary;
+    AccessorLibrary mAccessorLibrary;
+
+    /** Mesh library: mesh by ID */
+    typedef std::map<std::string, Collada::Mesh *> MeshLibrary;
+    MeshLibrary mMeshLibrary;
+
+    /** node library: root node of the hierarchy part by ID */
+    typedef std::map<std::string, Collada::Node *> NodeLibrary;
+    NodeLibrary mNodeLibrary;
+
+    /** Image library: stores texture properties by ID */
+    typedef std::map<std::string, Collada::Image> ImageLibrary;
+    ImageLibrary mImageLibrary;
+
+    /** Effect library: surface attributes by ID */
+    typedef std::map<std::string, Collada::Effect> EffectLibrary;
+    EffectLibrary mEffectLibrary;
+
+    /** Material library: surface material by ID */
+    typedef std::map<std::string, Collada::Material> MaterialLibrary;
+    MaterialLibrary mMaterialLibrary;
+
+    /** Light library: surface light by ID */
+    typedef std::map<std::string, Collada::Light> LightLibrary;
+    LightLibrary mLightLibrary;
+
+    /** Camera library: surface material by ID */
+    typedef std::map<std::string, Collada::Camera> CameraLibrary;
+    CameraLibrary mCameraLibrary;
+
+    /** Controller library: joint controllers by ID */
+    typedef std::map<std::string, Collada::Controller> ControllerLibrary;
+    ControllerLibrary mControllerLibrary;
+
+    /** Animation library: animation references by ID */
+    typedef std::map<std::string, Collada::Animation *> AnimationLibrary;
+    AnimationLibrary mAnimationLibrary;
+
+    /** Animation clip library: clip animation references by ID */
+    typedef std::vector<std::pair<std::string, std::vector<std::string>>> AnimationClipLibrary;
+    AnimationClipLibrary mAnimationClipLibrary;
+
+    /** Pointer to the root node. Don't delete, it just points to one of
+         the nodes in the node library. */
+    Collada::Node *mRootNode;
+
+    /** Root animation container */
+    Collada::Animation mAnims;
+
+    /** Size unit: how large compared to a meter */
+    ai_real mUnitSize;
+
+    /** Which is the up vector */
+    enum { UP_X,
+        UP_Y,
+        UP_Z } mUpDirection;
+
+    /** Asset metadata (global for scene) */
+    StringMetaData mAssetMetaData;
+
+    /** Collada file format version */
+    Collada::FormatVersion mFormat;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Check for element match
+inline bool ColladaParser::IsElement(const char *pName) const {
+    ai_assert(mReader->getNodeType() == irr::io::EXN_ELEMENT);
+    return ::strcmp(mReader->getNodeName(), pName) == 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds the item in the given library by its reference, throws if not found
+template <typename Type>
+const Type &ColladaParser::ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const {
+    typename std::map<std::string, Type>::const_iterator it = pLibrary.find(pURL);
+    if (it == pLibrary.end())
+        ThrowException(Formatter::format() << "Unable to resolve library reference \"" << pURL << "\".");
+    return it->second;
+}
+
+} // end of namespace Assimp
+
+#endif // AI_COLLADAPARSER_H_INC

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

@@ -135,7 +135,7 @@ public:
                 for(;splitter->length() && splitter->at(0) != '}'; splitter++, cnt++);
 
                 splitter++;
-                ASSIMP_LOG_DEBUG((Formatter::format("DXF: skipped over control group ("),cnt," lines)"));
+                ASSIMP_LOG_VERBOSE_DEBUG((Formatter::format("DXF: skipped over control group ("),cnt," lines)"));
             }
         } catch(std::logic_error&) {
             ai_assert(!splitter);

+ 5 - 5
code/DXF/DXFLoader.cpp → code/AssetLib/DXF/DXFLoader.cpp

@@ -48,8 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
 
-#include "DXF/DXFLoader.h"
-#include "DXF/DXFHelper.h"
+#include "AssetLib/DXF/DXFLoader.h"
+#include "AssetLib/DXF/DXFHelper.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 
 #include <assimp/ParsingUtils.h>
@@ -241,7 +241,7 @@ void DXFImporter::ConvertMeshes(aiScene* pScene, DXF::FileData& output) {
             }
         }
 
-        ASSIMP_LOG_DEBUG_F("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount);
+        ASSIMP_LOG_VERBOSE_DEBUG_F("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount);
     }
 
     if (! output.blocks.size()  ) {
@@ -473,7 +473,7 @@ void DXFImporter::ParseBlocks(DXF::LineReader& reader, DXF::FileData& output) {
         ++reader;
     }
 
-    ASSIMP_LOG_DEBUG_F("DXF: got ", output.blocks.size()," entries in BLOCKS" );
+    ASSIMP_LOG_VERBOSE_DEBUG_F("DXF: got ", output.blocks.size()," entries in BLOCKS" );
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -549,7 +549,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output)
         ++reader;
     }
 
-    ASSIMP_LOG_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), 
+    ASSIMP_LOG_VERBOSE_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), 
         " inserted blocks in ENTITIES" );
 }
 

+ 0 - 0
code/DXF/DXFLoader.h → code/AssetLib/DXF/DXFLoader.h


+ 0 - 0
code/FBX/FBXAnimation.cpp → code/AssetLib/FBX/FBXAnimation.cpp


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

@@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <stdint.h>
 #include <assimp/Exceptional.h>
 #include <assimp/ByteSwapper.h>
+#include <assimp/DefaultLogger.hpp>
 
 namespace Assimp {
 namespace FBX {
@@ -426,7 +427,8 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 // TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
 void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
 {
-    ai_assert(input);
+	ai_assert(input);
+	ASSIMP_LOG_DEBUG("Tokenizing binary FBX file");
 
     if(length < 0x1b) {
         TokenizeError("file is too short",0);
@@ -451,6 +453,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
 	/*Result ignored*/ ReadByte(input, cursor, input + length);
 	/*Result ignored*/ ReadByte(input, cursor, input + length);
 	const uint32_t version = ReadWord(input, cursor, input + length);
+	ASSIMP_LOG_DEBUG_F("FBX version: ", version);
 	const bool is64bits = version >= 7500;
     const char *end = input + length;
     while (cursor < end ) {

+ 34 - 34
code/FBX/FBXCommon.h → code/AssetLib/FBX/FBXCommon.h

@@ -48,40 +48,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 
 namespace Assimp {
-namespace FBX
-{
-    const std::string NULL_RECORD = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
-        '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
-        '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'
-    }; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)
-    const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings
-    const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
-    const int64_t SECOND = 46186158000; // FBX's kTime unit
-
-    // rotation order. We'll probably use EulerXYZ for everything
-    enum RotOrder {
-        RotOrder_EulerXYZ = 0,
-        RotOrder_EulerXZY,
-        RotOrder_EulerYZX,
-        RotOrder_EulerYXZ,
-        RotOrder_EulerZXY,
-        RotOrder_EulerZYX,
-
-        RotOrder_SphericXYZ,
-
-        RotOrder_MAX // end-of-enum sentinel
-    };
-
-    // transformation inheritance method. Most of the time RSrs
-    enum TransformInheritance {
-        TransformInheritance_RrSs = 0,
-        TransformInheritance_RSrs,
-        TransformInheritance_Rrs,
-
-        TransformInheritance_MAX // end-of-enum sentinel
-    };
-}
-}
+namespace FBX {
+
+const std::string NULL_RECORD = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
+    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
+}; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)
+const std::string SEPARATOR = { '\x00', '\x01' }; // for use inside strings
+const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
+const int64_t SECOND = 46186158000; // FBX's kTime unit
+
+// rotation order. We'll probably use EulerXYZ for everything
+enum RotOrder {
+    RotOrder_EulerXYZ = 0,
+    RotOrder_EulerXZY,
+    RotOrder_EulerYZX,
+    RotOrder_EulerYXZ,
+    RotOrder_EulerZXY,
+    RotOrder_EulerZYX,
+
+    RotOrder_SphericXYZ,
+
+    RotOrder_MAX // end-of-enum sentinel
+};
+
+// transformation inheritance method. Most of the time RSrs
+enum TransformInheritance {
+    TransformInheritance_RrSs = 0,
+    TransformInheritance_RSrs,
+    TransformInheritance_Rrs,
+
+    TransformInheritance_MAX // end-of-enum sentinel
+};
+} // namespace FBX
+} // namespace Assimp
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 #endif // AI_FBXCOMMON_H_INC

+ 0 - 0
code/FBX/FBXCompileConfig.h → code/AssetLib/FBX/FBXCompileConfig.h


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

@@ -0,0 +1,3510 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXConverter.cpp
+ *  @brief Implementation of the FBX DOM -> aiScene converter
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXConverter.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "FBXUtil.h"
+
+#include <assimp/MathFunctions.h>
+#include <assimp/StringComparison.h>
+
+#include <assimp/scene.h>
+
+#include <assimp/CreateAnimMesh.h>
+#include <assimp/StringUtils.h>
+#include <assimp/commonMetaData.h>
+
+#include <stdlib.h>
+#include <cstdint>
+#include <iomanip>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <sstream>
+#include <tuple>
+#include <vector>
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+#define MAGIC_NODE_TAG "_$AssimpFbx$"
+
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
+
+FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBones) :
+        defaultMaterialIndex(),
+        mMeshes(),
+        lights(),
+        cameras(),
+        textures(),
+        materials_converted(),
+        textures_converted(),
+        meshes_converted(),
+        node_anim_chain_bits(),
+        mNodeNames(),
+        anim_fps(),
+        mSceneOut(out),
+        doc(doc),
+        mRemoveEmptyBones(removeEmptyBones) {
+    // animations need to be converted first since this will
+    // populate the node_anim_chain_bits map, which is needed
+    // to determine which nodes need to be generated.
+    ConvertAnimations();
+    // Embedded textures in FBX could be connected to nothing but to itself,
+    // for instance Texture -> Video connection only but not to the main graph,
+    // The idea here is to traverse all objects to find these Textures and convert them,
+    // so later during material conversion it will find converted texture in the textures_converted array.
+    if (doc.Settings().readTextures) {
+        ConvertOrphanedEmbeddedTextures();
+    }
+    ConvertRootNode();
+
+    if (doc.Settings().readAllMaterials) {
+        // unfortunately this means we have to evaluate all objects
+        for (const ObjectMap::value_type &v : doc.Objects()) {
+
+            const Object *ob = v.second->Get();
+            if (!ob) {
+                continue;
+            }
+
+            const Material *mat = dynamic_cast<const Material *>(ob);
+            if (mat) {
+
+                if (materials_converted.find(mat) == materials_converted.end()) {
+                    ConvertMaterial(*mat, 0);
+                }
+            }
+        }
+    }
+
+    ConvertGlobalSettings();
+    TransferDataToScene();
+
+    // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
+    // to make sure the scene passes assimp's validation. FBX files
+    // need not contain geometry (i.e. camera animations, raw armatures).
+    if (out->mNumMeshes == 0) {
+        out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+    }
+}
+
+FBXConverter::~FBXConverter() {
+    std::for_each(mMeshes.begin(), mMeshes.end(), Util::delete_fun<aiMesh>());
+    std::for_each(materials.begin(), materials.end(), Util::delete_fun<aiMaterial>());
+    std::for_each(animations.begin(), animations.end(), Util::delete_fun<aiAnimation>());
+    std::for_each(lights.begin(), lights.end(), Util::delete_fun<aiLight>());
+    std::for_each(cameras.begin(), cameras.end(), Util::delete_fun<aiCamera>());
+    std::for_each(textures.begin(), textures.end(), Util::delete_fun<aiTexture>());
+}
+
+void FBXConverter::ConvertRootNode() {
+    mSceneOut->mRootNode = new aiNode();
+    std::string unique_name;
+    GetUniqueName("RootNode", unique_name);
+    mSceneOut->mRootNode->mName.Set(unique_name);
+
+    // root has ID 0
+    ConvertNodes(0L, mSceneOut->mRootNode, mSceneOut->mRootNode);
+}
+
+static std::string getAncestorBaseName(const aiNode *node) {
+    const char *nodeName = nullptr;
+    size_t length = 0;
+    while (node && (!nodeName || length == 0)) {
+        nodeName = node->mName.C_Str();
+        length = node->mName.length;
+        node = node->mParent;
+    }
+
+    if (!nodeName || length == 0) {
+        return {};
+    }
+    // could be std::string_view if c++17 available
+    return std::string(nodeName, length);
+}
+
+// Make unique name
+std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiNode &parent) {
+    std::string original_name = FixNodeName(model->Name());
+    if (original_name.empty()) {
+        original_name = getAncestorBaseName(&parent);
+    }
+    std::string unique_name;
+    GetUniqueName(original_name, unique_name);
+    return unique_name;
+}
+
+/// todo: pre-build node hierarchy
+/// todo: get bone from stack
+/// todo: make map of aiBone* to aiNode*
+/// then update convert clusters to the new format
+void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
+    const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
+
+    std::vector<aiNode *> nodes;
+    nodes.reserve(conns.size());
+
+    std::vector<aiNode *> nodes_chain;
+    std::vector<aiNode *> post_nodes_chain;
+
+    try {
+        for (const Connection *con : conns) {
+            // ignore object-property links
+            if (con->PropertyName().length()) {
+                // really important we document why this is ignored.
+                FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
+                continue; //?
+            }
+
+            // convert connection source object into Object base class
+            const Object *const object = con->SourceObject();
+            if (nullptr == object) {
+                FBXImporter::LogError("failed to convert source object for Model link");
+                continue;
+            }
+
+            // FBX Model::Cube, Model::Bone001, etc elements
+            // This detects if we can cast the object into this model structure.
+            const Model *const model = dynamic_cast<const Model *>(object);
+
+            if (nullptr != model) {
+                nodes_chain.clear();
+                post_nodes_chain.clear();
+
+                aiMatrix4x4 new_abs_transform = parent->mTransformation;
+                std::string node_name = FixNodeName(model->Name());
+                // even though there is only a single input node, the design of
+                // assimp (or rather: the complicated transformation chain that
+                // is employed by fbx) means that we may need multiple aiNode's
+                // to represent a fbx node's transformation.
+
+                // generate node transforms - this includes pivot data
+                // if need_additional_node is true then you t
+                const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
+
+                // assert that for the current node we must have at least a single transform
+                ai_assert(nodes_chain.size());
+
+                if (need_additional_node) {
+                    nodes_chain.push_back(new aiNode(node_name));
+                }
+
+                //setup metadata on newest node
+                SetupNodeMetadata(*model, *nodes_chain.back());
+
+                // link all nodes in a row
+                aiNode *last_parent = parent;
+                for (aiNode *child : nodes_chain) {
+                    ai_assert(child);
+
+                    if (last_parent != parent) {
+                        last_parent->mNumChildren = 1;
+                        last_parent->mChildren = new aiNode *[1];
+                        last_parent->mChildren[0] = child;
+                    }
+
+                    child->mParent = last_parent;
+                    last_parent = child;
+
+                    new_abs_transform *= child->mTransformation;
+                }
+
+                // attach geometry
+                ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform);
+
+                // check if there will be any child nodes
+                const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
+
+                // if so, link the geometric transform inverse nodes
+                // before we attach any child nodes
+                if (child_conns.size()) {
+                    for (aiNode *postnode : post_nodes_chain) {
+                        ai_assert(postnode);
+
+                        if (last_parent != parent) {
+                            last_parent->mNumChildren = 1;
+                            last_parent->mChildren = new aiNode *[1];
+                            last_parent->mChildren[0] = postnode;
+                        }
+
+                        postnode->mParent = last_parent;
+                        last_parent = postnode;
+
+                        new_abs_transform *= postnode->mTransformation;
+                    }
+                } else {
+                    // free the nodes we allocated as we don't need them
+                    Util::delete_fun<aiNode> deleter;
+                    std::for_each(
+                            post_nodes_chain.begin(),
+                            post_nodes_chain.end(),
+                            deleter);
+                }
+
+                // recursion call - child nodes
+                ConvertNodes(model->ID(), last_parent, root_node);
+
+                if (doc.Settings().readLights) {
+                    ConvertLights(*model, node_name);
+                }
+
+                if (doc.Settings().readCameras) {
+                    ConvertCameras(*model, node_name);
+                }
+
+                nodes.push_back(nodes_chain.front());
+                nodes_chain.clear();
+            }
+        }
+
+        if (nodes.size()) {
+            parent->mChildren = new aiNode *[nodes.size()]();
+            parent->mNumChildren = static_cast<unsigned int>(nodes.size());
+
+            std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren);
+        } else {
+            parent->mNumChildren = 0;
+            parent->mChildren = nullptr;
+        }
+
+    } catch (std::exception &) {
+        Util::delete_fun<aiNode> deleter;
+        std::for_each(nodes.begin(), nodes.end(), deleter);
+        std::for_each(nodes_chain.begin(), nodes_chain.end(), deleter);
+        std::for_each(post_nodes_chain.begin(), post_nodes_chain.end(), deleter);
+    }
+}
+
+void FBXConverter::ConvertLights(const Model &model, const std::string &orig_name) {
+    const std::vector<const NodeAttribute *> &node_attrs = model.GetAttributes();
+    for (const NodeAttribute *attr : node_attrs) {
+        const Light *const light = dynamic_cast<const Light *>(attr);
+        if (light) {
+            ConvertLight(*light, orig_name);
+        }
+    }
+}
+
+void FBXConverter::ConvertCameras(const Model &model, const std::string &orig_name) {
+    const std::vector<const NodeAttribute *> &node_attrs = model.GetAttributes();
+    for (const NodeAttribute *attr : node_attrs) {
+        const Camera *const cam = dynamic_cast<const Camera *>(attr);
+        if (cam) {
+            ConvertCamera(*cam, orig_name);
+        }
+    }
+}
+
+void FBXConverter::ConvertLight(const Light &light, const std::string &orig_name) {
+    lights.push_back(new aiLight());
+    aiLight *const out_light = lights.back();
+
+    out_light->mName.Set(orig_name);
+
+    const float intensity = light.Intensity() / 100.0f;
+    const aiVector3D &col = light.Color();
+
+    out_light->mColorDiffuse = aiColor3D(col.x, col.y, col.z);
+    out_light->mColorDiffuse.r *= intensity;
+    out_light->mColorDiffuse.g *= intensity;
+    out_light->mColorDiffuse.b *= intensity;
+
+    out_light->mColorSpecular = out_light->mColorDiffuse;
+
+    //lights are defined along negative y direction
+    out_light->mPosition = aiVector3D(0.0f);
+    out_light->mDirection = aiVector3D(0.0f, -1.0f, 0.0f);
+    out_light->mUp = aiVector3D(0.0f, 0.0f, -1.0f);
+
+    switch (light.LightType()) {
+        case Light::Type_Point:
+            out_light->mType = aiLightSource_POINT;
+            break;
+
+        case Light::Type_Directional:
+            out_light->mType = aiLightSource_DIRECTIONAL;
+            break;
+
+        case Light::Type_Spot:
+            out_light->mType = aiLightSource_SPOT;
+            out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle());
+            out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle());
+            break;
+
+        case Light::Type_Area:
+            FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED");
+            out_light->mType = aiLightSource_UNDEFINED;
+            break;
+
+        case Light::Type_Volume:
+            FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED");
+            out_light->mType = aiLightSource_UNDEFINED;
+            break;
+        default:
+            ai_assert(false);
+    }
+
+    float decay = light.DecayStart();
+    switch (light.DecayType()) {
+        case Light::Decay_None:
+            out_light->mAttenuationConstant = decay;
+            out_light->mAttenuationLinear = 0.0f;
+            out_light->mAttenuationQuadratic = 0.0f;
+            break;
+        case Light::Decay_Linear:
+            out_light->mAttenuationConstant = 0.0f;
+            out_light->mAttenuationLinear = 2.0f / decay;
+            out_light->mAttenuationQuadratic = 0.0f;
+            break;
+        case Light::Decay_Quadratic:
+            out_light->mAttenuationConstant = 0.0f;
+            out_light->mAttenuationLinear = 0.0f;
+            out_light->mAttenuationQuadratic = 2.0f / (decay * decay);
+            break;
+        case Light::Decay_Cubic:
+            FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic");
+            out_light->mAttenuationQuadratic = 1.0f;
+            break;
+        default:
+            ai_assert(false);
+            break;
+    }
+}
+
+void FBXConverter::ConvertCamera(const Camera &cam, const std::string &orig_name) {
+    cameras.push_back(new aiCamera());
+    aiCamera *const out_camera = cameras.back();
+
+    out_camera->mName.Set(orig_name);
+
+    out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
+
+    out_camera->mPosition = aiVector3D(0.0f);
+    out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
+    out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
+
+    out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
+
+    out_camera->mClipPlaneNear = cam.NearPlane();
+    out_camera->mClipPlaneFar = cam.FarPlane();
+
+    out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
+    out_camera->mClipPlaneNear = cam.NearPlane();
+    out_camera->mClipPlaneFar = cam.FarPlane();
+}
+
+void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueName) {
+    uniqueName = name;
+    auto it_pair = mNodeNames.insert({ name, 0 }); // duplicate node name instance count
+    unsigned int &i = it_pair.first->second;
+    while (!it_pair.second) {
+        i++;
+        std::ostringstream ext;
+        ext << name << std::setfill('0') << std::setw(3) << i;
+        uniqueName = ext.str();
+        it_pair = mNodeNames.insert({ uniqueName, 0 });
+    }
+}
+
+const char *FBXConverter::NameTransformationComp(TransformationComp comp) {
+    switch (comp) {
+        case TransformationComp_Translation:
+            return "Translation";
+        case TransformationComp_RotationOffset:
+            return "RotationOffset";
+        case TransformationComp_RotationPivot:
+            return "RotationPivot";
+        case TransformationComp_PreRotation:
+            return "PreRotation";
+        case TransformationComp_Rotation:
+            return "Rotation";
+        case TransformationComp_PostRotation:
+            return "PostRotation";
+        case TransformationComp_RotationPivotInverse:
+            return "RotationPivotInverse";
+        case TransformationComp_ScalingOffset:
+            return "ScalingOffset";
+        case TransformationComp_ScalingPivot:
+            return "ScalingPivot";
+        case TransformationComp_Scaling:
+            return "Scaling";
+        case TransformationComp_ScalingPivotInverse:
+            return "ScalingPivotInverse";
+        case TransformationComp_GeometricScaling:
+            return "GeometricScaling";
+        case TransformationComp_GeometricRotation:
+            return "GeometricRotation";
+        case TransformationComp_GeometricTranslation:
+            return "GeometricTranslation";
+        case TransformationComp_GeometricScalingInverse:
+            return "GeometricScalingInverse";
+        case TransformationComp_GeometricRotationInverse:
+            return "GeometricRotationInverse";
+        case TransformationComp_GeometricTranslationInverse:
+            return "GeometricTranslationInverse";
+        case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+        default:
+            break;
+    }
+
+    ai_assert(false);
+
+    return nullptr;
+}
+
+const char *FBXConverter::NameTransformationCompProperty(TransformationComp comp) {
+    switch (comp) {
+        case TransformationComp_Translation:
+            return "Lcl Translation";
+        case TransformationComp_RotationOffset:
+            return "RotationOffset";
+        case TransformationComp_RotationPivot:
+            return "RotationPivot";
+        case TransformationComp_PreRotation:
+            return "PreRotation";
+        case TransformationComp_Rotation:
+            return "Lcl Rotation";
+        case TransformationComp_PostRotation:
+            return "PostRotation";
+        case TransformationComp_RotationPivotInverse:
+            return "RotationPivotInverse";
+        case TransformationComp_ScalingOffset:
+            return "ScalingOffset";
+        case TransformationComp_ScalingPivot:
+            return "ScalingPivot";
+        case TransformationComp_Scaling:
+            return "Lcl Scaling";
+        case TransformationComp_ScalingPivotInverse:
+            return "ScalingPivotInverse";
+        case TransformationComp_GeometricScaling:
+            return "GeometricScaling";
+        case TransformationComp_GeometricRotation:
+            return "GeometricRotation";
+        case TransformationComp_GeometricTranslation:
+            return "GeometricTranslation";
+        case TransformationComp_GeometricScalingInverse:
+            return "GeometricScalingInverse";
+        case TransformationComp_GeometricRotationInverse:
+            return "GeometricRotationInverse";
+        case TransformationComp_GeometricTranslationInverse:
+            return "GeometricTranslationInverse";
+        case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+            break;
+    }
+
+    ai_assert(false);
+
+    return nullptr;
+}
+
+aiVector3D FBXConverter::TransformationCompDefaultValue(TransformationComp comp) {
+    // XXX a neat way to solve the never-ending special cases for scaling
+    // would be to do everything in log space!
+    return comp == TransformationComp_Scaling ? aiVector3D(1.f, 1.f, 1.f) : aiVector3D();
+}
+
+void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D &rotation, aiMatrix4x4 &out) {
+    if (mode == Model::RotOrder_SphericXYZ) {
+        FBXImporter::LogError("Unsupported RotationMode: SphericXYZ");
+        out = aiMatrix4x4();
+        return;
+    }
+
+    const float angle_epsilon = Math::getEpsilon<float>();
+
+    out = aiMatrix4x4();
+
+    bool is_id[3] = { true, true, true };
+
+    aiMatrix4x4 temp[3];
+    if (std::fabs(rotation.z) > angle_epsilon) {
+        aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z), temp[2]);
+        is_id[2] = false;
+    }
+    if (std::fabs(rotation.y) > angle_epsilon) {
+        aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y), temp[1]);
+        is_id[1] = false;
+    }
+    if (std::fabs(rotation.x) > angle_epsilon) {
+        aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x), temp[0]);
+        is_id[0] = false;
+    }
+
+    int order[3] = { -1, -1, -1 };
+
+    // note: rotation order is inverted since we're left multiplying as is usual in assimp
+    switch (mode) {
+        case Model::RotOrder_EulerXYZ:
+            order[0] = 2;
+            order[1] = 1;
+            order[2] = 0;
+            break;
+
+        case Model::RotOrder_EulerXZY:
+            order[0] = 1;
+            order[1] = 2;
+            order[2] = 0;
+            break;
+
+        case Model::RotOrder_EulerYZX:
+            order[0] = 0;
+            order[1] = 2;
+            order[2] = 1;
+            break;
+
+        case Model::RotOrder_EulerYXZ:
+            order[0] = 2;
+            order[1] = 0;
+            order[2] = 1;
+            break;
+
+        case Model::RotOrder_EulerZXY:
+            order[0] = 1;
+            order[1] = 0;
+            order[2] = 2;
+            break;
+
+        case Model::RotOrder_EulerZYX:
+            order[0] = 0;
+            order[1] = 1;
+            order[2] = 2;
+            break;
+
+        default:
+            ai_assert(false);
+            break;
+    }
+
+    ai_assert(order[0] >= 0);
+    ai_assert(order[0] <= 2);
+    ai_assert(order[1] >= 0);
+    ai_assert(order[1] <= 2);
+    ai_assert(order[2] >= 0);
+    ai_assert(order[2] <= 2);
+
+    if (!is_id[order[0]]) {
+        out = temp[order[0]];
+    }
+
+    if (!is_id[order[1]]) {
+        out = out * temp[order[1]];
+    }
+
+    if (!is_id[order[2]]) {
+        out = out * temp[order[2]];
+    }
+}
+
+bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
+    const PropertyTable &props = model.Props();
+    bool ok;
+
+    const float zero_epsilon = 1e-6f;
+    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);
+
+        if (comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation ||
+            comp == TransformationComp_PreRotation || comp == TransformationComp_PostRotation) {
+            continue;
+        }
+
+        bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling);
+
+        const aiVector3D &v = PropertyGet<aiVector3D>(props, NameTransformationCompProperty(comp), ok);
+        if (ok && scale_compare) {
+            if ((v - all_ones).SquareLength() > zero_epsilon) {
+                return true;
+            }
+        } else if (ok) {
+            if (v.SquareLength() > zero_epsilon) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+std::string FBXConverter::NameTransformationChainNode(const std::string &name, TransformationComp comp) {
+    return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
+}
+
+bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std::string &name, std::vector<aiNode *> &output_nodes,
+        std::vector<aiNode *> &post_output_nodes) {
+    const PropertyTable &props = model.Props();
+    const Model::RotOrder rot = model.RotationOrder();
+
+    bool ok;
+
+    aiMatrix4x4 chain[TransformationComp_MAXIMUM];
+
+    ai_assert(TransformationComp_MAXIMUM < 32);
+    std::uint32_t chainBits = 0;
+    // A node won't need a node chain if it only has these.
+    const std::uint32_t chainMaskSimple = (1 << TransformationComp_Translation) + (1 << TransformationComp_Scaling) + (1 << TransformationComp_Rotation);
+    // A node will need a node chain if it has any of these.
+    const std::uint32_t chainMaskComplex = ((1 << (TransformationComp_MAXIMUM)) - 1) - chainMaskSimple;
+
+    std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
+
+    // generate transformation matrices for all the different transformation components
+    const float zero_epsilon = Math::getEpsilon<float>();
+    const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
+
+    const aiVector3D &PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
+    if (ok && PreRotation.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_PreRotation);
+
+        GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]);
+    }
+
+    const aiVector3D &PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
+    if (ok && PostRotation.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_PostRotation);
+
+        GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]);
+    }
+
+    const aiVector3D &RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok);
+    if (ok && RotationPivot.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_RotationPivot) | (1 << TransformationComp_RotationPivotInverse);
+
+        aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]);
+        aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]);
+    }
+
+    const aiVector3D &RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok);
+    if (ok && RotationOffset.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_RotationOffset);
+
+        aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]);
+    }
+
+    const aiVector3D &ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok);
+    if (ok && ScalingOffset.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_ScalingOffset);
+
+        aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]);
+    }
+
+    const aiVector3D &ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok);
+    if (ok && ScalingPivot.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_ScalingPivot) | (1 << TransformationComp_ScalingPivotInverse);
+
+        aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]);
+        aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]);
+    }
+
+    const aiVector3D &Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok);
+    if (ok && Translation.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_Translation);
+
+        aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]);
+    }
+
+    const aiVector3D &Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok);
+    if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_Scaling);
+
+        aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]);
+    }
+
+    const aiVector3D &Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok);
+    if (ok && Rotation.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_Rotation);
+
+        GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
+    }
+
+    const aiVector3D &GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
+    if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_GeometricScaling);
+        aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
+        aiVector3D GeometricScalingInverse = GeometricScaling;
+        bool canscale = true;
+        for (unsigned int i = 0; i < 3; ++i) {
+            if (std::fabs(GeometricScalingInverse[i]) > zero_epsilon) {
+                GeometricScalingInverse[i] = 1.0f / GeometricScaling[i];
+            } else {
+                FBXImporter::LogError("cannot invert geometric scaling matrix with a 0.0 scale component");
+                canscale = false;
+                break;
+            }
+        }
+        if (canscale) {
+            chainBits = chainBits | (1 << TransformationComp_GeometricScalingInverse);
+            aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]);
+        }
+    }
+
+    const aiVector3D &GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
+    if (ok && GeometricRotation.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_GeometricRotation) | (1 << TransformationComp_GeometricRotationInverse);
+        GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]);
+        GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]);
+        chain[TransformationComp_GeometricRotationInverse].Inverse();
+    }
+
+    const aiVector3D &GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
+    if (ok && GeometricTranslation.SquareLength() > zero_epsilon) {
+        chainBits = chainBits | (1 << TransformationComp_GeometricTranslation) | (1 << TransformationComp_GeometricTranslationInverse);
+        aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]);
+        aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]);
+    }
+
+    // is_complex needs to be consistent with NeedsComplexTransformationChain()
+    // or the interplay between this code and the animation converter would
+    // not be guaranteed.
+    //ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0));
+
+    // now, if we have more than just Translation, Scaling and Rotation,
+    // we need to generate a full node chain to accommodate for assimp's
+    // lack to express pivots and offsets.
+    if ((chainBits & chainMaskComplex) && doc.Settings().preservePivots) {
+        FBXImporter::LogInfo("generating full transformation chain for node: " + name);
+
+        // query the anim_chain_bits dictionary to find out which chain elements
+        // have associated node animation channels. These can not be dropped
+        // even if they have identity transform in bind pose.
+        NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name);
+        const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second);
+
+        unsigned int bit = 0x1;
+        for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
+            const TransformationComp comp = static_cast<TransformationComp>(i);
+
+            if ((chainBits & bit) == 0 && (anim_chain_bitmask & bit) == 0) {
+                continue;
+            }
+
+            if (comp == TransformationComp_PostRotation) {
+                chain[i] = chain[i].Inverse();
+            }
+
+            aiNode *nd = new aiNode();
+            nd->mName.Set(NameTransformationChainNode(name, comp));
+            nd->mTransformation = chain[i];
+
+            // geometric inverses go in a post-node chain
+            if (comp == TransformationComp_GeometricScalingInverse ||
+                    comp == TransformationComp_GeometricRotationInverse ||
+                    comp == TransformationComp_GeometricTranslationInverse) {
+                post_output_nodes.push_back(nd);
+            } else {
+                output_nodes.push_back(nd);
+            }
+        }
+
+        ai_assert(output_nodes.size());
+        return true;
+    }
+
+    // else, we can just multiply the matrices together
+    aiNode *nd = new aiNode();
+    output_nodes.push_back(nd);
+
+    // name passed to the method is already unique
+    nd->mName.Set(name);
+    // for (const auto &transform : chain) {
+    // skip inverse chain for no preservePivots
+    for (unsigned int i = TransformationComp_Translation; i < TransformationComp_MAXIMUM; i++) {
+      nd->mTransformation = nd->mTransformation * chain[i];
+    }
+    return false;
+}
+  
+void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) {
+    const PropertyTable &props = model.Props();
+    DirectPropertyMap unparsedProperties = props.GetUnparsedProperties();
+
+    // create metadata on node
+    const std::size_t numStaticMetaData = 2;
+    aiMetadata *data = aiMetadata::Alloc(static_cast<unsigned int>(unparsedProperties.size() + numStaticMetaData));
+    nd.mMetaData = data;
+    int index = 0;
+
+    // find user defined properties (3ds Max)
+    data->Set(index++, "UserProperties", aiString(PropertyGet<std::string>(props, "UDP3DSMAX", "")));
+    // preserve the info that a node was marked as Null node in the original file.
+    data->Set(index++, "IsNull", model.IsNull() ? true : false);
+
+    // add unparsed properties to the node's metadata
+    for (const DirectPropertyMap::value_type &prop : unparsedProperties) {
+        // Interpret the property as a concrete type
+        if (const TypedProperty<bool> *interpretedBool = prop.second->As<TypedProperty<bool>>()) {
+            data->Set(index++, prop.first, interpretedBool->Value());
+        } else if (const TypedProperty<int> *interpretedInt = prop.second->As<TypedProperty<int>>()) {
+            data->Set(index++, prop.first, interpretedInt->Value());
+        } else if (const TypedProperty<uint64_t> *interpretedUint64 = prop.second->As<TypedProperty<uint64_t>>()) {
+            data->Set(index++, prop.first, interpretedUint64->Value());
+        } else if (const TypedProperty<float> *interpretedFloat = prop.second->As<TypedProperty<float>>()) {
+            data->Set(index++, prop.first, interpretedFloat->Value());
+        } else if (const TypedProperty<std::string> *interpretedString = prop.second->As<TypedProperty<std::string>>()) {
+            data->Set(index++, prop.first, aiString(interpretedString->Value()));
+        } else if (const TypedProperty<aiVector3D> *interpretedVec3 = prop.second->As<TypedProperty<aiVector3D>>()) {
+            data->Set(index++, prop.first, interpretedVec3->Value());
+        } else {
+            ai_assert(false);
+        }
+    }
+}
+
+void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
+        const aiMatrix4x4 &absolute_transform) {
+    const std::vector<const Geometry *> &geos = model.GetGeometry();
+
+    std::vector<unsigned int> meshes;
+    meshes.reserve(geos.size());
+
+    for (const Geometry *geo : geos) {
+
+        const MeshGeometry *const mesh = dynamic_cast<const MeshGeometry *>(geo);
+        const LineGeometry *const line = dynamic_cast<const LineGeometry *>(geo);
+        if (mesh) {
+            const std::vector<unsigned int> &indices = ConvertMesh(*mesh, model, parent, root_node,
+                    absolute_transform);
+            std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
+        } else if (line) {
+            const std::vector<unsigned int> &indices = ConvertLine(*line, root_node);
+            std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
+        } else {
+            FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
+        }
+    }
+
+    if (meshes.size()) {
+        parent->mMeshes = new unsigned int[meshes.size()]();
+        parent->mNumMeshes = static_cast<unsigned int>(meshes.size());
+
+        std::swap_ranges(meshes.begin(), meshes.end(), parent->mMeshes);
+    }
+}
+
+std::vector<unsigned int>
+FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
+        const aiMatrix4x4 &absolute_transform) {
+    std::vector<unsigned int> temp;
+
+    MeshMap::const_iterator it = meshes_converted.find(&mesh);
+    if (it != meshes_converted.end()) {
+        std::copy((*it).second.begin(), (*it).second.end(), std::back_inserter(temp));
+        return temp;
+    }
+
+    const std::vector<aiVector3D> &vertices = mesh.GetVertices();
+    const std::vector<unsigned int> &faces = mesh.GetFaceIndexCounts();
+    if (vertices.empty() || faces.empty()) {
+        FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
+        return temp;
+    }
+
+    // one material per mesh maps easily to aiMesh. Multiple material
+    // meshes need to be split.
+    const MatIndexArray &mindices = mesh.GetMaterialIndices();
+    if (doc.Settings().readMaterials && !mindices.empty()) {
+        const MatIndexArray::value_type base = mindices[0];
+        for (MatIndexArray::value_type index : mindices) {
+            if (index != base) {
+                return ConvertMeshMultiMaterial(mesh, model, parent, root_node, absolute_transform);
+            }
+        }
+    }
+
+    // faster code-path, just copy the data
+    temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node));
+    return temp;
+}
+
+std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry &line, aiNode *root_node) {
+    std::vector<unsigned int> temp;
+
+    const std::vector<aiVector3D> &vertices = line.GetVertices();
+    const std::vector<int> &indices = line.GetIndices();
+    if (vertices.empty() || indices.empty()) {
+        FBXImporter::LogWarn("ignoring empty line: " + line.Name());
+        return temp;
+    }
+
+    aiMesh *const out_mesh = SetupEmptyMesh(line, root_node);
+    out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+
+    // copy vertices
+    out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+    out_mesh->mVertices = new aiVector3D[out_mesh->mNumVertices];
+    std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices);
+
+    //Number of line segments (faces) is "Number of Points - Number of Endpoints"
+    //N.B.: Endpoints in FbxLine are denoted by negative indices.
+    //If such an Index is encountered, add 1 and multiply by -1 to get the real index.
+    unsigned int epcount = 0;
+    for (unsigned i = 0; i < indices.size(); i++) {
+        if (indices[i] < 0) {
+            epcount++;
+        }
+    }
+    unsigned int pcount = static_cast<unsigned int>(indices.size());
+    unsigned int scount = out_mesh->mNumFaces = pcount - epcount;
+
+    aiFace *fac = out_mesh->mFaces = new aiFace[scount]();
+    for (unsigned int i = 0; i < pcount; ++i) {
+        if (indices[i] < 0) continue;
+        aiFace &f = *fac++;
+        f.mNumIndices = 2; //2 == aiPrimitiveType_LINE
+        f.mIndices = new unsigned int[2];
+        f.mIndices[0] = indices[i];
+        int segid = indices[(i + 1 == pcount ? 0 : i + 1)]; //If we have reached he last point, wrap around
+        f.mIndices[1] = (segid < 0 ? (segid + 1) * -1 : segid); //Convert EndPoint Index to normal Index
+    }
+    temp.push_back(static_cast<unsigned int>(mMeshes.size() - 1));
+    return temp;
+}
+
+aiMesh *FBXConverter::SetupEmptyMesh(const Geometry &mesh, aiNode *parent) {
+    aiMesh *const out_mesh = new aiMesh();
+    mMeshes.push_back(out_mesh);
+    meshes_converted[&mesh].push_back(static_cast<unsigned int>(mMeshes.size() - 1));
+
+    // set name
+    std::string name = mesh.Name();
+    if (name.substr(0, 10) == "Geometry::") {
+        name = name.substr(10);
+    }
+
+    if (name.length()) {
+        out_mesh->mName.Set(name);
+    } else {
+        out_mesh->mName = parent->mName;
+    }
+
+    return out_mesh;
+}
+
+unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
+        const aiMatrix4x4 &absolute_transform, aiNode *parent,
+        aiNode *) {
+    const MatIndexArray &mindices = mesh.GetMaterialIndices();
+    aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
+
+    const std::vector<aiVector3D> &vertices = mesh.GetVertices();
+    const std::vector<unsigned int> &faces = mesh.GetFaceIndexCounts();
+
+    // copy vertices
+    out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+    out_mesh->mVertices = new aiVector3D[vertices.size()];
+
+    std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices);
+
+    // generate dummy faces
+    out_mesh->mNumFaces = static_cast<unsigned int>(faces.size());
+    aiFace *fac = out_mesh->mFaces = new aiFace[faces.size()]();
+
+    unsigned int cursor = 0;
+    for (unsigned int pcount : faces) {
+        aiFace &f = *fac++;
+        f.mNumIndices = pcount;
+        f.mIndices = new unsigned int[pcount];
+        switch (pcount) {
+            case 1:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+                break;
+            case 2:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+                break;
+            case 3:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+                break;
+            default:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+                break;
+        }
+        for (unsigned int i = 0; i < pcount; ++i) {
+            f.mIndices[i] = cursor++;
+        }
+    }
+
+    // copy normals
+    const std::vector<aiVector3D> &normals = mesh.GetNormals();
+    if (normals.size()) {
+        ai_assert(normals.size() == vertices.size());
+
+        out_mesh->mNormals = new aiVector3D[vertices.size()];
+        std::copy(normals.begin(), normals.end(), out_mesh->mNormals);
+    }
+
+    // copy tangents - assimp requires both tangents and bitangents (binormals)
+    // to be present, or neither of them. Compute binormals from normals
+    // and tangents if needed.
+    const std::vector<aiVector3D> &tangents = mesh.GetTangents();
+    const std::vector<aiVector3D> *binormals = &mesh.GetBinormals();
+
+    if (tangents.size()) {
+        std::vector<aiVector3D> tempBinormals;
+        if (!binormals->size()) {
+            if (normals.size()) {
+                tempBinormals.resize(normals.size());
+                for (unsigned int i = 0; i < tangents.size(); ++i) {
+                    tempBinormals[i] = normals[i] ^ tangents[i];
+                }
+
+                binormals = &tempBinormals;
+            } else {
+                binormals = nullptr;
+            }
+        }
+
+        if (binormals) {
+            ai_assert(tangents.size() == vertices.size());
+            ai_assert(binormals->size() == vertices.size());
+
+            out_mesh->mTangents = new aiVector3D[vertices.size()];
+            std::copy(tangents.begin(), tangents.end(), out_mesh->mTangents);
+
+            out_mesh->mBitangents = new aiVector3D[vertices.size()];
+            std::copy(binormals->begin(), binormals->end(), out_mesh->mBitangents);
+        }
+    }
+
+    // copy texture coords
+    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+        const std::vector<aiVector2D> &uvs = mesh.GetTextureCoords(i);
+        if (uvs.empty()) {
+            break;
+        }
+
+        aiVector3D *out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+        for (const aiVector2D &v : uvs) {
+            *out_uv++ = aiVector3D(v.x, v.y, 0.0f);
+        }
+
+        out_mesh->mNumUVComponents[i] = 2;
+    }
+
+    // copy vertex colors
+    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+        const std::vector<aiColor4D> &colors = mesh.GetVertexColors(i);
+        if (colors.empty()) {
+            break;
+        }
+
+        out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+        std::copy(colors.begin(), colors.end(), out_mesh->mColors[i]);
+    }
+
+    if (!doc.Settings().readMaterials || mindices.empty()) {
+        FBXImporter::LogError("no material assigned to mesh, setting default material");
+        out_mesh->mMaterialIndex = GetDefaultMaterial();
+    } else {
+        ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]);
+    }
+
+    if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) {
+        ConvertWeights(out_mesh, mesh, absolute_transform, parent, NO_MATERIAL_SEPARATION, nullptr);
+    }
+
+    std::vector<aiAnimMesh *> animMeshes;
+    for (const BlendShape *blendShape : mesh.GetBlendShapes()) {
+        for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
+            const std::vector<const ShapeGeometry *> &shapeGeometries = blendShapeChannel->GetShapeGeometries();
+            for (size_t i = 0; i < shapeGeometries.size(); i++) {
+                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
+                const ShapeGeometry *shapeGeometry = shapeGeometries.at(i);
+                const std::vector<aiVector3D> &curVertices = shapeGeometry->GetVertices();
+                const std::vector<aiVector3D> &curNormals = shapeGeometry->GetNormals();
+                const std::vector<unsigned int> &curIndices = shapeGeometry->GetIndices();
+                animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
+                for (size_t j = 0; j < curIndices.size(); j++) {
+                    const unsigned int curIndex = curIndices.at(j);
+                    aiVector3D vertex = curVertices.at(j);
+                    aiVector3D normal = curNormals.at(j);
+                    unsigned int count = 0;
+                    const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
+                    for (unsigned int k = 0; k < count; k++) {
+                        unsigned int index = outIndices[k];
+                        animMesh->mVertices[index] += vertex;
+                        if (animMesh->mNormals != nullptr) {
+                            animMesh->mNormals[index] += normal;
+                            animMesh->mNormals[index].NormalizeSafe();
+                        }
+                    }
+                }
+                animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f;
+                animMeshes.push_back(animMesh);
+            }
+        }
+    }
+    const size_t numAnimMeshes = animMeshes.size();
+    if (numAnimMeshes > 0) {
+        out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes);
+        out_mesh->mAnimMeshes = new aiAnimMesh *[numAnimMeshes];
+        for (size_t i = 0; i < numAnimMeshes; i++) {
+            out_mesh->mAnimMeshes[i] = animMeshes.at(i);
+        }
+    }
+    return static_cast<unsigned int>(mMeshes.size() - 1);
+}
+
+std::vector<unsigned int>
+FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
+        aiNode *root_node,
+        const aiMatrix4x4 &absolute_transform) {
+    const MatIndexArray &mindices = mesh.GetMaterialIndices();
+    ai_assert(mindices.size());
+
+    std::set<MatIndexArray::value_type> had;
+    std::vector<unsigned int> indices;
+
+    for (MatIndexArray::value_type index : mindices) {
+        if (had.find(index) == had.end()) {
+
+            indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node, absolute_transform));
+            had.insert(index);
+        }
+    }
+
+    return indices;
+}
+
+unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
+        MatIndexArray::value_type index,
+        aiNode *parent, aiNode *,
+        const aiMatrix4x4 &absolute_transform) {
+    aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
+
+    const MatIndexArray &mindices = mesh.GetMaterialIndices();
+    const std::vector<aiVector3D> &vertices = mesh.GetVertices();
+    const std::vector<unsigned int> &faces = mesh.GetFaceIndexCounts();
+
+    const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != nullptr;
+
+    unsigned int count_faces = 0;
+    unsigned int count_vertices = 0;
+
+    // count faces
+    std::vector<unsigned int>::const_iterator itf = faces.begin();
+    for (MatIndexArray::const_iterator it = mindices.begin(),
+                                       end = mindices.end();
+            it != end; ++it, ++itf) {
+        if ((*it) != index) {
+            continue;
+        }
+        ++count_faces;
+        count_vertices += *itf;
+    }
+
+    ai_assert(count_faces);
+    ai_assert(count_vertices);
+
+    // mapping from output indices to DOM indexing, needed to resolve weights or blendshapes
+    std::vector<unsigned int> reverseMapping;
+    std::map<unsigned int, unsigned int> translateIndexMap;
+    if (process_weights || mesh.GetBlendShapes().size() > 0) {
+        reverseMapping.resize(count_vertices);
+    }
+
+    // allocate output data arrays, but don't fill them yet
+    out_mesh->mNumVertices = count_vertices;
+    out_mesh->mVertices = new aiVector3D[count_vertices];
+
+    out_mesh->mNumFaces = count_faces;
+    aiFace *fac = out_mesh->mFaces = new aiFace[count_faces]();
+
+    // allocate normals
+    const std::vector<aiVector3D> &normals = mesh.GetNormals();
+    if (normals.size()) {
+        ai_assert(normals.size() == vertices.size());
+        out_mesh->mNormals = new aiVector3D[vertices.size()];
+    }
+
+    // allocate tangents, binormals.
+    const std::vector<aiVector3D> &tangents = mesh.GetTangents();
+    const std::vector<aiVector3D> *binormals = &mesh.GetBinormals();
+    std::vector<aiVector3D> tempBinormals;
+
+    if (tangents.size()) {
+        if (!binormals->size()) {
+            if (normals.size()) {
+                // XXX this computes the binormals for the entire mesh, not only
+                // the part for which we need them.
+                tempBinormals.resize(normals.size());
+                for (unsigned int i = 0; i < tangents.size(); ++i) {
+                    tempBinormals[i] = normals[i] ^ tangents[i];
+                }
+
+                binormals = &tempBinormals;
+            } else {
+                binormals = nullptr;
+            }
+        }
+
+        if (binormals) {
+            ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
+
+            out_mesh->mTangents = new aiVector3D[vertices.size()];
+            out_mesh->mBitangents = new aiVector3D[vertices.size()];
+        }
+    }
+
+    // allocate texture coords
+    unsigned int num_uvs = 0;
+    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) {
+        const std::vector<aiVector2D> &uvs = mesh.GetTextureCoords(i);
+        if (uvs.empty()) {
+            break;
+        }
+
+        out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+        out_mesh->mNumUVComponents[i] = 2;
+    }
+
+    // allocate vertex colors
+    unsigned int num_vcs = 0;
+    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) {
+        const std::vector<aiColor4D> &colors = mesh.GetVertexColors(i);
+        if (colors.empty()) {
+            break;
+        }
+
+        out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+    }
+
+    unsigned int cursor = 0, in_cursor = 0;
+
+    itf = faces.begin();
+    for (MatIndexArray::const_iterator it = mindices.begin(), end = mindices.end(); it != end; ++it, ++itf) {
+        const unsigned int pcount = *itf;
+        if ((*it) != index) {
+            in_cursor += pcount;
+            continue;
+        }
+
+        aiFace &f = *fac++;
+
+        f.mNumIndices = pcount;
+        f.mIndices = new unsigned int[pcount];
+        switch (pcount) {
+            case 1:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+                break;
+            case 2:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+                break;
+            case 3:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+                break;
+            default:
+                out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+                break;
+        }
+        for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) {
+            f.mIndices[i] = cursor;
+
+            if (reverseMapping.size()) {
+                reverseMapping[cursor] = in_cursor;
+                translateIndexMap[in_cursor] = cursor;
+            }
+
+            out_mesh->mVertices[cursor] = vertices[in_cursor];
+
+            if (out_mesh->mNormals) {
+                out_mesh->mNormals[cursor] = normals[in_cursor];
+            }
+
+            if (out_mesh->mTangents) {
+                out_mesh->mTangents[cursor] = tangents[in_cursor];
+                out_mesh->mBitangents[cursor] = (*binormals)[in_cursor];
+            }
+
+            for (unsigned int j = 0; j < num_uvs; ++j) {
+                const std::vector<aiVector2D> &uvs = mesh.GetTextureCoords(j);
+                out_mesh->mTextureCoords[j][cursor] = aiVector3D(uvs[in_cursor].x, uvs[in_cursor].y, 0.0f);
+            }
+
+            for (unsigned int j = 0; j < num_vcs; ++j) {
+                const std::vector<aiColor4D> &cols = mesh.GetVertexColors(j);
+                out_mesh->mColors[j][cursor] = cols[in_cursor];
+            }
+        }
+    }
+
+    ConvertMaterialForMesh(out_mesh, model, mesh, index);
+
+    if (process_weights) {
+        ConvertWeights(out_mesh, mesh, absolute_transform, parent, index, &reverseMapping);
+    }
+
+    std::vector<aiAnimMesh *> animMeshes;
+    for (const BlendShape *blendShape : mesh.GetBlendShapes()) {
+        for (const BlendShapeChannel *blendShapeChannel : blendShape->BlendShapeChannels()) {
+            const std::vector<const ShapeGeometry *> &shapeGeometries = blendShapeChannel->GetShapeGeometries();
+            for (size_t i = 0; i < shapeGeometries.size(); i++) {
+                aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
+                const ShapeGeometry *shapeGeometry = shapeGeometries.at(i);
+                const std::vector<aiVector3D> &curVertices = shapeGeometry->GetVertices();
+                const std::vector<aiVector3D> &curNormals = shapeGeometry->GetNormals();
+                const std::vector<unsigned int> &curIndices = shapeGeometry->GetIndices();
+                animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
+                for (size_t j = 0; j < curIndices.size(); j++) {
+                    unsigned int curIndex = curIndices.at(j);
+                    aiVector3D vertex = curVertices.at(j);
+                    aiVector3D normal = curNormals.at(j);
+                    unsigned int count = 0;
+                    const unsigned int *outIndices = mesh.ToOutputVertexIndex(curIndex, count);
+                    for (unsigned int k = 0; k < count; k++) {
+                        unsigned int outIndex = outIndices[k];
+                        if (translateIndexMap.find(outIndex) == translateIndexMap.end())
+                            continue;
+                        unsigned int transIndex = translateIndexMap[outIndex];
+                        animMesh->mVertices[transIndex] += vertex;
+                        if (animMesh->mNormals != nullptr) {
+                            animMesh->mNormals[transIndex] += normal;
+                            animMesh->mNormals[transIndex].NormalizeSafe();
+                        }
+                    }
+                }
+                animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f;
+                animMeshes.push_back(animMesh);
+            }
+        }
+    }
+
+    const size_t numAnimMeshes = animMeshes.size();
+    if (numAnimMeshes > 0) {
+        out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes);
+        out_mesh->mAnimMeshes = new aiAnimMesh *[numAnimMeshes];
+        for (size_t i = 0; i < numAnimMeshes; i++) {
+            out_mesh->mAnimMeshes[i] = animMeshes.at(i);
+        }
+    }
+
+    return static_cast<unsigned int>(mMeshes.size() - 1);
+}
+
+void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
+        const aiMatrix4x4 &absolute_transform,
+        aiNode *parent, unsigned int materialIndex,
+        std::vector<unsigned int> *outputVertStartIndices) {
+    ai_assert(geo.DeformerSkin());
+
+    std::vector<size_t> out_indices;
+    std::vector<size_t> index_out_indices;
+    std::vector<size_t> count_out_indices;
+
+    const Skin &sk = *geo.DeformerSkin();
+
+    std::vector<aiBone *> bones;
+
+    const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
+    ai_assert(no_mat_check || outputVertStartIndices);
+
+    try {
+        // iterate over the sub deformers
+        for (const Cluster *cluster : sk.Clusters()) {
+            ai_assert(cluster);
+
+            const WeightIndexArray &indices = cluster->GetIndices();
+
+            const MatIndexArray &mats = geo.GetMaterialIndices();
+
+            const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
+
+            count_out_indices.clear();
+            index_out_indices.clear();
+            out_indices.clear();
+
+            // now check if *any* of these weights is contained in the output mesh,
+            // taking notes so we don't need to do it twice.
+            for (WeightIndexArray::value_type index : indices) {
+
+                unsigned int count = 0;
+                const unsigned int *const out_idx = geo.ToOutputVertexIndex(index, count);
+                // ToOutputVertexIndex only returns nullptr if index is out of bounds
+                // which should never happen
+                ai_assert(out_idx != nullptr);
+
+                index_out_indices.push_back(no_index_sentinel);
+                count_out_indices.push_back(0);
+
+                for (unsigned int i = 0; i < count; ++i) {
+                    if (no_mat_check || static_cast<size_t>(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) {
+
+                        if (index_out_indices.back() == no_index_sentinel) {
+                            index_out_indices.back() = out_indices.size();
+                        }
+
+                        if (no_mat_check) {
+                            out_indices.push_back(out_idx[i]);
+                        } else {
+                            // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
+                            const std::vector<unsigned int>::iterator it = std::lower_bound(
+                                    outputVertStartIndices->begin(),
+                                    outputVertStartIndices->end(),
+                                    out_idx[i]);
+
+                            out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
+                        }
+
+                        ++count_out_indices.back();
+                    }
+                }
+            }
+
+            // if we found at least one, generate the output bones
+            // XXX this could be heavily simplified by collecting the bone
+            // data in a single step.
+            ConvertCluster(bones, cluster, out_indices, index_out_indices,
+                    count_out_indices, absolute_transform, parent);
+        }
+
+        bone_map.clear();
+    } catch (std::exception &) {
+        std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>());
+        throw;
+    }
+
+    if (bones.empty()) {
+        out->mBones = nullptr;
+        out->mNumBones = 0;
+        return;
+    } else {
+        out->mBones = new aiBone *[bones.size()]();
+        out->mNumBones = static_cast<unsigned int>(bones.size());
+
+        std::swap_ranges(bones.begin(), bones.end(), out->mBones);
+    }
+}
+
+const aiNode *GetNodeByName(aiNode *current_node) {
+    aiNode *iter = current_node;
+    //printf("Child count: %d", iter->mNumChildren);
+    return iter;
+}
+
+void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
+        std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
+        std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
+        aiNode *) {
+    ai_assert(cl); // make sure cluster valid
+    std::string deformer_name = cl->TargetNode()->Name();
+    aiString bone_name = aiString(FixNodeName(deformer_name));
+
+    aiBone *bone = nullptr;
+
+    if (bone_map.count(deformer_name)) {
+        ASSIMP_LOG_VERBOSE_DEBUG_F("retrieved bone from lookup ", bone_name.C_Str(), ". Deformer:", deformer_name);
+        bone = bone_map[deformer_name];
+    } else {
+        ASSIMP_LOG_VERBOSE_DEBUG_F("created new bone ", bone_name.C_Str(), ". Deformer: ", deformer_name);
+        bone = new aiBone();
+        bone->mName = bone_name;
+
+        // store local transform link for post processing
+        bone->mOffsetMatrix = cl->TransformLink();
+        bone->mOffsetMatrix.Inverse();
+
+        aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
+
+        bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
+
+        //
+        // Now calculate the aiVertexWeights
+        //
+
+        aiVertexWeight *cursor = nullptr;
+
+        bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
+        cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
+
+        const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
+        const WeightArray &weights = cl->GetWeights();
+
+        const size_t c = index_out_indices.size();
+        for (size_t i = 0; i < c; ++i) {
+            const size_t index_index = index_out_indices[i];
+
+            if (index_index == no_index_sentinel) {
+                continue;
+            }
+
+            const size_t cc = count_out_indices[i];
+            for (size_t j = 0; j < cc; ++j) {
+                // cursor runs from first element relative to the start
+                // or relative to the start of the next indexes.
+                aiVertexWeight &out_weight = *cursor++;
+
+                out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
+                out_weight.mWeight = weights[i];
+            }
+        }
+
+        bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
+    }
+
+    ASSIMP_LOG_DEBUG_F("bone research: Indicies size: ", out_indices.size());
+
+    // lookup must be populated in case something goes wrong
+    // this also allocates bones to mesh instance outside
+    local_mesh_bones.push_back(bone);
+}
+
+void FBXConverter::ConvertMaterialForMesh(aiMesh *out, const Model &model, const MeshGeometry &geo,
+        MatIndexArray::value_type materialIndex) {
+    // locate source materials for this mesh
+    const std::vector<const Material *> &mats = model.GetMaterials();
+    if (static_cast<unsigned int>(materialIndex) >= mats.size() || materialIndex < 0) {
+        FBXImporter::LogError("material index out of bounds, setting default material");
+        out->mMaterialIndex = GetDefaultMaterial();
+        return;
+    }
+
+    const Material *const mat = mats[materialIndex];
+    MaterialMap::const_iterator it = materials_converted.find(mat);
+    if (it != materials_converted.end()) {
+        out->mMaterialIndex = (*it).second;
+        return;
+    }
+
+    out->mMaterialIndex = ConvertMaterial(*mat, &geo);
+    materials_converted[mat] = out->mMaterialIndex;
+}
+
+unsigned int FBXConverter::GetDefaultMaterial() {
+    if (defaultMaterialIndex) {
+        return defaultMaterialIndex - 1;
+    }
+
+    aiMaterial *out_mat = new aiMaterial();
+    materials.push_back(out_mat);
+
+    const aiColor3D diffuse = aiColor3D(0.8f, 0.8f, 0.8f);
+    out_mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+
+    aiString s;
+    s.Set(AI_DEFAULT_MATERIAL_NAME);
+
+    out_mat->AddProperty(&s, AI_MATKEY_NAME);
+
+    defaultMaterialIndex = static_cast<unsigned int>(materials.size());
+    return defaultMaterialIndex - 1;
+}
+
+unsigned int FBXConverter::ConvertMaterial(const Material &material, const MeshGeometry *const mesh) {
+    const PropertyTable &props = material.Props();
+
+    // generate empty output material
+    aiMaterial *out_mat = new aiMaterial();
+    materials_converted[&material] = static_cast<unsigned int>(materials.size());
+
+    materials.push_back(out_mat);
+
+    aiString str;
+
+    // strip Material:: prefix
+    std::string name = material.Name();
+    if (name.substr(0, 10) == "Material::") {
+        name = name.substr(10);
+    }
+
+    // set material name if not empty - this could happen
+    // and there should be no key for it in this case.
+    if (name.length()) {
+        str.Set(name);
+        out_mat->AddProperty(&str, AI_MATKEY_NAME);
+    }
+
+    // Set the shading mode as best we can: The FBX specification only mentions Lambert and Phong, and only Phong is mentioned in Assimp's aiShadingMode enum.
+    if (material.GetShadingModel() == "phong") {
+        aiShadingMode shadingMode = aiShadingMode_Phong;
+        out_mat->AddProperty<aiShadingMode>(&shadingMode, 1, AI_MATKEY_SHADING_MODEL);
+    }
+
+    // shading stuff and colors
+    SetShadingPropertiesCommon(out_mat, props);
+    SetShadingPropertiesRaw(out_mat, props, material.Textures(), mesh);
+
+    // texture assignments
+    SetTextureProperties(out_mat, material.Textures(), mesh);
+    SetTextureProperties(out_mat, material.LayeredTextures(), mesh);
+
+    return static_cast<unsigned int>(materials.size() - 1);
+}
+
+unsigned int FBXConverter::ConvertVideo(const Video &video) {
+    // generate empty output texture
+    aiTexture *out_tex = new aiTexture();
+    textures.push_back(out_tex);
+
+    // assuming the texture is compressed
+    out_tex->mWidth = static_cast<unsigned int>(video.ContentLength()); // total data size
+    out_tex->mHeight = 0; // fixed to 0
+
+    // steal the data from the Video to avoid an additional copy
+    out_tex->pcData = reinterpret_cast<aiTexel *>(const_cast<Video &>(video).RelinquishContent());
+
+    // try to extract a hint from the file extension
+    const std::string &filename = video.RelativeFilename().empty() ? video.FileName() : video.RelativeFilename();
+    std::string ext = BaseImporter::GetExtension(filename);
+
+    if (ext == "jpeg") {
+        ext = "jpg";
+    }
+
+    if (ext.size() <= 3) {
+        memcpy(out_tex->achFormatHint, ext.c_str(), ext.size());
+    }
+
+    out_tex->mFilename.Set(filename.c_str());
+
+    return static_cast<unsigned int>(textures.size() - 1);
+}
+
+aiString FBXConverter::GetTexturePath(const Texture *tex) {
+    aiString path;
+    path.Set(tex->RelativeFilename());
+
+    const Video *media = tex->Media();
+    if (media != nullptr) {
+        bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
+        unsigned int index=0;
+
+        VideoMap::const_iterator it = textures_converted.find(*media);
+        if (it != textures_converted.end()) {
+            index = (*it).second;
+            textureReady = true;
+        } else {
+            if (media->ContentLength() > 0) {
+                index = ConvertVideo(*media);
+                textures_converted[*media] = index;
+                textureReady = true;
+            }
+        }
+
+        // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready
+        if (doc.Settings().useLegacyEmbeddedTextureNaming) {
+            if (textureReady) {
+                // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
+                // In FBX files textures are now stored internally by Assimp with their filename included
+                // Now Assimp can lookup through the loaded textures after all data is processed
+                // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it
+                // This may occur on this case too, it has to be studied
+                path.data[0] = '*';
+                path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+            }
+        }
+    }
+
+    return path;
+}
+
+void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap &_textures,
+        const std::string &propName,
+        aiTextureType target, const MeshGeometry *const mesh) {
+    TextureMap::const_iterator it = _textures.find(propName);
+    if (it == _textures.end()) {
+        return;
+    }
+
+    const Texture *const tex = (*it).second;
+    if (tex != nullptr) {
+        aiString path = GetTexturePath(tex);
+        out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, 0);
+
+        aiUVTransform uvTrafo;
+        // XXX handle all kinds of UV transformations
+        uvTrafo.mScaling = tex->UVScaling();
+        uvTrafo.mTranslation = tex->UVTranslation();
+        out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0);
+
+        const PropertyTable &props = tex->Props();
+
+        int uvIndex = 0;
+
+        bool ok;
+        const std::string &uvSet = PropertyGet<std::string>(props, "UVSet", ok);
+        if (ok) {
+            // "default" is the name which usually appears in the FbxFileTexture template
+            if (uvSet != "default" && uvSet.length()) {
+                // this is a bit awkward - we need to find a mesh that uses this
+                // material and scan its UV channels for the given UV name because
+                // assimp references UV channels by index, not by name.
+
+                // XXX: the case that UV channels may appear in different orders
+                // in meshes is unhandled. A possible solution would be to sort
+                // the UV channels alphabetically, but this would have the side
+                // effect that the primary (first) UV channel would sometimes
+                // be moved, causing trouble when users read only the first
+                // UV channel and ignore UV channel assignments altogether.
+
+                const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(),
+                        std::find(materials.begin(), materials.end(), out_mat)));
+
+                uvIndex = -1;
+                if (!mesh) {
+                    for (const MeshMap::value_type &v : meshes_converted) {
+                        const MeshGeometry *const meshGeom = dynamic_cast<const MeshGeometry *>(v.first);
+                        if (!meshGeom) {
+                            continue;
+                        }
+
+                        const MatIndexArray &mats = meshGeom->GetMaterialIndices();
+                        MatIndexArray::const_iterator curIt = std::find(mats.begin(), mats.end(), (int) matIndex);
+                        if (curIt == mats.end()) {
+                            continue;
+                        }
+
+                        int index = -1;
+                        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                            if (meshGeom->GetTextureCoords(i).empty()) {
+                                break;
+                            }
+                            const std::string &name = meshGeom->GetTextureCoordChannelName(i);
+                            if (name == uvSet) {
+                                index = static_cast<int>(i);
+                                break;
+                            }
+                        }
+                        if (index == -1) {
+                            FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+                            continue;
+                        }
+
+                        if (uvIndex == -1) {
+                            uvIndex = index;
+                        } else {
+                            FBXImporter::LogWarn("the UV channel named " + uvSet +
+                                                 " appears at different positions in meshes, results will be wrong");
+                        }
+                    }
+                } else {
+                    int index = -1;
+                    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                        if (mesh->GetTextureCoords(i).empty()) {
+                            break;
+                        }
+                        const std::string &name = mesh->GetTextureCoordChannelName(i);
+                        if (name == uvSet) {
+                            index = static_cast<int>(i);
+                            break;
+                        }
+                    }
+                    if (index == -1) {
+                        FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+                    }
+
+                    if (uvIndex == -1) {
+                        uvIndex = index;
+                    }
+                }
+
+                if (uvIndex == -1) {
+                    FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+                    uvIndex = 0;
+                }
+            }
+        }
+
+        out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, 0);
+    }
+}
+
+void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTextureMap &layeredTextures,
+        const std::string &propName,
+        aiTextureType target, const MeshGeometry *const mesh) {
+    LayeredTextureMap::const_iterator it = layeredTextures.find(propName);
+    if (it == layeredTextures.end()) {
+        return;
+    }
+
+    int texCount = (*it).second->textureCount();
+
+    // Set the blend mode for layered textures
+    int blendmode = (*it).second->GetBlendMode();
+    out_mat->AddProperty(&blendmode, 1, _AI_MATKEY_TEXOP_BASE, target, 0);
+
+    for (int texIndex = 0; texIndex < texCount; texIndex++) {
+
+        const Texture *const tex = (*it).second->getTexture(texIndex);
+
+        aiString path = GetTexturePath(tex);
+        out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, texIndex);
+
+        aiUVTransform uvTrafo;
+        // XXX handle all kinds of UV transformations
+        uvTrafo.mScaling = tex->UVScaling();
+        uvTrafo.mTranslation = tex->UVTranslation();
+        out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex);
+
+        const PropertyTable &props = tex->Props();
+
+        int uvIndex = 0;
+
+        bool ok;
+        const std::string &uvSet = PropertyGet<std::string>(props, "UVSet", ok);
+        if (ok) {
+            // "default" is the name which usually appears in the FbxFileTexture template
+            if (uvSet != "default" && uvSet.length()) {
+                // this is a bit awkward - we need to find a mesh that uses this
+                // material and scan its UV channels for the given UV name because
+                // assimp references UV channels by index, not by name.
+
+                // XXX: the case that UV channels may appear in different orders
+                // in meshes is unhandled. A possible solution would be to sort
+                // the UV channels alphabetically, but this would have the side
+                // effect that the primary (first) UV channel would sometimes
+                // be moved, causing trouble when users read only the first
+                // UV channel and ignore UV channel assignments altogether.
+
+                const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(),
+                        std::find(materials.begin(), materials.end(), out_mat)));
+
+                uvIndex = -1;
+                if (!mesh) {
+                    for (const MeshMap::value_type &v : meshes_converted) {
+                        const MeshGeometry *const meshGeom = dynamic_cast<const MeshGeometry *>(v.first);
+                        if (!meshGeom) {
+                            continue;
+                        }
+
+                        const MatIndexArray &mats = meshGeom->GetMaterialIndices();
+                        MatIndexArray::const_iterator curIt = std::find(mats.begin(), mats.end(), (int) matIndex);
+                        if ( curIt == mats.end()) {
+                            continue;
+                        }
+
+                        int index = -1;
+                        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                            if (meshGeom->GetTextureCoords(i).empty()) {
+                                break;
+                            }
+                            const std::string &name = meshGeom->GetTextureCoordChannelName(i);
+                            if (name == uvSet) {
+                                index = static_cast<int>(i);
+                                break;
+                            }
+                        }
+                        if (index == -1) {
+                            FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+                            continue;
+                        }
+
+                        if (uvIndex == -1) {
+                            uvIndex = index;
+                        } else {
+                            FBXImporter::LogWarn("the UV channel named " + uvSet +
+                                                 " appears at different positions in meshes, results will be wrong");
+                        }
+                    }
+                } else {
+                    int index = -1;
+                    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                        if (mesh->GetTextureCoords(i).empty()) {
+                            break;
+                        }
+                        const std::string &name = mesh->GetTextureCoordChannelName(i);
+                        if (name == uvSet) {
+                            index = static_cast<int>(i);
+                            break;
+                        }
+                    }
+                    if (index == -1) {
+                        FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+                    }
+
+                    if (uvIndex == -1) {
+                        uvIndex = index;
+                    }
+                }
+
+                if (uvIndex == -1) {
+                    FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+                    uvIndex = 0;
+                }
+            }
+        }
+
+        out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, texIndex);
+    }
+}
+
+void FBXConverter::SetTextureProperties(aiMaterial *out_mat, const TextureMap &_textures, const MeshGeometry *const mesh) {
+    TrySetTextureProperties(out_mat, _textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh);
+    TrySetTextureProperties(out_mat, _textures, "AmbientColor", aiTextureType_AMBIENT, mesh);
+    TrySetTextureProperties(out_mat, _textures, "EmissiveColor", aiTextureType_EMISSIVE, mesh);
+    TrySetTextureProperties(out_mat, _textures, "SpecularColor", aiTextureType_SPECULAR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "SpecularFactor", aiTextureType_SPECULAR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "TransparentColor", aiTextureType_OPACITY, mesh);
+    TrySetTextureProperties(out_mat, _textures, "ReflectionColor", aiTextureType_REFLECTION, mesh);
+    TrySetTextureProperties(out_mat, _textures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh);
+    TrySetTextureProperties(out_mat, _textures, "NormalMap", aiTextureType_NORMALS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Bump", aiTextureType_HEIGHT, mesh);
+    TrySetTextureProperties(out_mat, _textures, "ShininessExponent", aiTextureType_SHININESS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "TransparencyFactor", aiTextureType_OPACITY, mesh);
+    TrySetTextureProperties(out_mat, _textures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh);
+    //Maya counterparts
+    TrySetTextureProperties(out_mat, _textures, "Maya|DiffuseTexture", aiTextureType_DIFFUSE, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|NormalTexture", aiTextureType_NORMALS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|SpecularTexture", aiTextureType_SPECULAR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|FalloffTexture", aiTextureType_OPACITY, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|ReflectionMapTexture", aiTextureType_REFLECTION, mesh);
+
+    // Maya PBR
+    TrySetTextureProperties(out_mat, _textures, "Maya|baseColor|file", aiTextureType_BASE_COLOR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|normalCamera|file", aiTextureType_NORMAL_CAMERA, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|emissionColor|file", aiTextureType_EMISSION_COLOR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|metalness|file", aiTextureType_METALNESS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|diffuseRoughness|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
+
+    // Maya stingray
+    TrySetTextureProperties(out_mat, _textures, "Maya|TEX_color_map|file", aiTextureType_BASE_COLOR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|TEX_normal_map|file", aiTextureType_NORMAL_CAMERA, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|TEX_emissive_map|file", aiTextureType_EMISSION_COLOR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|TEX_metallic_map|file", aiTextureType_METALNESS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|TEX_roughness_map|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh);
+
+    // 3DSMax PBR
+    TrySetTextureProperties(out_mat, _textures, "3dsMax|Parameters|base_color_map", aiTextureType_BASE_COLOR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "3dsMax|Parameters|bump_map", aiTextureType_NORMAL_CAMERA, mesh);
+    TrySetTextureProperties(out_mat, _textures, "3dsMax|Parameters|emission_map", aiTextureType_EMISSION_COLOR, mesh);
+    TrySetTextureProperties(out_mat, _textures, "3dsMax|Parameters|metalness_map", aiTextureType_METALNESS, mesh);
+    TrySetTextureProperties(out_mat, _textures, "3dsMax|Parameters|roughness_map", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
+}
+
+void FBXConverter::SetTextureProperties(aiMaterial *out_mat, const LayeredTextureMap &layeredTextures, const MeshGeometry *const mesh) {
+    TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "SpecularFactor", aiTextureType_SPECULAR, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "TransparencyFactor", aiTextureType_OPACITY, mesh);
+}
+
+aiColor3D FBXConverter::GetColorPropertyFactored(const PropertyTable &props, const std::string &colorName,
+        const std::string &factorName, bool &result, bool useTemplate) {
+    result = true;
+
+    bool ok;
+    aiVector3D BaseColor = PropertyGet<aiVector3D>(props, colorName, ok, useTemplate);
+    if (!ok) {
+        result = false;
+        return aiColor3D(0.0f, 0.0f, 0.0f);
+    }
+
+    // if no factor name, return the colour as is
+    if (factorName.empty()) {
+        return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z);
+    }
+
+    // otherwise it should be multiplied by the factor, if found.
+    float factor = PropertyGet<float>(props, factorName, ok, useTemplate);
+    if (ok) {
+        BaseColor *= factor;
+    }
+    return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z);
+}
+
+aiColor3D FBXConverter::GetColorPropertyFromMaterial(const PropertyTable &props, const std::string &baseName,
+        bool &result) {
+    return GetColorPropertyFactored(props, baseName + "Color", baseName + "Factor", result, true);
+}
+
+aiColor3D FBXConverter::GetColorProperty(const PropertyTable &props, const std::string &colorName,
+        bool &result, bool useTemplate) {
+    result = true;
+    bool ok;
+    const aiVector3D &ColorVec = PropertyGet<aiVector3D>(props, colorName, ok, useTemplate);
+    if (!ok) {
+        result = false;
+        return aiColor3D(0.0f, 0.0f, 0.0f);
+    }
+    return aiColor3D(ColorVec.x, ColorVec.y, ColorVec.z);
+}
+
+void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const PropertyTable &props) {
+    // Set shading properties.
+    // Modern FBX Files have two separate systems for defining these,
+    // with only the more comprehensive one described in the property template.
+    // Likely the other values are a legacy system,
+    // which is still always exported by the official FBX SDK.
+    //
+    // Blender's FBX import and export mostly ignore this legacy system,
+    // and as we only support recent versions of FBX anyway, we can do the same.
+    bool ok;
+
+    const aiColor3D &Diffuse = GetColorPropertyFromMaterial(props, "Diffuse", ok);
+    if (ok) {
+        out_mat->AddProperty(&Diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+    }
+
+    const aiColor3D &Emissive = GetColorPropertyFromMaterial(props, "Emissive", ok);
+    if (ok) {
+        out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+    }
+
+    const aiColor3D &Ambient = GetColorPropertyFromMaterial(props, "Ambient", ok);
+    if (ok) {
+        out_mat->AddProperty(&Ambient, 1, AI_MATKEY_COLOR_AMBIENT);
+    }
+
+    // we store specular factor as SHININESS_STRENGTH, so just get the color
+    const aiColor3D &Specular = GetColorProperty(props, "SpecularColor", ok, true);
+    if (ok) {
+        out_mat->AddProperty(&Specular, 1, AI_MATKEY_COLOR_SPECULAR);
+    }
+
+    // and also try to get SHININESS_STRENGTH
+    const float SpecularFactor = PropertyGet<float>(props, "SpecularFactor", ok, true);
+    if (ok) {
+        out_mat->AddProperty(&SpecularFactor, 1, AI_MATKEY_SHININESS_STRENGTH);
+    }
+
+    // and the specular exponent
+    const float ShininessExponent = PropertyGet<float>(props, "ShininessExponent", ok);
+    if (ok) {
+        out_mat->AddProperty(&ShininessExponent, 1, AI_MATKEY_SHININESS);
+    }
+
+    // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes:
+    const aiColor3D &Transparent = GetColorPropertyFactored(props, "TransparentColor", "TransparencyFactor", ok);
+    float CalculatedOpacity = 1.0f;
+    if (ok) {
+        out_mat->AddProperty(&Transparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
+        // as calculated by FBX SDK 2017:
+        CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f);
+    }
+
+    // try to get the transparency factor
+    const float TransparencyFactor = PropertyGet<float>(props, "TransparencyFactor", ok);
+    if (ok) {
+        out_mat->AddProperty(&TransparencyFactor, 1, AI_MATKEY_TRANSPARENCYFACTOR);
+    }
+
+    // use of TransparencyFactor is inconsistent.
+    // Maya always stores it as 1.0,
+    // so we can't use it to set AI_MATKEY_OPACITY.
+    // Blender is more sensible and stores it as the alpha value.
+    // However both the FBX SDK and Blender always write an additional
+    // legacy "Opacity" field, so we can try to use that.
+    //
+    // If we can't find it,
+    // we can fall back to the value which the FBX SDK calculates
+    // from transparency colour (RGB) and factor (F) as
+    // 1.0 - F*((R+G+B)/3).
+    //
+    // There's no consistent way to interpret this opacity value,
+    // so it's up to clients to do the correct thing.
+    const float Opacity = PropertyGet<float>(props, "Opacity", ok);
+    if (ok) {
+        out_mat->AddProperty(&Opacity, 1, AI_MATKEY_OPACITY);
+    } else if (CalculatedOpacity != 1.0) {
+        out_mat->AddProperty(&CalculatedOpacity, 1, AI_MATKEY_OPACITY);
+    }
+
+    // reflection color and factor are stored separately
+    const aiColor3D &Reflection = GetColorProperty(props, "ReflectionColor", ok, true);
+    if (ok) {
+        out_mat->AddProperty(&Reflection, 1, AI_MATKEY_COLOR_REFLECTIVE);
+    }
+
+    float ReflectionFactor = PropertyGet<float>(props, "ReflectionFactor", ok, true);
+    if (ok) {
+        out_mat->AddProperty(&ReflectionFactor, 1, AI_MATKEY_REFLECTIVITY);
+    }
+
+    const float BumpFactor = PropertyGet<float>(props, "BumpFactor", ok);
+    if (ok) {
+        out_mat->AddProperty(&BumpFactor, 1, AI_MATKEY_BUMPSCALING);
+    }
+
+    const float DispFactor = PropertyGet<float>(props, "DisplacementFactor", ok);
+    if (ok) {
+        out_mat->AddProperty(&DispFactor, 1, "$mat.displacementscaling", 0, 0);
+    }
+}
+
+void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTable &props, const TextureMap &_textures, const MeshGeometry *const mesh) {
+    // Add all the unparsed properties with a "$raw." prefix
+
+    const std::string prefix = "$raw.";
+
+    for (const DirectPropertyMap::value_type &prop : props.GetUnparsedProperties()) {
+
+        std::string name = prefix + prop.first;
+
+        if (const TypedProperty<aiVector3D> *interpretedVec3 = prop.second->As<TypedProperty<aiVector3D>>()) {
+            out_mat->AddProperty(&interpretedVec3->Value(), 1, name.c_str(), 0, 0);
+        } else if (const TypedProperty<aiColor3D> *interpretedCol3 = prop.second->As<TypedProperty<aiColor3D>>()) {
+            out_mat->AddProperty(&interpretedCol3->Value(), 1, name.c_str(), 0, 0);
+        } else if (const TypedProperty<aiColor4D> *interpretedCol4 = prop.second->As<TypedProperty<aiColor4D>>()) {
+            out_mat->AddProperty(&interpretedCol4->Value(), 1, name.c_str(), 0, 0);
+        } else if (const TypedProperty<float> *interpretedFloat = prop.second->As<TypedProperty<float>>()) {
+            out_mat->AddProperty(&interpretedFloat->Value(), 1, name.c_str(), 0, 0);
+        } else if (const TypedProperty<int> *interpretedInt = prop.second->As<TypedProperty<int>>()) {
+            out_mat->AddProperty(&interpretedInt->Value(), 1, name.c_str(), 0, 0);
+        } else if (const TypedProperty<bool> *interpretedBool = prop.second->As<TypedProperty<bool>>()) {
+            int value = interpretedBool->Value() ? 1 : 0;
+            out_mat->AddProperty(&value, 1, name.c_str(), 0, 0);
+        } else if (const TypedProperty<std::string> *interpretedString = prop.second->As<TypedProperty<std::string>>()) {
+            const aiString value = aiString(interpretedString->Value());
+            out_mat->AddProperty(&value, name.c_str(), 0, 0);
+        }
+    }
+
+    // Add the textures' properties
+
+    for (TextureMap::const_iterator it = _textures.begin(); it != _textures.end(); ++it) {
+
+        std::string name = prefix + it->first;
+
+        const Texture *const tex = it->second;
+        if (tex != nullptr) {
+            aiString path;
+            path.Set(tex->RelativeFilename());
+
+            const Video *media = tex->Media();
+            if (media != nullptr && media->ContentLength() > 0) {
+                unsigned int index;
+
+                VideoMap::const_iterator videoIt = textures_converted.find(*media);
+                if (videoIt != textures_converted.end()) {
+                    index = videoIt->second;
+                } else {
+                    index = ConvertVideo(*media);
+                    textures_converted[*media] = index;
+                }
+
+                // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
+                path.data[0] = '*';
+                path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+            }
+
+            out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0);
+
+            aiUVTransform uvTrafo;
+            // XXX handle all kinds of UV transformations
+            uvTrafo.mScaling = tex->UVScaling();
+            uvTrafo.mTranslation = tex->UVTranslation();
+            out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0);
+
+            int uvIndex = 0;
+
+            bool uvFound = false;
+            const std::string &uvSet = PropertyGet<std::string>(tex->Props(), "UVSet", uvFound);
+            if (uvFound) {
+                // "default" is the name which usually appears in the FbxFileTexture template
+                if (uvSet != "default" && uvSet.length()) {
+                    // this is a bit awkward - we need to find a mesh that uses this
+                    // material and scan its UV channels for the given UV name because
+                    // assimp references UV channels by index, not by name.
+
+                    // XXX: the case that UV channels may appear in different orders
+                    // in meshes is unhandled. A possible solution would be to sort
+                    // the UV channels alphabetically, but this would have the side
+                    // effect that the primary (first) UV channel would sometimes
+                    // be moved, causing trouble when users read only the first
+                    // UV channel and ignore UV channel assignments altogether.
+
+                    std::vector<aiMaterial *>::iterator materialIt = std::find(materials.begin(), materials.end(), out_mat);
+                    const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), materialIt));
+
+                    uvIndex = -1;
+                    if (!mesh) {
+                        for (const MeshMap::value_type &v : meshes_converted) {
+                            const MeshGeometry *const meshGeom = dynamic_cast<const MeshGeometry *>(v.first);
+                            if (!meshGeom) {
+                                continue;
+                            }
+
+                            const MatIndexArray &mats = meshGeom->GetMaterialIndices();
+                            if (std::find(mats.begin(), mats.end(), (int)matIndex) == mats.end()) {
+                                continue;
+                            }
+
+                            int index = -1;
+                            for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                                if (meshGeom->GetTextureCoords(i).empty()) {
+                                    break;
+                                }
+                                const std::string &curName = meshGeom->GetTextureCoordChannelName(i);
+                                if (curName == uvSet) {
+                                    index = static_cast<int>(i);
+                                    break;
+                                }
+                            }
+                            if (index == -1) {
+                                FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+                                continue;
+                            }
+
+                            if (uvIndex == -1) {
+                                uvIndex = index;
+                            } else {
+                                FBXImporter::LogWarn("the UV channel named " + uvSet + " appears at different positions in meshes, results will be wrong");
+                            }
+                        }
+                    } else {
+                        int index = -1;
+                        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+                            if (mesh->GetTextureCoords(i).empty()) {
+                                break;
+                            }
+                            const std::string &curName = mesh->GetTextureCoordChannelName(i);
+                            if (curName == uvSet) {
+                                index = static_cast<int>(i);
+                                break;
+                            }
+                        }
+                        if (index == -1) {
+                            FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+                        }
+
+                        if (uvIndex == -1) {
+                            uvIndex = index;
+                        }
+                    }
+
+                    if (uvIndex == -1) {
+                        FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+                        uvIndex = 0;
+                    }
+                }
+            }
+
+            out_mat->AddProperty(&uvIndex, 1, (name + "|uvwsrc").c_str(), aiTextureType_UNKNOWN, 0);
+        }
+    }
+}
+
+double FBXConverter::FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal) {
+    switch (fp) {
+        case FileGlobalSettings::FrameRate_DEFAULT:
+            return 1.0;
+
+        case FileGlobalSettings::FrameRate_120:
+            return 120.0;
+
+        case FileGlobalSettings::FrameRate_100:
+            return 100.0;
+
+        case FileGlobalSettings::FrameRate_60:
+            return 60.0;
+
+        case FileGlobalSettings::FrameRate_50:
+            return 50.0;
+
+        case FileGlobalSettings::FrameRate_48:
+            return 48.0;
+
+        case FileGlobalSettings::FrameRate_30:
+        case FileGlobalSettings::FrameRate_30_DROP:
+            return 30.0;
+
+        case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME:
+        case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME:
+            return 29.9700262;
+
+        case FileGlobalSettings::FrameRate_PAL:
+            return 25.0;
+
+        case FileGlobalSettings::FrameRate_CINEMA:
+            return 24.0;
+
+        case FileGlobalSettings::FrameRate_1000:
+            return 1000.0;
+
+        case FileGlobalSettings::FrameRate_CINEMA_ND:
+            return 23.976;
+
+        case FileGlobalSettings::FrameRate_CUSTOM:
+            return customFPSVal;
+
+        case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings
+            break;
+    }
+
+    ai_assert(false);
+
+    return -1.0f;
+}
+
+void FBXConverter::ConvertAnimations() {
+    // first of all determine framerate
+    const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode();
+    const float custom = doc.GlobalSettings().CustomFrameRate();
+    anim_fps = FrameRateToDouble(fps, custom);
+
+    const std::vector<const AnimationStack *> &curAnimations = doc.AnimationStacks();
+    for (const AnimationStack *stack : curAnimations) {
+        ConvertAnimationStack(*stack);
+    }
+}
+
+std::string FBXConverter::FixNodeName(const std::string &name) {
+    // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if
+    // this causes ambiguities, well possible between empty identifiers,
+    // such as "Model::" and ""). Make sure the behaviour is consistent
+    // across multiple calls to FixNodeName().
+    if (name.substr(0, 7) == "Model::") {
+        std::string temp = name.substr(7);
+        return temp;
+    }
+
+    return name;
+}
+
+std::string FBXConverter::FixAnimMeshName(const std::string &name) {
+    if (name.length()) {
+        size_t indexOf = name.find_first_of("::");
+        if (indexOf != std::string::npos && indexOf < name.size() - 2) {
+            return name.substr(indexOf + 2);
+        }
+    }
+    return name.length() ? name : "AnimMesh";
+}
+
+void FBXConverter::ConvertAnimationStack(const AnimationStack &st) {
+    const AnimationLayerList &layers = st.Layers();
+    if (layers.empty()) {
+        return;
+    }
+
+    aiAnimation *const anim = new aiAnimation();
+    animations.push_back(anim);
+
+    // strip AnimationStack:: prefix
+    std::string name = st.Name();
+    if (name.substr(0, 16) == "AnimationStack::") {
+        name = name.substr(16);
+    } else if (name.substr(0, 11) == "AnimStack::") {
+        name = name.substr(11);
+    }
+
+    anim->mName.Set(name);
+
+    // need to find all nodes for which we need to generate node animations -
+    // it may happen that we need to merge multiple layers, though.
+    NodeMap node_map;
+
+    // reverse mapping from curves to layers, much faster than querying
+    // the FBX DOM for it.
+    LayerMap layer_map;
+
+    const char *prop_whitelist[] = {
+        "Lcl Scaling",
+        "Lcl Rotation",
+        "Lcl Translation",
+        "DeformPercent"
+    };
+
+    std::map<std::string, morphAnimData *> morphAnimDatas;
+
+    for (const AnimationLayer *layer : layers) {
+        ai_assert(layer);
+        const AnimationCurveNodeList &nodes = layer->Nodes(prop_whitelist, 4);
+        for (const AnimationCurveNode *node : nodes) {
+            ai_assert(node);
+            const Model *const model = dynamic_cast<const Model *>(node->Target());
+            if (model) {
+                const std::string &curName = FixNodeName(model->Name());
+                node_map[curName].push_back(node);
+                layer_map[node] = layer;
+                continue;
+            }
+            const BlendShapeChannel *const bsc = dynamic_cast<const BlendShapeChannel *>(node->Target());
+            if (bsc) {
+                ProcessMorphAnimDatas(&morphAnimDatas, bsc, node);
+            }
+        }
+    }
+
+    // generate node animations
+    std::vector<aiNodeAnim *> node_anims;
+
+    double min_time = 1e10;
+    double max_time = -1e10;
+
+    int64_t start_time = st.LocalStart();
+    int64_t stop_time = st.LocalStop();
+    bool has_local_startstop = start_time != 0 || stop_time != 0;
+    if (!has_local_startstop) {
+        // no time range given, so accept every keyframe and use the actual min/max time
+        // the numbers are INT64_MIN/MAX, the 20000 is for safety because GenerateNodeAnimations uses an epsilon of 10000
+        start_time = -9223372036854775807ll + 20000;
+        stop_time = 9223372036854775807ll - 20000;
+    }
+
+    try {
+        for (const NodeMap::value_type &kv : node_map) {
+            GenerateNodeAnimations(node_anims,
+                    kv.first,
+                    kv.second,
+                    layer_map,
+                    start_time, stop_time,
+                    max_time,
+                    min_time);
+        }
+    } catch (std::exception &) {
+        std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>());
+        throw;
+    }
+
+    if (node_anims.size() || morphAnimDatas.size()) {
+        if (node_anims.size()) {
+            anim->mChannels = new aiNodeAnim *[node_anims.size()]();
+            anim->mNumChannels = static_cast<unsigned int>(node_anims.size());
+            std::swap_ranges(node_anims.begin(), node_anims.end(), anim->mChannels);
+        }
+        if (morphAnimDatas.size()) {
+            unsigned int numMorphMeshChannels = static_cast<unsigned int>(morphAnimDatas.size());
+            anim->mMorphMeshChannels = new aiMeshMorphAnim *[numMorphMeshChannels];
+            anim->mNumMorphMeshChannels = numMorphMeshChannels;
+            unsigned int i = 0;
+            for (auto morphAnimIt : morphAnimDatas) {
+                morphAnimData *animData = morphAnimIt.second;
+                unsigned int numKeys = static_cast<unsigned int>(animData->size());
+                aiMeshMorphAnim *meshMorphAnim = new aiMeshMorphAnim();
+                meshMorphAnim->mName.Set(morphAnimIt.first);
+                meshMorphAnim->mNumKeys = numKeys;
+                meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys];
+                unsigned int j = 0;
+                for (auto animIt : *animData) {
+                    morphKeyData *keyData = animIt.second;
+                    unsigned int numValuesAndWeights = static_cast<unsigned int>(keyData->values.size());
+                    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) * 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);
+                    }
+                    j++;
+                }
+                anim->mMorphMeshChannels[i++] = meshMorphAnim;
+            }
+        }
+    } else {
+        // empty animations would fail validation, so drop them
+        delete anim;
+        animations.pop_back();
+        FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name);
+        return;
+    }
+
+    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++) {
+        aiNodeAnim *channel = anim->mChannels[c];
+        for (uint32_t i = 0; i < channel->mNumPositionKeys; i++) {
+            channel->mPositionKeys[i].mTime -= start_time_fps;
+        }
+        for (uint32_t i = 0; i < channel->mNumRotationKeys; i++) {
+            channel->mRotationKeys[i].mTime -= start_time_fps;
+        }
+        for (uint32_t i = 0; i < channel->mNumScalingKeys; i++) {
+            channel->mScalingKeys[i].mTime -= start_time_fps;
+        }
+    }
+    for (unsigned int c = 0; c < anim->mNumMorphMeshChannels; c++) {
+        aiMeshMorphAnim *channel = anim->mMorphMeshChannels[c];
+        for (uint32_t i = 0; i < channel->mNumKeys; i++) {
+            channel->mKeys[i].mTime -= start_time_fps;
+        }
+    }
+
+    // for some mysterious reason, mDuration is simply the maximum key -- the
+    // validator always assumes animations to start at zero.
+    anim->mDuration = stop_time_fps - start_time_fps;
+    anim->mTicksPerSecond = anim_fps;
+}
+
+// ------------------------------------------------------------------------------------------------
+void FBXConverter::ProcessMorphAnimDatas(std::map<std::string, morphAnimData *> *morphAnimDatas, const BlendShapeChannel *bsc, const AnimationCurveNode *node) {
+    std::vector<const Connection *> bscConnections = doc.GetConnectionsBySourceSequenced(bsc->ID(), "Deformer");
+    for (const Connection *bscConnection : bscConnections) {
+        auto bs = dynamic_cast<const BlendShape *>(bscConnection->DestinationObject());
+        if (bs) {
+            auto channelIt = std::find(bs->BlendShapeChannels().begin(), bs->BlendShapeChannels().end(), bsc);
+            if (channelIt != bs->BlendShapeChannels().end()) {
+                auto channelIndex = static_cast<unsigned int>(std::distance(bs->BlendShapeChannels().begin(), channelIt));
+                std::vector<const Connection *> bsConnections = doc.GetConnectionsBySourceSequenced(bs->ID(), "Geometry");
+                for (const Connection *bsConnection : bsConnections) {
+                    auto geo = dynamic_cast<const Geometry *>(bsConnection->DestinationObject());
+                    if (geo) {
+                        std::vector<const Connection *> geoConnections = doc.GetConnectionsBySourceSequenced(geo->ID(), "Model");
+                        for (const Connection *geoConnection : geoConnections) {
+                            auto model = dynamic_cast<const Model *>(geoConnection->DestinationObject());
+                            if (model) {
+                                auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo);
+                                auto geoIndex = static_cast<unsigned int>(std::distance(model->GetGeometry().begin(), geoIt));
+                                auto name = aiString(FixNodeName(model->Name() + "*"));
+                                name.length = 1 + ASSIMP_itoa10(name.data + name.length, MAXLEN - 1, geoIndex);
+                                morphAnimData *animData;
+                                auto animIt = morphAnimDatas->find(name.C_Str());
+                                if (animIt == morphAnimDatas->end()) {
+                                    animData = new morphAnimData();
+                                    morphAnimDatas->insert(std::make_pair(name.C_Str(), animData));
+                                } else {
+                                    animData = animIt->second;
+                                }
+                                for (std::pair<std::string, const AnimationCurve *> curvesIt : node->Curves()) {
+                                    if (curvesIt.first == "d|DeformPercent") {
+                                        const AnimationCurve *animationCurve = curvesIt.second;
+                                        const KeyTimeList &keys = animationCurve->GetKeys();
+                                        const KeyValueList &values = animationCurve->GetValues();
+                                        unsigned int k = 0;
+                                        for (auto key : keys) {
+                                            morphKeyData *keyData;
+                                            auto keyIt = animData->find(key);
+                                            if (keyIt == animData->end()) {
+                                                keyData = new morphKeyData();
+                                                animData->insert(std::make_pair(key, keyData));
+                                            } else {
+                                                keyData = keyIt->second;
+                                            }
+                                            keyData->values.push_back(channelIndex);
+                                            keyData->weights.push_back(values.at(k) / 100.0f);
+                                            k++;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+#ifdef ASSIMP_BUILD_DEBUG
+// ------------------------------------------------------------------------------------------------
+// sanity check whether the input is ok
+static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode *> &curves,
+        bool strictMode) {
+    const Object *target(nullptr);
+    for (const AnimationCurveNode *node : curves) {
+        if (!target) {
+            target = node->Target();
+        }
+        if (node->Target() != target) {
+            FBXImporter::LogWarn("Node target is nullptr type.");
+        }
+        if (strictMode) {
+            ai_assert(node->Target() == target);
+        }
+    }
+}
+#endif // ASSIMP_BUILD_DEBUG
+
+// ------------------------------------------------------------------------------------------------
+void FBXConverter::GenerateNodeAnimations(std::vector<aiNodeAnim *> &node_anims,
+        const std::string &fixed_name,
+        const std::vector<const AnimationCurveNode *> &curves,
+        const LayerMap &layer_map,
+        int64_t start, int64_t stop,
+        double &max_time,
+        double &min_time) {
+
+    NodeMap node_property_map;
+    ai_assert(curves.size());
+
+#ifdef ASSIMP_BUILD_DEBUG
+    validateAnimCurveNodes(curves, doc.Settings().strictMode);
+#endif
+    const AnimationCurveNode *curve_node = nullptr;
+    for (const AnimationCurveNode *node : curves) {
+        ai_assert(node);
+
+        if (node->TargetProperty().empty()) {
+            FBXImporter::LogWarn("target property for animation curve not set: " + node->Name());
+            continue;
+        }
+
+        curve_node = node;
+        if (node->Curves().empty()) {
+            FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name());
+            continue;
+        }
+
+        node_property_map[node->TargetProperty()].push_back(node);
+    }
+
+    ai_assert(curve_node);
+    ai_assert(curve_node->TargetAsModel());
+
+    const Model &target = *curve_node->TargetAsModel();
+
+    // check for all possible transformation components
+    NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
+
+    bool has_any = false;
+    bool has_complex = false;
+
+    for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+        const TransformationComp comp = static_cast<TransformationComp>(i);
+
+        // inverse pivots don't exist in the input, we just generate them
+        if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) {
+            chain[i] = node_property_map.end();
+            continue;
+        }
+
+        chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
+        if (chain[i] != node_property_map.end()) {
+
+            // check if this curves contains redundant information by looking
+            // up the corresponding node's transformation chain.
+            if (doc.Settings().optimizeEmptyAnimationCurves &&
+                    IsRedundantAnimationData(target, comp, (chain[i]->second))) {
+
+                FBXImporter::LogVerboseDebug("dropping redundant animation channel for node " + target.Name());
+                continue;
+            }
+
+            has_any = true;
+
+            if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation) {
+                has_complex = true;
+            }
+        }
+    }
+
+    if (!has_any) {
+        FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
+        return;
+    }
+
+    // this needs to play nicely with GenerateTransformationNodeChain() which will
+    // be invoked _later_ (animations come first). If this node has only rotation,
+    // scaling and translation _and_ there are no animated other components either,
+    // we can use a single node and also a single node animation channel.
+    if( !has_complex && !NeedsComplexTransformationChain(target)) {
+        aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain,
+                node_property_map.end(),
+                start, stop,
+                max_time,
+                min_time
+        );
+
+        ai_assert(nd);
+        if (nd->mNumPositionKeys == 0 && nd->mNumRotationKeys == 0 && nd->mNumScalingKeys == 0) {
+            delete nd;
+        } else {
+            node_anims.push_back(nd);
+        }
+        return;
+    }
+
+    // otherwise, things get gruesome and we need separate animation channels
+    // for each part of the transformation chain. Remember which channels
+    // we generated and pass this information to the node conversion
+    // code to avoid nodes that have identity transform, but non-identity
+    // animations, being dropped.
+    unsigned int flags = 0, bit = 0x1;
+    for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
+        const TransformationComp comp = static_cast<TransformationComp>(i);
+
+        if (chain[i] != node_property_map.end()) {
+            flags |= bit;
+
+            ai_assert(comp != TransformationComp_RotationPivotInverse);
+            ai_assert(comp != TransformationComp_ScalingPivotInverse);
+
+            const std::string &chain_name = NameTransformationChainNode(fixed_name, comp);
+
+            aiNodeAnim *na = nullptr;
+            switch (comp) {
+                case TransformationComp_Rotation:
+                case TransformationComp_PreRotation:
+                case TransformationComp_PostRotation:
+                case TransformationComp_GeometricRotation:
+                    na = GenerateRotationNodeAnim(chain_name,
+                            target,
+                            (*chain[i]).second,
+                            layer_map,
+                            start, stop,
+                            max_time,
+                            min_time);
+
+                    break;
+
+                case TransformationComp_RotationOffset:
+                case TransformationComp_RotationPivot:
+                case TransformationComp_ScalingOffset:
+                case TransformationComp_ScalingPivot:
+                case TransformationComp_Translation:
+                case TransformationComp_GeometricTranslation:
+                    na = GenerateTranslationNodeAnim(chain_name,
+                            target,
+                            (*chain[i]).second,
+                            layer_map,
+                            start, stop,
+                            max_time,
+                            min_time);
+
+                    // pivoting requires us to generate an implicit inverse channel to undo the pivot translation
+                    if (comp == TransformationComp_RotationPivot) {
+                        const std::string &invName = NameTransformationChainNode(fixed_name,
+                                TransformationComp_RotationPivotInverse);
+
+                        aiNodeAnim *const inv = GenerateTranslationNodeAnim(invName,
+                                target,
+                                (*chain[i]).second,
+                                layer_map,
+                                start, stop,
+                                max_time,
+                                min_time,
+                                true);
+
+                        ai_assert(inv);
+                        if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) {
+                            delete inv;
+                        } else {
+                            node_anims.push_back(inv);
+                        }
+
+                        ai_assert(TransformationComp_RotationPivotInverse > i);
+                        flags |= bit << (TransformationComp_RotationPivotInverse - i);
+                    } else if (comp == TransformationComp_ScalingPivot) {
+                        const std::string &invName = NameTransformationChainNode(fixed_name,
+                                TransformationComp_ScalingPivotInverse);
+
+                        aiNodeAnim *const inv = GenerateTranslationNodeAnim(invName,
+                                target,
+                                (*chain[i]).second,
+                                layer_map,
+                                start, stop,
+                                max_time,
+                                min_time,
+                                true);
+
+                        ai_assert(inv);
+                        if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) {
+                            delete inv;
+                        } else {
+                            node_anims.push_back(inv);
+                        }
+
+                        ai_assert(TransformationComp_RotationPivotInverse > i);
+                        flags |= bit << (TransformationComp_RotationPivotInverse - i);
+                    }
+
+                    break;
+
+                case TransformationComp_Scaling:
+                case TransformationComp_GeometricScaling:
+                    na = GenerateScalingNodeAnim(chain_name,
+                            target,
+                            (*chain[i]).second,
+                            layer_map,
+                            start, stop,
+                            max_time,
+                            min_time);
+
+                    break;
+
+                default:
+                    ai_assert(false);
+            }
+
+            ai_assert(na);
+            if (na->mNumPositionKeys == 0 && na->mNumRotationKeys == 0 && na->mNumScalingKeys == 0) {
+                delete na;
+            } else {
+                node_anims.push_back(na);
+            }
+            continue;
+        }
+    }
+
+    node_anim_chain_bits[fixed_name] = flags;
+}
+
+bool FBXConverter::IsRedundantAnimationData(const Model &target,
+        TransformationComp comp,
+        const std::vector<const AnimationCurveNode *> &curves) {
+    ai_assert(curves.size());
+
+    // look for animation nodes with
+    //  * sub channels for all relevant components set
+    //  * one key/value pair per component
+    //  * combined values match up the corresponding value in the bind pose node transformation
+    // only such nodes are 'redundant' for this function.
+
+    if (curves.size() > 1) {
+        return false;
+    }
+
+    const AnimationCurveNode &nd = *curves.front();
+    const AnimationCurveMap &sub_curves = nd.Curves();
+
+    const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X");
+    const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y");
+    const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z");
+
+    if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) {
+        return false;
+    }
+
+    const KeyValueList &vx = (*dx).second->GetValues();
+    const KeyValueList &vy = (*dy).second->GetValues();
+    const KeyValueList &vz = (*dz).second->GetValues();
+
+    if (vx.size() != 1 || vy.size() != 1 || vz.size() != 1) {
+        return false;
+    }
+
+    const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]);
+    const aiVector3D &static_val = PropertyGet<aiVector3D>(target.Props(),
+            NameTransformationCompProperty(comp),
+            TransformationCompDefaultValue(comp));
+
+    const float epsilon = Math::getEpsilon<float>();
+    return (dyn_val - static_val).SquareLength() < epsilon;
+}
+
+aiNodeAnim *FBXConverter::GenerateRotationNodeAnim(const std::string &name,
+        const Model &target,
+        const std::vector<const AnimationCurveNode *> &curves,
+        const LayerMap &layer_map,
+        int64_t start, int64_t stop,
+        double &max_time,
+        double &min_time) {
+    std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+    na->mNodeName.Set(name);
+
+    ConvertRotationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time, target.RotationOrder());
+
+    // dummy scaling key
+    na->mScalingKeys = new aiVectorKey[1];
+    na->mNumScalingKeys = 1;
+
+    na->mScalingKeys[0].mTime = 0.;
+    na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f);
+
+    // dummy position key
+    na->mPositionKeys = new aiVectorKey[1];
+    na->mNumPositionKeys = 1;
+
+    na->mPositionKeys[0].mTime = 0.;
+    na->mPositionKeys[0].mValue = aiVector3D();
+
+    return na.release();
+}
+
+aiNodeAnim *FBXConverter::GenerateScalingNodeAnim(const std::string &name,
+        const Model & /*target*/,
+        const std::vector<const AnimationCurveNode *> &curves,
+        const LayerMap &layer_map,
+        int64_t start, int64_t stop,
+        double &max_time,
+        double &min_time) {
+    std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+    na->mNodeName.Set(name);
+
+    ConvertScaleKeys(na.get(), curves, layer_map, start, stop, max_time, min_time);
+
+    // dummy rotation key
+    na->mRotationKeys = new aiQuatKey[1];
+    na->mNumRotationKeys = 1;
+
+    na->mRotationKeys[0].mTime = 0.;
+    na->mRotationKeys[0].mValue = aiQuaternion();
+
+    // dummy position key
+    na->mPositionKeys = new aiVectorKey[1];
+    na->mNumPositionKeys = 1;
+
+    na->mPositionKeys[0].mTime = 0.;
+    na->mPositionKeys[0].mValue = aiVector3D();
+
+    return na.release();
+}
+
+aiNodeAnim *FBXConverter::GenerateTranslationNodeAnim(const std::string &name,
+        const Model & /*target*/,
+        const std::vector<const AnimationCurveNode *> &curves,
+        const LayerMap &layer_map,
+        int64_t start, int64_t stop,
+        double &max_time,
+        double &min_time,
+        bool inverse) {
+    std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+    na->mNodeName.Set(name);
+
+    ConvertTranslationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time);
+
+    if (inverse) {
+        for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) {
+            na->mPositionKeys[i].mValue *= -1.0f;
+        }
+    }
+
+    // dummy scaling key
+    na->mScalingKeys = new aiVectorKey[1];
+    na->mNumScalingKeys = 1;
+
+    na->mScalingKeys[0].mTime = 0.;
+    na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f);
+
+    // dummy rotation key
+    na->mRotationKeys = new aiQuatKey[1];
+    na->mNumRotationKeys = 1;
+
+    na->mRotationKeys[0].mTime = 0.;
+    na->mRotationKeys[0].mValue = aiQuaternion();
+
+    return na.release();
+}
+
+aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
+        const Model& target,
+        NodeMap::const_iterator chain[TransformationComp_MAXIMUM],
+        NodeMap::const_iterator iterEnd,
+        int64_t start, int64_t stop,
+        double& maxTime,
+        double& minTime)
+{
+    std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+    na->mNodeName.Set(name);
+
+    const PropertyTable &props = target.Props();
+
+    // collect unique times and keyframe lists
+    KeyFrameListList keyframeLists[TransformationComp_MAXIMUM];
+    KeyTimeList keytimes;
+
+    for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+        if (chain[i] == iterEnd)
+            continue;
+
+        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);
+            keytimes.insert(keytimes.end(), times.begin(), times.end());
+        }
+
+        // remove duplicates
+        std::sort(keytimes.begin(), keytimes.end());
+
+        auto last = std::unique(keytimes.begin(), keytimes.end());
+        keytimes.erase(last, keytimes.end());
+    }
+
+    const Model::RotOrder rotOrder = target.RotationOrder();
+    const size_t keyCount = keytimes.size();
+
+    aiVector3D defTranslate = PropertyGet(props, "Lcl Translation", aiVector3D(0.f, 0.f, 0.f));
+    aiVector3D defRotation = PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f));
+    aiVector3D defScale = PropertyGet(props, "Lcl Scaling", aiVector3D(1.f, 1.f, 1.f));
+    aiQuaternion defQuat = EulerToQuaternion(defRotation, rotOrder);
+
+    aiVectorKey* outTranslations = new aiVectorKey[keyCount];
+    aiQuatKey* outRotations = new aiQuatKey[keyCount];
+    aiVectorKey* outScales = new aiVectorKey[keyCount];
+
+    if (keyframeLists[TransformationComp_Translation].size() > 0) {
+        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]) * anim_fps;
+            outTranslations[i].mValue = defTranslate;
+        }
+    }
+
+    if (keyframeLists[TransformationComp_Rotation].size() > 0) {
+        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]) * anim_fps;
+            outRotations[i].mValue = defQuat;
+        }
+    }
+
+    if (keyframeLists[TransformationComp_Scaling].size() > 0) {
+        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]) * anim_fps;
+            outScales[i].mValue = defScale;
+        }
+    }
+
+    bool ok = false;
+    const float zero_epsilon = 1e-6f;
+
+    const aiVector3D& preRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
+    if (ok && preRotation.SquareLength() > zero_epsilon) {
+        const aiQuaternion preQuat = EulerToQuaternion(preRotation, Model::RotOrder_EulerXYZ);
+        for (size_t i = 0; i < keyCount; ++i) {
+            outRotations[i].mValue = preQuat * outRotations[i].mValue;
+        }
+    }
+
+    const aiVector3D& postRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
+    if (ok && postRotation.SquareLength() > zero_epsilon) {
+        const aiQuaternion postQuat = EulerToQuaternion(postRotation, Model::RotOrder_EulerXYZ);
+        for (size_t i = 0; i < keyCount; ++i) {
+            outRotations[i].mValue = outRotations[i].mValue * postQuat;
+        }
+    }
+
+    // convert TRS to SRT
+    for (size_t i = 0; i < keyCount; ++i) {
+        aiQuaternion& r = outRotations[i].mValue;
+        aiVector3D& s = outScales[i].mValue;
+        aiVector3D& t = outTranslations[i].mValue;
+
+        aiMatrix4x4 mat, temp;
+        aiMatrix4x4::Translation(t, mat);
+        mat *= aiMatrix4x4(r.GetMatrix());
+        mat *= aiMatrix4x4::Scaling(s, temp);
+
+        mat.Decompose(s, r, t);
+    }
+
+    na->mNumScalingKeys = static_cast<unsigned int>(keyCount);
+    na->mNumRotationKeys = na->mNumScalingKeys;
+    na->mNumPositionKeys = na->mNumScalingKeys;
+
+    na->mScalingKeys = outScales;
+    na->mRotationKeys = outRotations;
+    na->mPositionKeys = outTranslations;
+
+    return na.release();
+}
+
+FBXConverter::KeyFrameListList FBXConverter::GetKeyframeList(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
+    int64_t adj_start = start - 10000;
+    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() && 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();
+            Keys->reserve(count);
+            Values->reserve(count);
+            for (size_t n = 0; n < count; n++) {
+                int64_t k = curve->GetKeys().at(n);
+                if (k >= adj_start && k <= adj_stop) {
+                    Keys->push_back(k);
+                    Values->push_back(curve->GetValues().at(n));
+                }
+            }
+
+            inputs.push_back(std::make_tuple(Keys, Values, mapto));
+        }
+    }
+    return inputs; // pray for NRVO :-)
+}
+
+KeyTimeList FBXConverter::GetKeyTimeList(const KeyFrameListList &inputs) {
+    ai_assert(!inputs.empty());
+
+    // reserve some space upfront - it is likely that the key-frame lists
+    // have matching time values, so max(of all key-frame lists) should
+    // be a good estimate.
+    KeyTimeList keys;
+
+    size_t estimate = 0;
+    for (const KeyFrameList &kfl : inputs) {
+        estimate = std::max(estimate, std::get<0>(kfl)->size());
+    }
+
+    keys.reserve(estimate);
+
+    std::vector<unsigned int> next_pos;
+    next_pos.resize(inputs.size(), 0);
+
+    const size_t count = inputs.size();
+    while (true) {
+
+        int64_t min_tick = std::numeric_limits<int64_t>::max();
+        for (size_t i = 0; i < count; ++i) {
+            const KeyFrameList &kfl = inputs[i];
+
+            if (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) < min_tick) {
+                min_tick = std::get<0>(kfl)->at(next_pos[i]);
+            }
+        }
+
+        if (min_tick == std::numeric_limits<int64_t>::max()) {
+            break;
+        }
+        keys.push_back(min_tick);
+
+        for (size_t i = 0; i < count; ++i) {
+            const KeyFrameList &kfl = inputs[i];
+
+            while (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == min_tick) {
+                ++next_pos[i];
+            }
+        }
+    }
+
+    return keys;
+}
+
+void FBXConverter::InterpolateKeys(aiVectorKey *valOut, const KeyTimeList &keys, const KeyFrameListList &inputs,
+        const aiVector3D &def_value,
+        double &max_time,
+        double &min_time) {
+    ai_assert(!keys.empty());
+    ai_assert(nullptr != valOut);
+
+    std::vector<unsigned int> next_pos;
+    const size_t count(inputs.size());
+
+    next_pos.resize(inputs.size(), 0);
+
+    for (KeyTimeList::value_type time : keys) {
+        ai_real result[3] = { def_value.x, def_value.y, def_value.z };
+
+        for (size_t i = 0; i < count; ++i) {
+            const KeyFrameList &kfl = inputs[i];
+
+            const size_t ksize = std::get<0>(kfl)->size();
+            if (ksize == 0) {
+                continue;
+            }
+            if (ksize > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == time) {
+                ++next_pos[i];
+            }
+
+            const size_t id0 = next_pos[i] > 0 ? next_pos[i] - 1 : 0;
+            const size_t id1 = next_pos[i] == ksize ? ksize - 1 : next_pos[i];
+
+            // use lerp for interpolation
+            const KeyValueList::value_type valueA = std::get<1>(kfl)->at(id0);
+            const KeyValueList::value_type valueB = std::get<1>(kfl)->at(id1);
+
+            const KeyTimeList::value_type timeA = std::get<0>(kfl)->at(id0);
+            const KeyTimeList::value_type timeB = std::get<0>(kfl)->at(id1);
+
+            const ai_real factor = timeB == timeA ? ai_real(0.) : static_cast<ai_real>((time - timeA)) / (timeB - timeA);
+            const ai_real interpValue = static_cast<ai_real>(valueA + (valueB - valueA) * factor);
+
+            result[std::get<2>(kfl)] = interpValue;
+        }
+
+        // magic value to convert fbx times to seconds
+        valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps;
+
+        min_time = std::min(min_time, valOut->mTime);
+        max_time = std::max(max_time, valOut->mTime);
+
+        valOut->mValue.x = result[0];
+        valOut->mValue.y = result[1];
+        valOut->mValue.z = result[2];
+
+        ++valOut;
+    }
+}
+
+void FBXConverter::InterpolateKeys(aiQuatKey *valOut, const KeyTimeList &keys, const KeyFrameListList &inputs,
+        const aiVector3D &def_value,
+        double &maxTime,
+        double &minTime,
+        Model::RotOrder order) {
+    ai_assert(!keys.empty());
+    ai_assert(nullptr != valOut);
+
+    std::unique_ptr<aiVectorKey[]> temp(new aiVectorKey[keys.size()]);
+    InterpolateKeys(temp.get(), keys, inputs, def_value, maxTime, minTime);
+
+    aiMatrix4x4 m;
+
+    aiQuaternion lastq;
+
+    for (size_t i = 0, c = keys.size(); i < c; ++i) {
+
+        valOut[i].mTime = temp[i].mTime;
+
+        GetRotationMatrix(order, temp[i].mValue, m);
+        aiQuaternion quat = aiQuaternion(aiMatrix3x3(m));
+
+        // take shortest path by checking the inner product
+        // http://www.3dkingdoms.com/weekly/weekly.php?a=36
+        if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0) {
+            quat.Conjugate();
+            quat.w = -quat.w;
+        }
+        lastq = quat;
+
+        valOut[i].mValue = quat;
+    }
+}
+
+aiQuaternion FBXConverter::EulerToQuaternion(const aiVector3D &rot, Model::RotOrder order) {
+    aiMatrix4x4 m;
+    GetRotationMatrix(order, rot, m);
+
+    return aiQuaternion(aiMatrix3x3(m));
+}
+
+void FBXConverter::ConvertScaleKeys(aiNodeAnim *na, const std::vector<const AnimationCurveNode *> &nodes, const LayerMap & /*layers*/,
+        int64_t start, int64_t stop,
+        double &maxTime,
+        double &minTime) {
+    ai_assert(nodes.size());
+
+    // XXX for now, assume scale should be blended geometrically (i.e. two
+    // layers should be multiplied with each other). There is a FBX
+    // property in the layer to specify the behaviour, though.
+
+    const KeyFrameListList &inputs = GetKeyframeList(nodes, start, stop);
+    const KeyTimeList &keys = GetKeyTimeList(inputs);
+
+    na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
+    na->mScalingKeys = new aiVectorKey[keys.size()];
+    if (keys.size() > 0) {
+        InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime);
+    }
+}
+
+void FBXConverter::ConvertTranslationKeys(aiNodeAnim *na, const std::vector<const AnimationCurveNode *> &nodes,
+        const LayerMap & /*layers*/,
+        int64_t start, int64_t stop,
+        double &maxTime,
+        double &minTime) {
+    ai_assert(nodes.size());
+
+    // XXX see notes in ConvertScaleKeys()
+    const KeyFrameListList &inputs = GetKeyframeList(nodes, start, stop);
+    const KeyTimeList &keys = GetKeyTimeList(inputs);
+
+    na->mNumPositionKeys = static_cast<unsigned int>(keys.size());
+    na->mPositionKeys = new aiVectorKey[keys.size()];
+    if (keys.size() > 0)
+        InterpolateKeys(na->mPositionKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime);
+}
+
+void FBXConverter::ConvertRotationKeys(aiNodeAnim *na, const std::vector<const AnimationCurveNode *> &nodes,
+        const LayerMap & /*layers*/,
+        int64_t start, int64_t stop,
+        double &maxTime,
+        double &minTime,
+        Model::RotOrder order) {
+    ai_assert(nodes.size());
+
+    // XXX see notes in ConvertScaleKeys()
+    const std::vector<KeyFrameList> &inputs = GetKeyframeList(nodes, start, stop);
+    const KeyTimeList &keys = GetKeyTimeList(inputs);
+
+    na->mNumRotationKeys = static_cast<unsigned int>(keys.size());
+    na->mRotationKeys = new aiQuatKey[keys.size()];
+    if (!keys.empty()) {
+        InterpolateKeys(na->mRotationKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime, order);
+    }
+}
+
+void FBXConverter::ConvertGlobalSettings() {
+    if (nullptr == mSceneOut) {
+        return;
+    }
+
+    const bool hasGenerator = !doc.Creator().empty();
+
+    mSceneOut->mMetaData = aiMetadata::Alloc(16 + (hasGenerator ? 1 : 0));
+    mSceneOut->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis());
+    mSceneOut->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign());
+    mSceneOut->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis());
+    mSceneOut->mMetaData->Set(3, "FrontAxisSign", doc.GlobalSettings().FrontAxisSign());
+    mSceneOut->mMetaData->Set(4, "CoordAxis", doc.GlobalSettings().CoordAxis());
+    mSceneOut->mMetaData->Set(5, "CoordAxisSign", doc.GlobalSettings().CoordAxisSign());
+    mSceneOut->mMetaData->Set(6, "OriginalUpAxis", doc.GlobalSettings().OriginalUpAxis());
+    mSceneOut->mMetaData->Set(7, "OriginalUpAxisSign", doc.GlobalSettings().OriginalUpAxisSign());
+    //const double unitScaleFactor = (double)doc.GlobalSettings().UnitScaleFactor();
+    mSceneOut->mMetaData->Set(8, "UnitScaleFactor", doc.GlobalSettings().UnitScaleFactor());
+    mSceneOut->mMetaData->Set(9, "OriginalUnitScaleFactor", doc.GlobalSettings().OriginalUnitScaleFactor());
+    mSceneOut->mMetaData->Set(10, "AmbientColor", doc.GlobalSettings().AmbientColor());
+    mSceneOut->mMetaData->Set(11, "FrameRate", (int)doc.GlobalSettings().TimeMode());
+    mSceneOut->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart());
+    mSceneOut->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop());
+    mSceneOut->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
+    mSceneOut->mMetaData->Set(15, AI_METADATA_SOURCE_FORMAT_VERSION, aiString(to_string(doc.FBXVersion())));
+    if (hasGenerator) {
+        mSceneOut->mMetaData->Set(16, AI_METADATA_SOURCE_GENERATOR, aiString(doc.Creator()));
+    }
+}
+
+void FBXConverter::TransferDataToScene() {
+    ai_assert(!mSceneOut->mMeshes);
+    ai_assert(!mSceneOut->mNumMeshes);
+
+    // note: the trailing () ensures initialization with nullptr - not
+    // many C++ users seem to know this, so pointing it out to avoid
+    // confusion why this code works.
+
+    if (mMeshes.size()) {
+        mSceneOut->mMeshes = new aiMesh *[mMeshes.size()]();
+        mSceneOut->mNumMeshes = static_cast<unsigned int>(mMeshes.size());
+
+        std::swap_ranges(mMeshes.begin(), mMeshes.end(), mSceneOut->mMeshes);
+    }
+
+    if (materials.size()) {
+        mSceneOut->mMaterials = new aiMaterial *[materials.size()]();
+        mSceneOut->mNumMaterials = static_cast<unsigned int>(materials.size());
+
+        std::swap_ranges(materials.begin(), materials.end(), mSceneOut->mMaterials);
+    }
+
+    if (animations.size()) {
+        mSceneOut->mAnimations = new aiAnimation *[animations.size()]();
+        mSceneOut->mNumAnimations = static_cast<unsigned int>(animations.size());
+
+        std::swap_ranges(animations.begin(), animations.end(), mSceneOut->mAnimations);
+    }
+
+    if (lights.size()) {
+        mSceneOut->mLights = new aiLight *[lights.size()]();
+        mSceneOut->mNumLights = static_cast<unsigned int>(lights.size());
+
+        std::swap_ranges(lights.begin(), lights.end(), mSceneOut->mLights);
+    }
+
+    if (cameras.size()) {
+        mSceneOut->mCameras = new aiCamera *[cameras.size()]();
+        mSceneOut->mNumCameras = static_cast<unsigned int>(cameras.size());
+
+        std::swap_ranges(cameras.begin(), cameras.end(), mSceneOut->mCameras);
+    }
+
+    if (textures.size()) {
+        mSceneOut->mTextures = new aiTexture *[textures.size()]();
+        mSceneOut->mNumTextures = static_cast<unsigned int>(textures.size());
+
+        std::swap_ranges(textures.begin(), textures.end(), mSceneOut->mTextures);
+    }
+}
+
+void FBXConverter::ConvertOrphanedEmbeddedTextures() {
+    // in C++14 it could be:
+    // for (auto&& [id, object] : objects)
+    for (auto &&id_and_object : doc.Objects()) {
+        auto &&id = std::get<0>(id_and_object);
+        auto &&object = std::get<1>(id_and_object);
+        // If an object doesn't have parent
+        if (doc.ConnectionsBySource().count(id) == 0) {
+            const Texture *realTexture = nullptr;
+            try {
+                const auto &element = object->GetElement();
+                const Token &key = element.KeyToken();
+                const char *obtype = key.begin();
+                const size_t length = static_cast<size_t>(key.end() - key.begin());
+                if (strncmp(obtype, "Texture", length) == 0) {
+                    const Texture *texture = static_cast<const Texture *>(object->Get());
+                    if (texture->Media() && texture->Media()->ContentLength() > 0) {
+                        realTexture = texture;
+                    }
+                }
+            } catch (...) {
+                // do nothing
+            }
+            if (realTexture) {
+                const Video *media = realTexture->Media();
+                unsigned int index = ConvertVideo(*media);
+                textures_converted[*media] = index;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertToAssimpScene(aiScene *out, const Document &doc, bool removeEmptyBones) {
+    FBXConverter converter(out, doc, removeEmptyBones);
+}
+
+} // namespace FBX
+} // namespace Assimp
+
+#endif

部分文件因为文件数量过多而无法显示