Browse Source

Merge branch 'master' into issue_3500

Kim Kulling 4 years ago
parent
commit
c4ba5eeeea
100 changed files with 5421 additions and 7034 deletions
  1. 0 1
      .github/FUNDING.yml
  2. 48 14
      .github/workflows/ccpp.yml
  3. 1 1
      .github/workflows/sanitizer.yml
  4. 0 3
      AssimpBuildTreeSettings.cmake.in
  5. 0 11
      AssimpConfigVersion.cmake.in
  6. 42 70
      CMakeLists.txt
  7. 1 1
      Readme.md
  8. 0 48
      assimp-config-version.cmake.in
  9. 0 1
      assimp-config.cmake.in
  10. 0 126
      assimpTargets-debug.cmake.in
  11. 0 127
      assimpTargets-release.cmake.in
  12. 0 91
      assimpTargets.cmake.in
  13. 1 1
      cmake-modules/FindRT.cmake
  14. 15 16
      cmake/HunterGate.cmake
  15. 1 1
      cmake/assimp-hunter-config.cmake.in
  16. 9 0
      cmake/assimp-plain-config.cmake.in
  17. 2 2
      code/AssetLib/3DS/3DSLoader.cpp
  18. 0 1
      code/AssetLib/3MF/3MFXmlTags.h
  19. 0 1
      code/AssetLib/3MF/D3MFExporter.cpp
  20. 0 1
      code/AssetLib/3MF/D3MFExporter.h
  21. 84 112
      code/AssetLib/3MF/D3MFImporter.cpp
  22. 35 29
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  23. 2 7
      code/AssetLib/3MF/D3MFOpcPackage.h
  24. 1 1
      code/AssetLib/AC/ACLoader.cpp
  25. 165 298
      code/AssetLib/AMF/AMFImporter.cpp
  26. 177 298
      code/AssetLib/AMF/AMFImporter.hpp
  27. 179 247
      code/AssetLib/AMF/AMFImporter_Geometry.cpp
  28. 0 2
      code/AssetLib/AMF/AMFImporter_Macro.hpp
  29. 148 147
      code/AssetLib/AMF/AMFImporter_Material.cpp
  30. 116 150
      code/AssetLib/AMF/AMFImporter_Node.hpp
  31. 176 156
      code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  32. 1 1
      code/AssetLib/ASE/ASELoader.cpp
  33. 2 2
      code/AssetLib/B3D/B3DImporter.cpp
  34. 19 18
      code/AssetLib/BVH/BVHLoader.cpp
  35. 2 1
      code/AssetLib/BVH/BVHLoader.h
  36. 1 1
      code/AssetLib/Blender/BlenderCustomData.cpp
  37. 4 13
      code/AssetLib/Blender/BlenderDNA.cpp
  38. 6 6
      code/AssetLib/Blender/BlenderDNA.h
  39. 22 34
      code/AssetLib/Blender/BlenderDNA.inl
  40. 2 3
      code/AssetLib/Blender/BlenderLoader.cpp
  41. 5 38
      code/AssetLib/Blender/BlenderLoader.h
  42. 5 2
      code/AssetLib/Blender/BlenderScene.h
  43. 1 1
      code/AssetLib/COB/COBLoader.cpp
  44. 1 1
      code/AssetLib/CSM/CSMLoader.cpp
  45. 1 1
      code/AssetLib/Collada/ColladaExporter.cpp
  46. 1 2
      code/AssetLib/Collada/ColladaHelper.cpp
  47. 109 119
      code/AssetLib/Collada/ColladaHelper.h
  48. 49 52
      code/AssetLib/Collada/ColladaLoader.cpp
  49. 890 1320
      code/AssetLib/Collada/ColladaParser.cpp
  50. 66 111
      code/AssetLib/Collada/ColladaParser.h
  51. 1 1
      code/AssetLib/DXF/DXFLoader.cpp
  52. 2 2
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  53. 5 4
      code/AssetLib/FBX/FBXConverter.cpp
  54. 3 3
      code/AssetLib/FBX/FBXDocumentUtil.cpp
  55. 10 1
      code/AssetLib/FBX/FBXImporter.cpp
  56. 1 1
      code/AssetLib/FBX/FBXMaterial.cpp
  57. 2 2
      code/AssetLib/FBX/FBXParser.cpp
  58. 10 1
      code/AssetLib/FBX/FBXProperties.cpp
  59. 1 1
      code/AssetLib/FBX/FBXTokenizer.cpp
  60. 9 11
      code/AssetLib/FBX/FBXUtil.cpp
  61. 7 14
      code/AssetLib/FBX/FBXUtil.h
  62. 3 3
      code/AssetLib/HMP/HMPLoader.cpp
  63. 1212 1226
      code/AssetLib/Irr/IRRLoader.cpp
  64. 11 22
      code/AssetLib/Irr/IRRLoader.h
  65. 426 446
      code/AssetLib/Irr/IRRMeshLoader.cpp
  66. 8 15
      code/AssetLib/Irr/IRRMeshLoader.h
  67. 215 329
      code/AssetLib/Irr/IRRShared.cpp
  68. 45 44
      code/AssetLib/Irr/IRRShared.h
  69. 3 3
      code/AssetLib/LWO/LWOLoader.cpp
  70. 11 6
      code/AssetLib/LWS/LWSLoader.cpp
  71. 5 2
      code/AssetLib/M3D/M3DExporter.cpp
  72. 5 5
      code/AssetLib/M3D/M3DImporter.cpp
  73. 3 0
      code/AssetLib/M3D/M3DWrapper.cpp
  74. 1 1
      code/AssetLib/M3D/m3d.h
  75. 1 1
      code/AssetLib/MD2/MD2Loader.cpp
  76. 1 1
      code/AssetLib/MD3/MD3Loader.cpp
  77. 1 1
      code/AssetLib/MD5/MD5Loader.cpp
  78. 1 1
      code/AssetLib/MDC/MDCLoader.cpp
  79. 2 2
      code/AssetLib/MDL/HalfLife/HL1MDLLoader.h
  80. 3 3
      code/AssetLib/MDL/MDLLoader.cpp
  81. 2 2
      code/AssetLib/MMD/MMDImporter.cpp
  82. 1 1
      code/AssetLib/MMD/MMDPmxParser.cpp
  83. 1 1
      code/AssetLib/MS3D/MS3DLoader.cpp
  84. 1 1
      code/AssetLib/NFF/NFFLoader.cpp
  85. 1 1
      code/AssetLib/OFF/OFFLoader.cpp
  86. 20 15
      code/AssetLib/Obj/ObjFileImporter.cpp
  87. 6 2
      code/AssetLib/Obj/ObjFileMtlImporter.cpp
  88. 0 29
      code/AssetLib/Obj/ObjTools.h
  89. 9 9
      code/AssetLib/Ogre/OgreBinarySerializer.cpp
  90. 192 200
      code/AssetLib/Ogre/OgreBinarySerializer.h
  91. 20 29
      code/AssetLib/Ogre/OgreImporter.cpp
  92. 5 5
      code/AssetLib/Ogre/OgreStructs.cpp
  93. 318 428
      code/AssetLib/Ogre/OgreXmlSerializer.cpp
  94. 32 48
      code/AssetLib/Ogre/OgreXmlSerializer.h
  95. 408 413
      code/AssetLib/OpenGEX/OpenGEXImporter.cpp
  96. 2 2
      code/AssetLib/Ply/PlyLoader.cpp
  97. 1 1
      code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp
  98. 2 3
      code/AssetLib/Q3D/Q3DLoader.cpp
  99. 1 1
      code/AssetLib/Raw/RawLoader.cpp
  100. 1 1
      code/AssetLib/SIB/SIBImporter.cpp

+ 0 - 1
.github/FUNDING.yml

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

+ 48 - 14
.github/workflows/ccpp.yml

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

+ 1 - 1
.github/workflows/sanitizer.yml

@@ -19,7 +19,7 @@ jobs:
         CC: clang
     
     - name: configure and build
-      uses: lukka/run-cmake@v2
+      uses: lukka/run-cmake@v3
       with:
         cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
         cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'

+ 0 - 3
AssimpBuildTreeSettings.cmake.in

@@ -1,3 +0,0 @@
-set(ASSIMP_INCLUDE_DIRS
-  "@PROJECT_SOURCE_DIR@"
-  "@PROJECT_BINARY_DIR@")

+ 0 - 11
AssimpConfigVersion.cmake.in

@@ -1,11 +0,0 @@
-set(ASSIMP_PACKAGE_VERSION "@ASSIMP_SOVERSION@")
-
-# Check whether the requested PACKAGE_FIND_VERSION is compatible
-if("${ASSIMP_PACKAGE_VERSION}" VERSION_LESS "${ASSIMP_PACKAGE_FIND_VERSION}")
-  set(ASSIMP_PACKAGE_VERSION_COMPATIBLE FALSE)
-else()
-  set(ASSIMP_PACKAGE_VERSION_COMPATIBLE TRUE)
-  if ("${ASSIMP_PACKAGE_VERSION}" VERSION_EQUAL "${ASSIMP_PACKAGE_FIND_VERSION}")
-    set(ASSIMP_PACKAGE_VERSION_EXACT TRUE)
-  endif()
-endif()

+ 42 - 70
CMakeLists.txt

@@ -45,8 +45,8 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
 IF(ASSIMP_HUNTER_ENABLED)
   include("cmake/HunterGate.cmake")
   HunterGate(
-    URL "https://github.com/cpp-pm/hunter/archive/v0.23.261.tar.gz"
-    SHA1 "1540dad7b97c849784a09e8c452ba811c9f71ba2"
+    URL "https://github.com/cpp-pm/hunter/archive/v0.23.269.tar.gz"
+    SHA1 "64024b7b95b4c86d50ae05b926814448c93a70a0"
   )
 
   add_definitions(-DASSIMP_USE_HUNTER)
@@ -117,10 +117,6 @@ OPTION ( ASSIMP_UBSAN
   "Enable Undefined Behavior sanitizer."
   OFF
 )
-OPTION ( ASSIMP_SYSTEM_IRRXML
-  "Use system installed Irrlicht/IrrXML library."
-  OFF
-)
 OPTION ( ASSIMP_BUILD_DOCS
   "Build documentation using Doxygen."
   OFF
@@ -136,8 +132,8 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
 )
 
 IF ( WIN32 )
-    OPTION ( ASSIMP_BUILD_ASSIMP_VIEW 
-      "If the Assimp view tool is built. (requires DirectX)" 
+    OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
+      "If the Assimp view tool is built. (requires DirectX)"
       OFF )
 ENDIF()
 
@@ -214,7 +210,7 @@ IF(NOT GIT_COMMIT_HASH)
 ENDIF()
 
 IF(ASSIMP_DOUBLE_PRECISION)
-    ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION)
+  ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION)
 ENDIF()
 
 CONFIGURE_FILE(
@@ -357,7 +353,7 @@ ELSE()
 ENDIF()
 
 # Only generate this target if no higher-level project already has
-IF (NOT TARGET uninstall)
+IF (NOT TARGET uninstall AND ASSIMP_INSTALL)
   # add make uninstall capability
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
   ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
@@ -391,76 +387,56 @@ ELSE()
   SET(CMAKE_INSTALL_FULL_BINDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_BIN_INSTALL_DIR})
 ENDIF()
 
+set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+
 IF(ASSIMP_HUNTER_ENABLED)
   set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
-  set(INCLUDE_INSTALL_DIR "include")
-
-  set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
-
-  # Configuration
+  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-hunter-config.cmake.in")
+  set(NAMESPACE "${PROJECT_NAME}::")
+  set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
   set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
   set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
-  set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
-  set(NAMESPACE "${PROJECT_NAME}::")
+ELSE()
+  set(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
+  set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-plain-config.cmake.in")
+  string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE)
+  set(NAMESPACE "${PROJECT_NAME_LOWERCASE}::")
+  set(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets")
+  set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME_LOWERCASE}ConfigVersion.cmake")
+  set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME_LOWERCASE}Config.cmake")
+ENDIF()
 
-  # Include module with fuction 'write_basic_package_version_file'
-  include(CMakePackageConfigHelpers)
+set(INCLUDE_INSTALL_DIR "include")
 
-  # Note: PROJECT_VERSION is used as a VERSION
-  write_basic_package_version_file("${VERSION_CONFIG}" COMPATIBILITY SameMajorVersion)
+# Include module with fuction 'write_basic_package_version_file'
+include(CMakePackageConfigHelpers)
 
-  # Use variables:
-  #   * TARGETS_EXPORT_NAME
-  #   * PROJECT_NAME
-  configure_package_config_file(
-      "cmake/assimp-hunter-config.cmake.in"
-      "${PROJECT_CONFIG}"
-      INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
-  )
+# Note: PROJECT_VERSION is used as a VERSION
+write_basic_package_version_file("${VERSION_CONFIG}" COMPATIBILITY SameMajorVersion)
 
-  install(
-      FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
-      DESTINATION "${CONFIG_INSTALL_DIR}"
-  )
+configure_package_config_file(
+    ${CMAKE_CONFIG_TEMPLATE_FILE}
+    "${PROJECT_CONFIG}"
+    INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
+)
 
-  install(
-      EXPORT "${TARGETS_EXPORT_NAME}"
-      NAMESPACE "${NAMESPACE}"
-      DESTINATION "${CONFIG_INSTALL_DIR}"
-  )
-ELSE()
-  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)
-    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-debug.cmake.in"   "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake" @ONLY IMMEDIATE)
-    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-release.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake" @ONLY IMMEDIATE)
-    SET(PACKAGE_TARGETS_FILE "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake")
-  ELSEIF (CMAKE_BUILD_TYPE STREQUAL Debug)
-    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-debug.cmake.in"   "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake" @ONLY IMMEDIATE)
-    SET(PACKAGE_TARGETS_FILE "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake")
-  ELSE()
-    CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-release.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake" @ONLY IMMEDIATE)
-    SET(PACKAGE_TARGETS_FILE "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake")
-  ENDIF()
-  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" @ONLY IMMEDIATE)
-  #we should generated these scripts after CMake VERSION 3.0.2 using export(EXPORT ...) and write_basic_package_version_file(...)
-  INSTALL(FILES
-    "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake"
-    "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake"
-    "${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()
+install(
+    FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
+    DESTINATION "${CONFIG_INSTALL_DIR}"
+    COMPONENT ${LIBASSIMP-DEV_COMPONENT}
+)
+
+install(
+    EXPORT "${TARGETS_EXPORT_NAME}"
+    NAMESPACE "${NAMESPACE}"
+    DESTINATION "${CONFIG_INSTALL_DIR}"
+    COMPONENT ${LIBASSIMP-DEV_COMPONENT}
+)
 
 IF( ASSIMP_BUILD_DOCS )
   ADD_SUBDIRECTORY(doc)
 ENDIF()
 
-# Look for system installed irrXML
-IF ( ASSIMP_SYSTEM_IRRXML )
-  FIND_PACKAGE( IrrXML REQUIRED )
-ENDIF()
-
 # Search for external dependencies, and build them from source if not found
 # Search for zlib
 IF(ASSIMP_HUNTER_ENABLED)
@@ -587,10 +563,6 @@ ELSE ()
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
 ENDIF ()
 
-IF(NOT ASSIMP_HUNTER_ENABLED)
-  ADD_SUBDIRECTORY(contrib)
-ENDIF()
-
 ADD_SUBDIRECTORY( code/ )
 IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
   # The viewer for windows only

+ 1 - 1
Readme.md

@@ -70,7 +70,7 @@ The source code is organized in the following way:
 For more information, visit [our website](http://assimp.org/). Or check out the `./doc`- folder, which contains the official documentation in HTML format.
 (CHMs for Windows are included in some release packages and should be located right here in the root folder).
 
-If the docs don't solve your problem, ask on [StackOverflow](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
+If the docs don't solve your problem, ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
 
 For development discussions, there is also a (very low-volume) mailing list, _assimp-discussions_
   [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions)

+ 0 - 48
assimp-config-version.cmake.in

@@ -1,48 +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.
-#----------------------------------------------------------------------
-set( PACKAGE_VERSION "@ASSIMP_VERSION@" )
-if( "${PACKAGE_FIND_VERSION}" VERSION_EQUAL "@ASSIMP_VERSION@")
-  set(PACKAGE_VERSION_EXACT 1)
-endif()
-if( "${PACKAGE_FIND_VERSION_MAJOR}.${PACKAGE_FIND_VERSION_MINOR}" EQUAL "@ASSIMP_VERSION@" )
-  set(PACKAGE_VERSION_COMPATIBLE 1)
-elseif( "${PACKAGE_FIND_VERSION_MAJOR}" EQUAL "@ASSIMP_VERSION_MAJOR@" )
-  # for now backward compatible if minor version is less
-  if( ${PACKAGE_FIND_VERSION_MINOR}  LESS @ASSIMP_VERSION_MINOR@ )
-    set(PACKAGE_VERSION_COMPATIBLE 1)
-  endif()
-endif()
-set( ASSIMP_STATIC_LIB "@ASSIMP_BUILD_STATIC_LIB@")

+ 0 - 1
assimp-config.cmake.in

@@ -1 +0,0 @@
-include(${CMAKE_CURRENT_LIST_DIR}/assimpTargets.cmake)

+ 0 - 126
assimpTargets-debug.cmake.in

@@ -1,126 +0,0 @@
-#----------------------------------------------------------------
-# Generated CMake target import file for configuration "Debug".
-#----------------------------------------------------------------
-
-# Commands may need to know the format version.
-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}")
-  else()
-    if( MSVC70 OR MSVC71 )
-      set(MSVC_PREFIX "vc70")
-    elseif( MSVC80 )
-      set(MSVC_PREFIX "vc80")
-    elseif( MSVC90 )
-      set(MSVC_PREFIX "vc90")
-    elseif( MSVC10 )
-      set(MSVC_PREFIX "vc100")
-    elseif( MSVC11 )
-      set(MSVC_PREFIX "vc110")
-    elseif( MSVC12 )
-      set(MSVC_PREFIX "vc120")
-    elseif( MSVC_VERSION LESS 1910)
-      set(MSVC_PREFIX "vc140")
-    elseif( MSVC_VERSION LESS 1920)
-      set(MSVC_PREFIX "vc141")
-    elseif( MSVC_VERSION LESS 1930)
-      set(MSVC_PREFIX "vc142")
-    else()
-      set(MSVC_PREFIX "vc150")
-    endif()
-  endif()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
-
-  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@")
-
-    # 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 "@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 "@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 "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
-    )
-    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}")
-  endif()
-
-else()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
-  if(ASSIMP_BUILD_SHARED_LIBS)
-    if(WIN32)
-      # Handle MinGW compiler.
-      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
-    elseif(APPLE)
-      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
-    else()
-      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
-    endif()
-
-    # 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 "@CMAKE_INSTALL_FULL_LIBDIR@/${sharedLibraryName}"
-    )
-    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    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 "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
-    )
-    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    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)
-
-get_filename_component(ASSIMP_ROOT_DIR "@CMAKE_INSTALL_PREFIX@" REALPATH)
-set( ASSIMP_CXX_FLAGS ) # dynamically linked library
-set( ASSIMP_LINK_FLAGS "" )
-set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
-set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@")
-if(ASSIMP_BUILD_SHARED_LIBS)
-  set( ASSIMP_LIBRARIES ${sharedLibraryName})
-else()
-  set( ASSIMP_LIBRARIES ${staticLibraryName})
-endif()
-
-# for compatibility with pkg-config
-set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
-set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}")
-
-MARK_AS_ADVANCED(
-  ASSIMP_ROOT_DIR
-  ASSIMP_CXX_FLAGS
-  ASSIMP_LINK_FLAGS
-  ASSIMP_INCLUDE_DIRS
-  ASSIMP_LIBRARIES
-  ASSIMP_CFLAGS_OTHER
-  ASSIMP_LDFLAGS_OTHER
-  ASSIMP_LIBRARY_SUFFIX
-  ASSIMP_BUILD_SHARED_LIBS
-)

+ 0 - 127
assimpTargets-release.cmake.in

@@ -1,127 +0,0 @@
-#----------------------------------------------------------------
-# Generated CMake target import file for configuration "Release".
-#----------------------------------------------------------------
-
-# Commands may need to know the format version.
-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}")
-  else()
-    if( MSVC70 OR MSVC71 )
-      set(MSVC_PREFIX "vc70")
-    elseif( MSVC80 )
-      set(MSVC_PREFIX "vc80")
-    elseif( MSVC90 )
-      set(MSVC_PREFIX "vc90")
-    elseif( MSVC10 )
-      set(MSVC_PREFIX "vc100")
-    elseif( MSVC11 )
-      set(MSVC_PREFIX "vc110")
-    elseif( MSVC12 )
-      set(MSVC_PREFIX "vc120")
-    elseif( MSVC_VERSION LESS 1910)
-      set(MSVC_PREFIX "vc140")
-    elseif( MSVC_VERSION LESS 1920)
-      set(MSVC_PREFIX "vc141")
-    elseif( MSVC_VERSION LESS 1930)
-      set(MSVC_PREFIX "vc142")
-    else()
-      set(MSVC_PREFIX "vc150")
-    endif()
-  endif()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
-
-  if(ASSIMP_BUILD_SHARED_LIBS)
-    set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@")
-    set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_IMPORT_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_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 "@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 "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
-    )
-    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}")
-  endif()
-
-else()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
-  if(ASSIMP_BUILD_SHARED_LIBS)
-    if(WIN32)
-      # Handle MinGW compiler.
-      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
-    elseif(APPLE)
-      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
-    else()
-      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
-    endif()
-
-    # 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 "@CMAKE_INSTALL_FULL_LIBDIR@/${sharedLibraryName}"
-    )
-    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    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 "@CMAKE_INSTALL_FULL_LIBDIR@/${staticLibraryName}"
-    )
-    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-    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)
-
-get_filename_component(ASSIMP_ROOT_DIR "@CMAKE_INSTALL_PREFIX@" REALPATH)
-
-set( ASSIMP_CXX_FLAGS ) # dynamically linked library
-set( ASSIMP_LINK_FLAGS "" )
-set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
-set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@")
-if(ASSIMP_BUILD_SHARED_LIBS)
-  set( ASSIMP_LIBRARIES ${sharedLibraryName})
-else()
-  set( ASSIMP_LIBRARIES ${staticLibraryName})
-endif()
-
-# for compatibility with pkg-config
-set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
-set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}")
-
-MARK_AS_ADVANCED(
-  ASSIMP_ROOT_DIR
-  ASSIMP_CXX_FLAGS
-  ASSIMP_LINK_FLAGS
-  ASSIMP_INCLUDE_DIRS
-  ASSIMP_LIBRARIES
-  ASSIMP_CFLAGS_OTHER
-  ASSIMP_LDFLAGS_OTHER
-  ASSIMP_LIBRARY_SUFFIX
-  ASSIMP_BUILD_SHARED_LIBS
-)

+ 0 - 91
assimpTargets.cmake.in

@@ -1,91 +0,0 @@
-# Generated by CMake
-
-if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5)
-   message(FATAL_ERROR "CMake >= 2.6.0 required")
-endif()
-cmake_policy(PUSH)
-cmake_policy(VERSION 2.6)
-# Required for the evaluation of "if(@BUILD_SHARED_LIBS@)" below to function
-cmake_policy(SET CMP0012 NEW)
-
-#----------------------------------------------------------------
-# Generated CMake target import file.
-#----------------------------------------------------------------
-
-# Commands may need to know the format version.
-set(CMAKE_IMPORT_FILE_VERSION 1)
-
-# Protect against multiple inclusion, which would fail when already imported targets are added once more.
-set(_targetsDefined)
-set(_targetsNotDefined)
-set(_expectedTargets)
-foreach(_expectedTarget assimp::assimp)
-  list(APPEND _expectedTargets ${_expectedTarget})
-  if(NOT TARGET ${_expectedTarget})
-    list(APPEND _targetsNotDefined ${_expectedTarget})
-  endif()
-  if(TARGET ${_expectedTarget})
-    list(APPEND _targetsDefined ${_expectedTarget})
-  endif()
-endforeach()
-if("${_targetsDefined}" STREQUAL "${_expectedTargets}")
-  unset(_targetsDefined)
-  unset(_targetsNotDefined)
-  unset(_expectedTargets)
-  set(CMAKE_IMPORT_FILE_VERSION)
-  cmake_policy(POP)
-  return()
-endif()
-if(NOT "${_targetsDefined}" STREQUAL "")
-  message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n")
-endif()
-unset(_targetsDefined)
-unset(_targetsNotDefined)
-unset(_expectedTargets)
-
-# 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 "@CMAKE_INSTALL_FULL_INCLUDEDIR@"
-  #INTERFACE_LINK_LIBRARIES "TxtUtils::TxtUtils;MealyMachine::MealyMachine"
-)
-
-if(CMAKE_VERSION VERSION_LESS 2.8.12)
-  message(FATAL_ERROR "This file relies on consumers using CMake 2.8.12 or greater.")
-endif()
-
-# Load information for each installed configuration.
-get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
-file(GLOB CONFIG_FILES "${_DIR}/assimpTargets-*.cmake")
-foreach(f ${CONFIG_FILES})
-  include(${f})
-endforeach()
-
-# Loop over all imported files and verify that they actually exist
-foreach(target ${_IMPORT_CHECK_TARGETS} )
-  foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )
-    if(NOT EXISTS "${file}" )
-      message(FATAL_ERROR "The imported target \"${target}\" references the file
-   \"${file}\"
-but this file does not exist.  Possible reasons include:
-* The file was deleted, renamed, or moved to another location.
-* An install or uninstall procedure did not complete successfully.
-* The installation package was faulty and contained
-   \"${CMAKE_CURRENT_LIST_FILE}\"
-but not all the files it references.
-")
-    endif()
-  endforeach()
-  unset(_IMPORT_CHECK_FILES_FOR_${target})
-endforeach()
-unset(_IMPORT_CHECK_TARGETS)
-
-# This file does not depend on other imported targets which have
-# been exported from the same project but in a separate export set.
-
-# Commands beyond this point should not need to know the version.
-set(CMAKE_IMPORT_FILE_VERSION)
-cmake_policy(POP)

+ 1 - 1
cmake-modules/FindRT.cmake

@@ -16,5 +16,5 @@ set(RT_LIBRARIES ${RT_LIBRARY})
 # handle the QUIETLY and REQUIRED arguments and set
 # RT_FOUND to TRUE if all listed variables are TRUE
 include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(rt DEFAULT_MSG RT_LIBRARY)
+find_package_handle_standard_args(RT DEFAULT_MSG RT_LIBRARY)
 mark_as_advanced(RT_LIBRARY)

+ 15 - 16
cmake/HunterGate.cmake

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

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

@@ -3,12 +3,12 @@
 find_package(RapidJSON CONFIG REQUIRED)
 find_package(ZLIB CONFIG REQUIRED)
 find_package(utf8cpp CONFIG REQUIRED)
-find_package(irrXML CONFIG REQUIRED)
 find_package(minizip CONFIG REQUIRED)
 find_package(openddlparser CONFIG REQUIRED)
 find_package(poly2tri CONFIG REQUIRED)
 find_package(polyclipping CONFIG REQUIRED)
 find_package(zip CONFIG REQUIRED)
+find_package(pugixml CONFIG REQUIRED)
 
 include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
 check_required_components("@PROJECT_NAME@")

+ 9 - 0
cmake/assimp-plain-config.cmake.in

@@ -0,0 +1,9 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
+
+set(ASSIMP_ROOT_DIR ${PACKAGE_PREFIX_DIR})
+set(ASSIMP_LIBRARIES assimp::assimp)
+set(ASSIMP_BUILD_SHARED_LIBS @BUILD_SHARED_LIBS@)
+get_property(ASSIMP_INCLUDE_DIRS TARGET assimp::assimp PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
+set(ASSIMP_LIBRARY_DIRS "")

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

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

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

@@ -4,7 +4,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,

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

@@ -4,7 +4,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,

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

@@ -4,7 +4,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,

+ 84 - 112
code/AssetLib/3MF/D3MFImporter.cpp

@@ -4,7 +4,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,
@@ -46,12 +45,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
+#include <assimp/XmlParser.h>
 #include <assimp/ZipArchiveIOSystem.h>
 #include <assimp/importerdesc.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/IOSystem.hpp>
-
 #include <cassert>
 #include <map>
 #include <memory>
@@ -61,7 +60,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "3MFXmlTags.h"
 #include "D3MFOpcPackage.h"
 #include <assimp/fast_atof.h>
-#include <assimp/irrXMLWrapper.h>
 
 #include <iomanip>
 
@@ -73,12 +71,12 @@ public:
     using MatArray = std::vector<aiMaterial *>;
     using MatId2MatArray = std::map<unsigned int, std::vector<unsigned int>>;
 
-    XmlSerializer(XmlReader *xmlReader) :
+    XmlSerializer(XmlParser *xmlParser) :
             mMeshes(),
             mMatArray(),
             mActiveMatGroup(99999999),
             mMatId2MatArray(),
-            xmlReader(xmlReader) {
+            mXmlParser(xmlParser) {
         // empty
     }
 
@@ -95,16 +93,21 @@ public:
         std::vector<aiNode *> children;
 
         std::string nodeName;
-        while (ReadToEndElement(D3MF::XmlTag::model)) {
-            nodeName = xmlReader->getNodeName();
-            if (nodeName == D3MF::XmlTag::object) {
-                children.push_back(ReadObject(scene));
-            } else if (nodeName == D3MF::XmlTag::build) {
+        XmlNode node = mXmlParser->getRootNode().child("model");
+        if (node.empty()) {
+            return;
+        }
+        XmlNode resNode = node.child("resources");
+        for (XmlNode currentNode = resNode.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string &currentNodeName = currentNode.name();
+            if (currentNodeName == D3MF::XmlTag::object) {
+                children.push_back(ReadObject(currentNode, scene));
+            } else if (currentNodeName == D3MF::XmlTag::build) {
                 //
-            } else if (nodeName == D3MF::XmlTag::basematerials) {
-                ReadBaseMaterials();
-            } else if (nodeName == D3MF::XmlTag::meta) {
-                ReadMetadata();
+            } else if (currentNodeName == D3MF::XmlTag::basematerials) {
+                ReadBaseMaterials(currentNode);
+            } else if (currentNodeName == D3MF::XmlTag::meta) {
+                ReadMetadata(currentNode);
             }
         }
 
@@ -134,38 +137,37 @@ public:
             std::copy(mMatArray.begin(), mMatArray.end(), scene->mMaterials);
         }
 
-        // create the scenegraph
+        // create the scene-graph
         scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
         scene->mRootNode->mChildren = new aiNode *[scene->mRootNode->mNumChildren]();
         std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
     }
 
 private:
-    aiNode *ReadObject(aiScene *scene) {
-        std::unique_ptr<aiNode> node(new aiNode());
+    aiNode *ReadObject(XmlNode &node, aiScene *scene) {
+        std::unique_ptr<aiNode> nodePtr(new aiNode());
 
         std::vector<unsigned long> meshIds;
 
-        const char *attrib(nullptr);
         std::string name, type;
-        attrib = xmlReader->getAttributeValue(D3MF::XmlTag::id.c_str());
-        if (nullptr != attrib) {
-            name = attrib;
+        pugi::xml_attribute attr = node.attribute(D3MF::XmlTag::id.c_str());
+        if (!attr.empty()) {
+            name = attr.as_string();
         }
-        attrib = xmlReader->getAttributeValue(D3MF::XmlTag::type.c_str());
-        if (nullptr != attrib) {
-            type = attrib;
+        attr = node.attribute(D3MF::XmlTag::type.c_str());
+        if (!attr.empty()) {
+            type = attr.as_string();
         }
 
-        node->mParent = scene->mRootNode;
-        node->mName.Set(name);
+        nodePtr->mParent = scene->mRootNode;
+        nodePtr->mName.Set(name);
 
         size_t meshIdx = mMeshes.size();
 
-        while (ReadToEndElement(D3MF::XmlTag::object)) {
-            if (xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
-                auto mesh = ReadMesh();
-
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == D3MF::XmlTag::mesh) {
+                auto mesh = ReadMesh(currentNode);
                 mesh->mName.Set(name);
                 mMeshes.push_back(mesh);
                 meshIds.push_back(static_cast<unsigned long>(meshIdx));
@@ -173,33 +175,34 @@ private:
             }
         }
 
-        node->mNumMeshes = static_cast<unsigned int>(meshIds.size());
+        nodePtr->mNumMeshes = static_cast<unsigned int>(meshIds.size());
 
-        node->mMeshes = new unsigned int[node->mNumMeshes];
+        nodePtr->mMeshes = new unsigned int[nodePtr->mNumMeshes];
 
-        std::copy(meshIds.begin(), meshIds.end(), node->mMeshes);
+        std::copy(meshIds.begin(), meshIds.end(), nodePtr->mMeshes);
 
-        return node.release();
+        return nodePtr.release();
     }
 
-    aiMesh *ReadMesh() {
+    aiMesh *ReadMesh(XmlNode &node) {
         aiMesh *mesh = new aiMesh();
-        while (ReadToEndElement(D3MF::XmlTag::mesh)) {
-            if (xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
-                ImportVertices(mesh);
-            } else if (xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
-                ImportTriangles(mesh);
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == D3MF::XmlTag::vertices) {
+                ImportVertices(currentNode, mesh);
+            } else if (currentName == D3MF::XmlTag::triangles) {
+                ImportTriangles(currentNode, mesh);
             }
+
         }
 
         return mesh;
     }
 
-    void ReadMetadata() {
-        const std::string name = xmlReader->getAttributeValue(D3MF::XmlTag::meta_name.c_str());
-        xmlReader->read();
-        const std::string value = xmlReader->getNodeData();
-
+    void ReadMetadata(XmlNode &node) {
+        pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name.c_str());
+        const std::string name = attribute.as_string();
+        const std::string value = node.value();
         if (name.empty()) {
             return;
         }
@@ -210,37 +213,36 @@ private:
         mMetaData.push_back(entry);
     }
 
-    void ImportVertices(aiMesh *mesh) {
+    void ImportVertices(XmlNode &node, aiMesh *mesh) {
         std::vector<aiVector3D> vertices;
-        while (ReadToEndElement(D3MF::XmlTag::vertices)) {
-            if (xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
-                vertices.push_back(ReadVertex());
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == D3MF::XmlTag::vertex) {
+                vertices.push_back(ReadVertex(currentNode));
             }
         }
+
         mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
         mesh->mVertices = new aiVector3D[mesh->mNumVertices];
-
         std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
     }
 
-    aiVector3D ReadVertex() {
+    aiVector3D ReadVertex(XmlNode &node) {
         aiVector3D vertex;
-
-        vertex.x = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::x.c_str()), nullptr);
-        vertex.y = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::y.c_str()), nullptr);
-        vertex.z = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::z.c_str()), nullptr);
+        vertex.x = ai_strtof(node.attribute(D3MF::XmlTag::x.c_str()).as_string(), nullptr);
+        vertex.y = ai_strtof(node.attribute(D3MF::XmlTag::y.c_str()).as_string(), nullptr);
+        vertex.z = ai_strtof(node.attribute(D3MF::XmlTag::z.c_str()).as_string(), nullptr);
 
         return vertex;
     }
 
-    void ImportTriangles(aiMesh *mesh) {
+    void ImportTriangles(XmlNode &node, 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()));
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == D3MF::XmlTag::triangle) {
+                faces.push_back(ReadTriangle(currentNode));
+                const char *pidToken = currentNode.attribute(D3MF::XmlTag::p1.c_str()).as_string();
                 if (nullptr != pidToken) {
                     int matIdx(std::atoi(pidToken));
                     mesh->mMaterialIndex = matIdx;
@@ -255,21 +257,21 @@ private:
         std::copy(faces.begin(), faces.end(), mesh->mFaces);
     }
 
-    aiFace ReadTriangle() {
+    aiFace ReadTriangle(XmlNode &node) {
         aiFace face;
 
         face.mNumIndices = 3;
         face.mIndices = new unsigned int[face.mNumIndices];
-        face.mIndices[0] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v1.c_str())));
-        face.mIndices[1] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v2.c_str())));
-        face.mIndices[2] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v3.c_str())));
+        face.mIndices[0] = static_cast<unsigned int>(std::atoi(node.attribute(D3MF::XmlTag::v1.c_str()).as_string()));
+        face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(D3MF::XmlTag::v2.c_str()).as_string()));
+        face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(D3MF::XmlTag::v3.c_str()).as_string()));
 
         return face;
     }
 
-    void ReadBaseMaterials() {
+    void ReadBaseMaterials(XmlNode &node) {
         std::vector<unsigned int> MatIdArray;
-        const char *baseMaterialId(xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_id.c_str()));
+        const char *baseMaterialId = node.attribute(D3MF::XmlTag::basematerials_id.c_str()).as_string();
         if (nullptr != baseMaterialId) {
             unsigned int id = std::atoi(baseMaterialId);
             const size_t newMatIdx(mMatArray.size());
@@ -287,9 +289,7 @@ private:
             mMatId2MatArray[mActiveMatGroup] = MatIdArray;
         }
 
-        while (ReadToEndElement(D3MF::XmlTag::basematerials)) {
-            mMatArray.push_back(readMaterialDef());
-        }
+        mMatArray.push_back(readMaterialDef(node));
     }
 
     bool parseColor(const char *color, aiColor4D &diffuse) {
@@ -339,19 +339,20 @@ private:
         return true;
     }
 
-    void assignDiffuseColor(aiMaterial *mat) {
-        const char *color = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_displaycolor.c_str());
+    void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
+        const char *color = node.attribute(D3MF::XmlTag::basematerials_displaycolor.c_str()).as_string();
         aiColor4D diffuse;
         if (parseColor(color, diffuse)) {
             mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
         }
     }
-    aiMaterial *readMaterialDef() {
+
+    aiMaterial *readMaterialDef(XmlNode &node) {
         aiMaterial *mat(nullptr);
         const char *name(nullptr);
-        const std::string nodeName(xmlReader->getNodeName());
+        const std::string nodeName = node.name();
         if (nodeName == D3MF::XmlTag::basematerials_base) {
-            name = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_name.c_str());
+            name = node.attribute(D3MF::XmlTag::basematerials_name.c_str()).as_string();
             std::string stdMatName;
             aiString matName;
             std::string strId(to_string(mActiveMatGroup));
@@ -368,40 +369,12 @@ private:
             mat = new aiMaterial;
             mat->AddProperty(&matName, AI_MATKEY_NAME);
 
-            assignDiffuseColor(mat);
+            assignDiffuseColor(node, mat);
         }
 
         return mat;
     }
 
-private:
-    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) {
-                return false;
-            }
-        }
-
-        return false;
-    }
-
-    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) {
-                return false;
-            }
-        }
-        ASSIMP_LOG_ERROR("unexpected EOF, expected closing <" + closeTag + "> tag");
-
-        return false;
-    }
-
 private:
     struct MetaEntry {
         std::string name;
@@ -412,7 +385,7 @@ private:
     MatArray mMatArray;
     unsigned int mActiveMatGroup;
     MatId2MatArray mMatId2MatArray;
-    XmlReader *xmlReader;
+    XmlParser *mXmlParser;
 };
 
 } //namespace D3MF
@@ -468,12 +441,11 @@ const aiImporterDesc *D3MFImporter::GetInfo() const {
 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()));
-    std::unique_ptr<D3MF::XmlReader> xmlReader(irr::io::createIrrXMLReader(xmlStream.get()));
-
-    D3MF::XmlSerializer xmlSerializer(xmlReader.get());
-
-    xmlSerializer.ImportXml(pScene);
+    XmlParser xmlParser;
+    if (xmlParser.parse(opcPackage.RootStream())) {
+        D3MF::XmlSerializer xmlSerializer(&xmlParser);
+        xmlSerializer.ImportXml(pScene);
+    }
 }
 
 } // Namespace Assimp

+ 35 - 29
code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -4,7 +4,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,
@@ -45,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "D3MFOpcPackage.h"
 #include <assimp/Exceptional.h>
 
+#include <assimp/XmlParser.h>
 #include <assimp/ZipArchiveIOSystem.h>
 #include <assimp/ai_assert.h>
 #include <assimp/DefaultLogger.hpp>
@@ -68,27 +68,22 @@ typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
 
 class OpcPackageRelationshipReader {
 public:
-    OpcPackageRelationshipReader(XmlReader *xmlReader) {
-        while (xmlReader->read()) {
-            if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
-                    xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_CONTAINER) {
-                ParseRootNode(xmlReader);
-            }
-        }
+    OpcPackageRelationshipReader(XmlParser &parser) {
+        XmlNode root = parser.getRootNode();
+        ParseRootNode(root);
     }
 
-    void ParseRootNode(XmlReader *xmlReader) {
-        ParseAttributes(xmlReader);
-
-        while (xmlReader->read()) {
-            if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
-                    xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_NODE) {
-                ParseChildNode(xmlReader);
+    void ParseRootNode(XmlNode &node) {
+        ParseAttributes(node);
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            std::string name = currentNode.name();
+            if (name == "Relationships") {
+                ParseRelationsNode(currentNode);
             }
         }
     }
 
-    void ParseAttributes(XmlReader *) {
+    void ParseAttributes(XmlNode & /*node*/) {
         // empty
     }
 
@@ -99,14 +94,22 @@ public:
         return true;
     }
 
-    void ParseChildNode(XmlReader *xmlReader) {
-        OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
+    void ParseRelationsNode(XmlNode &node) {
+        if (node.empty()) {
+            return;
+        }
 
-        relPtr->id = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_ID.c_str());
-        relPtr->type = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TYPE.c_str());
-        relPtr->target = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TARGET.c_str());
-        if (validateRels(relPtr)) {
-            m_relationShips.push_back(relPtr);
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            std::string name = currentNode.name();
+            if (name == "Relationship") {
+                OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
+                relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID.c_str()).as_string();
+                relPtr->type = currentNode.attribute(XmlTag::RELS_ATTRIB_TYPE.c_str()).as_string();
+                relPtr->target = currentNode.attribute(XmlTag::RELS_ATTRIB_TARGET.c_str()).as_string();
+                if (validateRels(relPtr)) {
+                    m_relationShips.push_back(relPtr);
+                }
+            }
         }
     }
 
@@ -115,10 +118,11 @@ public:
 
 // ------------------------------------------------------------------------------------------------
 D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
-        mRootStream(nullptr), mZipArchive() {
+        mRootStream(nullptr),
+        mZipArchive() {
     mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile));
     if (!mZipArchive->isOpen()) {
-        throw DeadlyImportError("Failed to open file " + rFile + ".");
+        throw DeadlyImportError("Failed to open file ", rFile, ".");
     }
 
     std::vector<std::string> fileList;
@@ -182,17 +186,19 @@ bool D3MFOpcPackage::validate() {
 }
 
 std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
-    std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(stream));
-    std::unique_ptr<XmlReader> xml(irr::io::createIrrXMLReader(xmlStream.get()));
+    XmlParser xmlParser;
+    if (!xmlParser.parse(stream)) {
+        return "";
+    }
 
-    OpcPackageRelationshipReader reader(xml.get());
+    OpcPackageRelationshipReader reader(xmlParser);
 
     auto itr = std::find_if(reader.m_relationShips.begin(), reader.m_relationShips.end(), [](const OpcPackageRelationshipPtr &rel) {
         return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
     });
 
     if (itr == reader.m_relationShips.end()) {
-        throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
+        throw DeadlyImportError("Cannot find ", XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
     }
 
     return (*itr)->target;

+ 2 - 7
code/AssetLib/3MF/D3MFOpcPackage.h

@@ -4,7 +4,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,18 +43,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define D3MFOPCPACKAGE_H
 
 #include <memory>
-
+#include <string>
 #include <assimp/IOSystem.hpp>
-#include <assimp/irrXMLWrapper.h>
 
 namespace Assimp {
     class ZipArchiveIOSystem;
 
 namespace D3MF {
 
-using XmlReader = irr::io::IrrXMLReader ;
-using XmlReaderPtr = std::shared_ptr<XmlReader> ;
-
 struct OpcPackageRelationship {
     std::string id;
     std::string type;
@@ -64,7 +59,7 @@ struct OpcPackageRelationship {
 
 class D3MFOpcPackage {
 public:
-    D3MFOpcPackage( IOSystem* pIOHandler, const std::string& rFile );
+    D3MFOpcPackage( IOSystem* pIOHandler, const std::string& file );
     ~D3MFOpcPackage();
     IOStream* RootStream() const;
     bool validate();

+ 1 - 1
code/AssetLib/AC/ACLoader.cpp

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

+ 165 - 298
code/AssetLib/AMF/AMFImporter.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,
@@ -60,8 +58,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
-/// \var aiImporterDesc AMFImporter::Description
-/// Conastant which hold importer description
 const aiImporterDesc AMFImporter::Description = {
     "Additive manufacturing file format(AMF) Importer",
     "smalcom",
@@ -82,7 +78,7 @@ void AMFImporter::Clear() {
     mTexture_Converted.clear();
     // Delete all elements
     if (!mNodeElement_List.empty()) {
-        for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
+        for (AMFNodeElementBase *ne : mNodeElement_List) {
             delete ne;
         }
 
@@ -90,8 +86,18 @@ void AMFImporter::Clear() {
     }
 }
 
+AMFImporter::AMFImporter() AI_NO_EXCEPT :
+        mNodeElement_Cur(nullptr),
+        mXmlParser(nullptr),
+        mUnit(),
+        mVersion(),
+        mMaterial_Converted(),
+        mTexture_Converted() {
+    // empty
+}
+
 AMFImporter::~AMFImporter() {
-    if (mReader != nullptr) delete mReader;
+    delete mXmlParser;
     // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
     Clear();
 }
@@ -100,10 +106,12 @@ AMFImporter::~AMFImporter() {
 /************************************************************ 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) {
+bool AMFImporter::Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const {
+    for (AMFNodeElementBase *ne : mNodeElement_List) {
         if ((ne->ID == pID) && (ne->Type == pType)) {
-            if (pNodeElement != nullptr) *pNodeElement = ne;
+            if (pNodeElement != nullptr) {
+                *pNodeElement = ne;
+            }
 
             return true;
         }
@@ -112,12 +120,13 @@ bool AMFImporter::Find_NodeElement(const std::string &pID, const CAMFImporter_No
     return false;
 }
 
-bool AMFImporter::Find_ConvertedNode(const std::string &pID, std::list<aiNode *> &pNodeList, aiNode **pNode) const {
+bool AMFImporter::Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const {
     aiString node_name(pID.c_str());
-
-    for (aiNode *node : pNodeList) {
+    for (aiNode *node : nodeArray) {
         if (node->mName == node_name) {
-            if (pNode != nullptr) *pNode = node;
+            if (pNode != nullptr) {
+                *pNode = node;
+            }
 
             return true;
         }
@@ -129,7 +138,9 @@ bool AMFImporter::Find_ConvertedNode(const std::string &pID, std::list<aiNode *>
 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;
+            if (pConvertedMaterial != nullptr) {
+                *pConvertedMaterial = &mat;
+            }
 
             return true;
         }
@@ -142,148 +153,38 @@ bool AMFImporter::Find_ConvertedMaterial(const std::string &pID, const SPP_Mater
 /************************************************************ 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_CloseNotFound(const std::string &nodeName) {
+    throw DeadlyImportError("Close tag for node <" + nodeName + "> 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_IncorrectAttr(const std::string &nodeName, const std::string &attrName) {
+    throw DeadlyImportError("Node <" + nodeName + "> has incorrect attribute \"" + attrName + "\".");
 }
 
-void AMFImporter::Throw_IncorrectAttrValue(const std::string &pAttrName) {
-    throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
+void AMFImporter::Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &attrName) {
+    throw DeadlyImportError("Attribute \"" + attrName + "\" in node <" + nodeName + "> 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_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) {
+    throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + nodeName + ". Description: " + pDescription);
 }
 
 void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
-    throw DeadlyImportError("Not found node with name \"" + pID + "\".");
+    throw DeadlyImportError("Not found node with name \"", pID, "\".");
 }
 
 /*********************************************************************************************************************************************/
 /************************************************************* 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;
+void AMFImporter::XML_CheckNode_MustHaveChildren(pugi::xml_node &node) {
+    if (node.children().begin() == node.children().end()) {
+        throw DeadlyImportError(std::string("Node <") + node.name() + "> must have children.");
     }
-
-    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;
+bool AMFImporter::XML_SearchNode(const std::string &nodeName) {
+    return nullptr != mXmlParser->findNode(nodeName);
 }
 
 void AMFImporter::ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString) {
@@ -362,29 +263,33 @@ void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std
 }
 
 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() == nullptr) {
-        throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
+        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
+    mXmlParser = new XmlParser();
+    if (!mXmlParser->parse(file.get())) {
+        delete mXmlParser;
+        throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
+    }
+
+    // Start reading, search for root tag <amf>
+    if (!mXmlParser->hasNode("amf")) {
         throw DeadlyImportError("Root node \"amf\" not found.");
+    }
+    ParseNode_Root();
+} // namespace Assimp
 
-    delete mReader;
-    // restore old XMLreader
-    mReader = OldReader;
+void AMFImporter::ParseHelper_Node_Enter(AMFNodeElementBase *node) {
+    mNodeElement_Cur->Child.push_back(node); // add new element to current element child list.
+    mNodeElement_Cur = node;
+}
+
+void AMFImporter::ParseHelper_Node_Exit() {
+    if (mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
 }
 
 // <amf
@@ -395,54 +300,48 @@ void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) {
 // Root XML element.
 // Multi elements - No.
 void AMFImporter::ParseNode_Root() {
-    std::string unit, version;
-    CAMFImporter_NodeElement *ne(nullptr);
+    AMFNodeElementBase *ne = nullptr;
+    XmlNode *root = mXmlParser->findNode("amf");
+    if (nullptr == root) {
+        throw DeadlyImportError("Root node \"amf\" not found.");
+    }
+    XmlNode node = *root;
+    mUnit = node.attribute("unit").as_string();
+    mVersion = node.attribute("version").as_string();
 
     // 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");
+        if ((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) {
+            Throw_IncorrectAttrValue("unit", mUnit);
+        }
     }
 
     // create root node element.
-    ne = new CAMFImporter_NodeElement_Root(nullptr);
+    ne = new AMFRoot(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;
+    ((AMFRoot *)ne)->Unit = mUnit;
+    ((AMFRoot *)ne)->Version = mVersion;
 
     // 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;
+    for (XmlNode &currentNode : node.children() ) {
+        const std::string currentName = currentNode.name();
+        if (currentName == "object") {
+            ParseNode_Object(currentNode);
+        } else if (currentName == "material") {
+            ParseNode_Material(currentNode);
+        } else if (currentName == "texture") {
+            ParseNode_Texture(currentNode);
+        } else if (currentName == "constellation") {
+            ParseNode_Constellation(currentNode);
+        } else if (currentName == "metadata") {
+            ParseNode_Metadata(currentNode);
         }
-        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_Cur = ne;
+    }
+    mNodeElement_Cur = ne; // force restore "current" element
     mNodeElement_List.push_back(ne); // add to node element list because its a new object in graph.
 }
 
@@ -453,40 +352,34 @@ void AMFImporter::ParseNode_Root() {
 // A collection of objects or constellations with specific relative locations.
 // Multi elements - Yes.
 // Parent element - <amf>.
-void AMFImporter::ParseNode_Constellation() {
+void AMFImporter::ParseNode_Constellation(XmlNode &node) {
     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;
+    id = node.attribute("id").as_string();
 
     // create and if needed - define new grouping object.
-    ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
+    AMFNodeElementBase *ne = new AMFConstellation(mNodeElement_Cur);
 
-    CAMFImporter_NodeElement_Constellation &als = *((CAMFImporter_NodeElement_Constellation *)ne); // alias for convenience
+    AMFConstellation &als = *((AMFConstellation *)ne); // alias for convenience
+
+    if (!id.empty()) {
+        als.ID = id;
+    }
 
-    if (!id.empty()) als.ID = id;
     // Check for child nodes
-    if (!mReader->isEmptyElement()) {
+    if (!node.empty()) {
         ParseHelper_Node_Enter(ne);
-        MACRO_NODECHECK_LOOPBEGIN("constellation");
-        if (XML_CheckNode_NameEqual("instance")) {
-            ParseNode_Instance();
-            continue;
-        }
-        if (XML_CheckNode_NameEqual("metadata")) {
-            ParseNode_Metadata();
-            continue;
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            std::string name = currentNode.name();
+            if (name == "instance") {
+                ParseNode_Instance(currentNode);
+            } else if (name == "metadata") {
+                ParseNode_Metadata(currentNode);
+            }
         }
-        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
-
+    } else {
+        mNodeElement_Cur->Child.push_back(ne);
+    }
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
 }
 
@@ -497,47 +390,43 @@ void AMFImporter::ParseNode_Constellation() {
 // 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);
+void AMFImporter::ParseNode_Instance(XmlNode &node) {
+    AMFNodeElementBase *ne(nullptr);
 
     // Read attributes for node <constellation>.
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
-    MACRO_ATTRREAD_LOOPEND;
+    std::string objectid = node.attribute("objectid").as_string();
 
     // used object id must be defined, check that.
-    if (objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
+    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
-
+    ne = new AMFInstance(mNodeElement_Cur);
+    AMFInstance &als = *((AMFInstance *)ne);
     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);
+    if (!node.empty()) {
         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");
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == "deltax") {
+                als.Delta.x = (ai_real)std::atof(currentNode.value());
+            } else if (currentName == "deltay") {
+                als.Delta.y = (ai_real)std::atof(currentNode.value());
+            } else if (currentName == "deltaz") {
+                als.Delta.z = (ai_real)std::atof(currentNode.value());
+            } else if (currentName == "rx") {
+                als.Delta.x = (ai_real)std::atof(currentNode.value());
+            } else if (currentName == "ry") {
+                als.Delta.y = (ai_real)std::atof(currentNode.value());
+            } else if (currentName == "rz") {
+                als.Delta.z = (ai_real)std::atof(currentNode.value());
+            }
+        }
         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
+    } else {
+        mNodeElement_Cur->Child.push_back(ne);
+    }
 
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
 }
@@ -549,51 +438,38 @@ void AMFImporter::ParseNode_Instance() {
 // An object definition.
 // Multi elements - Yes.
 // Parent element - <amf>.
-void AMFImporter::ParseNode_Object() {
-    std::string id;
-    CAMFImporter_NodeElement *ne(nullptr);
+void AMFImporter::ParseNode_Object(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
 
     // Read attributes for node <object>.
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
-    MACRO_ATTRREAD_LOOPEND;
+    std::string id = node.attribute("id").as_string();
 
     // create and if needed - define new geometry object.
-    ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
+    ne = new AMFObject(mNodeElement_Cur);
 
-    CAMFImporter_NodeElement_Object &als = *((CAMFImporter_NodeElement_Object *)ne); // alias for convenience
+    AMFObject &als = *((AMFObject *)ne); // alias for convenience
 
-    if (!id.empty()) als.ID = id;
-    // Check for child nodes
-    if (!mReader->isEmptyElement()) {
-        bool col_read = false;
+    if (!id.empty()) {
+        als.ID = id;
+    }
 
+    // Check for child nodes
+    if (!node.empty()) {
         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;
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == "color") {
+                ParseNode_Color(currentNode);
+            } else if (currentName == "mesh") {
+                ParseNode_Mesh(currentNode);
+            } else if (currentName == "metadata") {
+                ParseNode_Metadata(currentNode);
+            }
         }
-        MACRO_NODECHECK_LOOPEND("object");
         ParseHelper_Node_Exit();
-    } // if(!mReader->isEmptyElement())
-    else {
+    } 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.
 }
@@ -616,28 +492,20 @@ void AMFImporter::ParseNode_Object() {
 // "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);
+void AMFImporter::ParseNode_Metadata(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    std::string type = node.attribute("type").as_string(), value;
+    XmlParser::getValueAsString(node, value);
 
     // 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;
+    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);
 
@@ -645,9 +513,8 @@ bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool p
         return true;
     }
 
-    if (!extension.length() || pCheckSig) {
+    if (extension.empty() || pCheckSig) {
         const char *tokens[] = { "<amf" };
-
         return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
     }
 

+ 177 - 298
code/AssetLib/AMF/AMFImporter.hpp

@@ -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,
@@ -54,11 +52,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "AMFImporter_Node.hpp"
 
 // Header files, Assimp.
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/importerdesc.h>
 #include "assimp/types.h"
 #include <assimp/BaseImporter.h>
-#include <assimp/irrXMLWrapper.h>
+#include <assimp/XmlParser.h>
+#include <assimp/importerdesc.h>
+#include <assimp/DefaultLogger.hpp>
 
 // Header files, stdlib.
 #include <set>
@@ -101,22 +99,21 @@ namespace Assimp {
 ///
 class AMFImporter : public BaseImporter {
 private:
-    struct SPP_Material;// forward declaration
+    struct SPP_Material; // forward declaration
 
-                        /// \struct SPP_Composite
-                        /// Data type for post-processing step. More suitable container for part of material's composition.
+    /// Data type for post-processing step. More suitable container for part of material's composition.
     struct SPP_Composite {
-        SPP_Material* Material;///< Pointer to material - part of composition.
-        std::string Formula;///< Formula for calculating ratio of \ref Material.
+        SPP_Material *Material; ///< Pointer to material - part of composition.
+        std::string Formula; ///< Formula for calculating ratio of \ref Material.
     };
 
     /// \struct SPP_Material
     /// Data type for post-processing step. More suitable container for material.
     struct SPP_Material {
-        std::string ID;///< Material ID.
-        std::list<CAMFImporter_NodeElement_Metadata*> Metadata;///< Metadata of material.
-        CAMFImporter_NodeElement_Color* Color;///< Color of material.
-        std::list<SPP_Composite> Composition;///< List of child materials if current material is composition of few another.
+        std::string ID; ///< Material ID.
+        std::list<AMFMetadata *> Metadata; ///< Metadata of material.
+        AMFColor *Color; ///< Color of material.
+        std::list<SPP_Composite> Composition; ///< List of child materials if current material is composition of few another.
 
         /// Return color calculated for specified coordinate.
         /// \param [in] pX - "x" coordinate.
@@ -129,304 +126,186 @@ private:
     /// Data type for post-processing step. More suitable container for texture.
     struct SPP_Texture {
         std::string ID;
-        size_t      Width, Height, Depth;
-        bool        Tiled;
-        char        FormatHint[9];// 8 for string + 1 for terminator.
-        uint8_t    *Data;
+        size_t Width, Height, Depth;
+        bool Tiled;
+        char FormatHint[9]; // 8 for string + 1 for terminator.
+        uint8_t *Data;
     };
 
     /// Data type for post-processing step. Contain face data.
     struct SComplexFace {
-        aiFace Face;///< Face vertices.
-        const CAMFImporter_NodeElement_Color* Color;///< Face color. Equal to nullptr if color is not set for the face.
-        const CAMFImporter_NodeElement_TexMap* TexMap;///< Face texture mapping data. Equal to nullptr if texture mapping is not set for the face.
+        aiFace Face; ///< Face vertices.
+        const AMFColor *Color; ///< Face color. Equal to nullptr if color is not set for the face.
+        const AMFTexMap *TexMap; ///< Face texture mapping data. Equal to nullptr if texture mapping is not set for the face.
     };
 
-	/// Clear all temporary data.
-	void Clear();
-
-	/***********************************************/
-	/************* Functions: find set *************/
-	/***********************************************/
-
-	/// Find specified node element in node elements list ( \ref mNodeElement_List).
-	/// \param [in] pID - ID(name) of requested node element.
-	/// \param [in] pType - type of node element.
-	/// \param [out] pNode - pointer to pointer to item found.
-	/// \return true - if the node element is found, else - false.
-	bool Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const;
-
-	/// Find requested aiNode in node list.
-	/// \param [in] pID - ID(name) of requested node.
-	/// \param [in] pNodeList - list of nodes where to find the node.
-	/// \param [out] pNode - pointer to pointer to item found.
-	/// \return true - if the node is found, else - false.
-	bool Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const;
-
-	/// Find material in list for converted materials. Use at postprocessing step.
-	/// \param [in] pID - material ID.
-	/// \param [out] pConvertedMaterial - pointer to found converted material (\ref SPP_Material).
-	/// \return true - if the material is found, else - false.
-	bool Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const;
-
-    /// Find texture in list of converted textures. Use at postprocessing step,
-	/// \param [in] pID_R - ID of source "red" texture.
-	/// \param [in] pID_G - ID of source "green" texture.
-	/// \param [in] pID_B - ID of source "blue" texture.
-	/// \param [in] pID_A - ID of source "alpha" texture. Use empty string to find RGB-texture.
-	/// \param [out] pConvertedTextureIndex - pointer where index in list of found texture will be written. If equivalent to nullptr then nothing will be
-	/// written.
-	/// \return true - if the texture is found, else - false.
-	bool Find_ConvertedTexture(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A,
-								uint32_t* pConvertedTextureIndex = nullptr) const;
-
-
-	/// Get data stored in <vertices> and place it to arrays.
-	/// \param [in] pNodeElement - reference to node element which kept <object> data.
-	/// \param [in] pVertexCoordinateArray - reference to vertices coordinates kept in <vertices>.
-	/// \param [in] pVertexColorArray - reference to vertices colors for all <vertex's. If color for vertex is not set then corresponding member of array
-	/// contain nullptr.
-	void PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray,
-												std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const;
-
-	/// Return converted texture ID which related to specified source textures ID's. If converted texture does not exist then it will be created and ID on new
-	/// converted texture will be returned. Conversion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it
-	/// to converted textures list.
-	/// Any of source ID's can be absent(empty string) or even one ID only specified. But at least one ID must be specified.
-	/// \param [in] pID_R - ID of source "red" texture.
-	/// \param [in] pID_G - ID of source "green" texture.
-	/// \param [in] pID_B - ID of source "blue" texture.
-	/// \param [in] pID_A - ID of source "alpha" texture.
-	/// \return index of the texture in array of the converted textures.
-	size_t PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A);
-
-	/// Separate input list by texture IDs. This step is needed because aiMesh can contain mesh which is use only one texture (or set: diffuse, bump etc).
-	/// \param [in] pInputList - input list with faces. Some of them can contain color or texture mapping, or both of them, or nothing. Will be cleared after
-	/// processing.
-	/// \param [out] pOutputList_Separated - output list of the faces lists. Separated faces list by used texture IDs. Will be cleared before processing.
-	void PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> >& pOutputList_Separated);
-
-	/// Check if child elements of node element is metadata and add it to scene node.
-	/// \param [in] pMetadataList - reference to list with collected metadata.
-	/// \param [out] pSceneNode - scene node in which metadata will be added.
-	void Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& pMetadataList, aiNode& pSceneNode) const;
-
-	/// To create aiMesh and aiNode for it from <object>.
-	/// \param [in] pNodeElement - reference to node element which kept <object> data.
-	/// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
-	/// \param [out] pSceneNode - pointer to place where new aiNode will be created.
-	void Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode);
-
-	/// Create mesh for every <volume> in <mesh>.
-	/// \param [in] pNodeElement - reference to node element which kept <mesh> data.
-	/// \param [in] pVertexCoordinateArray - reference to vertices coordinates for all <volume>'s.
-	/// \param [in] pVertexColorArray - reference to vertices colors for all <volume>'s. If color for vertex is not set then corresponding member of array
-	/// contain nullptr.
-	/// \param [in] pObjectColor - pointer to colors for <object>. If color is not set then argument contain nullptr.
-	/// \param [in] pMaterialList - reference to a list with defined materials.
-	/// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
-	/// \param [out] pSceneNode - reference to aiNode which will own new aiMesh's.
-	void 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);
-
-	/// Convert material from \ref CAMFImporter_NodeElement_Material to \ref SPP_Material.
-	/// \param [in] pMaterial - source CAMFImporter_NodeElement_Material.
-	void Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial);
-
-	/// Create and add to aiNode's list new part of scene graph defined by <constellation>.
-	/// \param [in] pConstellation - reference to <constellation> node.
-	/// \param [out] pNodeList - reference to aiNode's list.
-	void Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const;
-
-	/// Build Assimp scene graph in aiScene from collected data.
-	/// \param [out] pScene - pointer to aiScene where tree will be built.
-	void Postprocess_BuildScene(aiScene* pScene);
-
-
-	/// Call that function when close tag of node not found and exception must be raised.
-	/// E.g.:
-	/// <amf>
-	///     <object>
-	/// </amf> <!--- object not closed --->
-	/// \throw DeadlyImportError.
-	/// \param [in] pNode - node name in which exception happened.
-	void Throw_CloseNotFound(const std::string& pNode);
-
-	/// Call that function when attribute name is incorrect and exception must be raised.
-	/// \param [in] pAttrName - attribute name.
-	/// \throw DeadlyImportError.
-	void Throw_IncorrectAttr(const std::string& pAttrName);
-
-	/// Call that function when attribute value is incorrect and exception must be raised.
-	/// \param [in] pAttrName - attribute name.
-	/// \throw DeadlyImportError.
-	void Throw_IncorrectAttrValue(const std::string& pAttrName);
-
-	/// Call that function when some type of nodes are defined twice or more when must be used only once and exception must be raised.
-	/// E.g.:
-	/// <object>
-	///     <color>...    <!--- color defined --->
-	///     <color>...    <!--- color defined again --->
-	/// </object>
-	/// \throw DeadlyImportError.
-	/// \param [in] pNodeType - type of node which defined one more time.
-	/// \param [in] pDescription - message about error. E.g. what the node defined while exception raised.
-	void Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription);
-
-	/// Call that function when referenced element ID are not found in graph and exception must be raised.
-	/// \param [in] pID - ID of of element which not found.
-	/// \throw DeadlyImportError.
-	void Throw_ID_NotFound(const std::string& pID) const;
-
-	/// Check if current node have children: <node>...</node>. If not then exception will throwed.
-	void XML_CheckNode_MustHaveChildren();
-
-	/// Check if current node name is equal to pNodeName.
-	/// \param [in] pNodeName - name for checking.
-	/// return true if current node name is equal to pNodeName, else - false.
-	bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; }
-
-	/// Skip unsupported node and report about that. Depend on node name can be skipped begin tag of node all whole node.
-	/// \param [in] pParentNodeName - parent node name. Used for reporting.
-	void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName);
-
-	/// Search for specified node in file. XML file read pointer(mReader) will point to found node or file end after search is end.
-	/// \param [in] pNodeName - requested node name.
-	/// return true - if node is found, else - false.
-	bool XML_SearchNode(const std::string& pNodeName);
-
-	/// Read attribute value.
-	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
-	/// \return read data.
-	bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx);
-
-	/// Read attribute value.
-	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
-	/// \return read data.
-	float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx);
-
-	/// Read attribute value.
-	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
-	/// \return read data.
-	uint32_t XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx);
-
-	/// Read node value.
-	/// \return read data.
-	float XML_ReadNode_GetVal_AsFloat();
-
-	/// Read node value.
-	/// \return read data.
-	uint32_t XML_ReadNode_GetVal_AsU32();
-
-	/// Read node value.
-	/// \return read data.
-	void XML_ReadNode_GetVal_AsString(std::string& pValue);
-
-	/// Make pNode as current and enter deeper for parsing child nodes. At end \ref ParseHelper_Node_Exit must be called.
-	/// \param [in] pNode - new current node.
-	void ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode);
-
-	/// This function must be called when exiting from grouping node. \ref ParseHelper_Group_Begin.
-	void ParseHelper_Node_Exit();
-
-	/// Attribute values of floating point types can take form ".x"(without leading zero). irrXMLReader can not read this form of values and it
-	/// must be converted to right form - "0.xxx".
-	/// \param [in] pInStr - pointer to input string which can contain incorrect form of values.
-	/// \param [out[ pOutString - output string with right form of values.
-	void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString);
-
-	/// Decode Base64-encoded data.
-	/// \param [in] pInputBase64 - reference to input Base64-encoded string.
-	/// \param [out] pOutputData - reference to output array for decoded data.
-	void ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const;
-
-	/// Parse <AMF> node of the file.
-	void ParseNode_Root();
-
-	/// Parse <constellation> node of the file.
-	void ParseNode_Constellation();
-
-	/// Parse <instance> node of the file.
-	void ParseNode_Instance();
-
-	/// Parse <material> node of the file.
-	void ParseNode_Material();
-
-	/// Parse <metadata> node.
-	void ParseNode_Metadata();
-
-	/// Parse <object> node of the file.
-	void ParseNode_Object();
-
-	/// Parse <texture> node of the file.
-	void ParseNode_Texture();
-
-	/// Parse <coordinates> node of the file.
-	void ParseNode_Coordinates();
-
-	/// Parse <edge> node of the file.
-	void ParseNode_Edge();
-
-	/// Parse <mesh> node of the file.
-	void ParseNode_Mesh();
-
-	/// Parse <triangle> node of the file.
-	void ParseNode_Triangle();
-
-	/// Parse <vertex> node of the file.
-	void ParseNode_Vertex();
-
-	/// Parse <vertices> node of the file.
-	void ParseNode_Vertices();
-
-	/// Parse <volume> node of the file.
-	void ParseNode_Volume();
-
-	/// Parse <color> node of the file.
-	void ParseNode_Color();
-
-	/// Parse <texmap> of <map> node of the file.
-	/// \param [in] pUseOldName - if true then use old name of node(and children) - <map>, instead of new name - <texmap>.
-	void ParseNode_TexMap(const bool pUseOldName = false);
+    using AMFMetaDataArray = std::vector<AMFMetadata*>;
+    using MeshArray = std::vector<aiMesh*>;
+    using NodeArray = std::vector<aiNode*>;
+
+    /// Clear all temporary data.
+    void Clear();
+
+    /// Get data stored in <vertices> and place it to arrays.
+    /// \param [in] pNodeElement - reference to node element which kept <object> data.
+    /// \param [in] pVertexCoordinateArray - reference to vertices coordinates kept in <vertices>.
+    /// \param [in] pVertexColorArray - reference to vertices colors for all <vertex's. If color for vertex is not set then corresponding member of array
+    /// contain nullptr.
+    void PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElement, std::vector<aiVector3D> &pVertexCoordinateArray,
+            std::vector<AMFColor *> &pVertexColorArray) const;
+
+    /// Return converted texture ID which related to specified source textures ID's. If converted texture does not exist then it will be created and ID on new
+    /// converted texture will be returned. Conversion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it
+    /// to converted textures list.
+    /// Any of source ID's can be absent(empty string) or even one ID only specified. But at least one ID must be specified.
+    /// \param [in] pID_R - ID of source "red" texture.
+    /// \param [in] pID_G - ID of source "green" texture.
+    /// \param [in] pID_B - ID of source "blue" texture.
+    /// \param [in] pID_A - ID of source "alpha" texture.
+    /// \return index of the texture in array of the converted textures.
+    size_t PostprocessHelper_GetTextureID_Or_Create(const std::string &pID_R, const std::string &pID_G, const std::string &pID_B, const std::string &pID_A);
+
+    /// Separate input list by texture IDs. This step is needed because aiMesh can contain mesh which is use only one texture (or set: diffuse, bump etc).
+    /// \param [in] pInputList - input list with faces. Some of them can contain color or texture mapping, or both of them, or nothing. Will be cleared after
+    /// processing.
+    /// \param [out] pOutputList_Separated - output list of the faces lists. Separated faces list by used texture IDs. Will be cleared before processing.
+    void PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace> &pInputList, std::list<std::list<SComplexFace>> &pOutputList_Separated);
+
+    /// Check if child elements of node element is metadata and add it to scene node.
+    /// \param [in] pMetadataList - reference to list with collected metadata.
+    /// \param [out] pSceneNode - scene node in which metadata will be added.
+    void Postprocess_AddMetadata(const AMFMetaDataArray &pMetadataList, aiNode &pSceneNode) const;
+
+    /// To create aiMesh and aiNode for it from <object>.
+    /// \param [in] pNodeElement - reference to node element which kept <object> data.
+    /// \param [out] meshList    - reference to a list with all aiMesh of the scene.
+    /// \param [out] pSceneNode  - pointer to place where new aiNode will be created.
+    void Postprocess_BuildNodeAndObject(const AMFObject &pNodeElement, MeshArray &meshList, aiNode **pSceneNode);
+
+    /// Create mesh for every <volume> in <mesh>.
+    /// \param [in] pNodeElement - reference to node element which kept <mesh> data.
+    /// \param [in] pVertexCoordinateArray - reference to vertices coordinates for all <volume>'s.
+    /// \param [in] pVertexColorArray - reference to vertices colors for all <volume>'s. If color for vertex is not set then corresponding member of array
+    /// contain nullptr.
+    /// \param [in] pObjectColor - pointer to colors for <object>. If color is not set then argument contain nullptr.
+    /// \param [in] pMaterialList - reference to a list with defined materials.
+    /// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
+    /// \param [out] pSceneNode - reference to aiNode which will own new aiMesh's.
+    void Postprocess_BuildMeshSet(const AMFMesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray,
+            const std::vector<AMFColor *> &pVertexColorArray, const AMFColor *pObjectColor,
+            MeshArray &pMeshList, aiNode &pSceneNode);
+
+    /// Convert material from \ref CAMFImporter_NodeElement_Material to \ref SPP_Material.
+    /// \param [in] pMaterial - source CAMFImporter_NodeElement_Material.
+    void Postprocess_BuildMaterial(const AMFMaterial &pMaterial);
+
+    /// Create and add to aiNode's list new part of scene graph defined by <constellation>.
+    /// \param [in] pConstellation - reference to <constellation> node.
+    /// \param [out] nodeArray     - reference to aiNode's list.
+    void Postprocess_BuildConstellation(AMFConstellation &pConstellation, NodeArray &nodeArray) const;
+
+    /// Build Assimp scene graph in aiScene from collected data.
+    /// \param [out] pScene - pointer to aiScene where tree will be built.
+    void Postprocess_BuildScene(aiScene *pScene);
+
+    /// Decode Base64-encoded data.
+    /// \param [in] pInputBase64 - reference to input Base64-encoded string.
+    /// \param [out] pOutputData - reference to output array for decoded data.
+    void ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector<uint8_t> &pOutputData) const;
+
+    /// Parse <AMF> node of the file.
+    void ParseNode_Root();
+
+    /// Parse <constellation> node of the file.
+    void ParseNode_Constellation(XmlNode &node);
+
+    /// Parse <instance> node of the file.
+    void ParseNode_Instance(XmlNode &node);
+
+    /// Parse <material> node of the file.
+    void ParseNode_Material(XmlNode &node);
+
+    /// Parse <metadata> node.
+    void ParseNode_Metadata(XmlNode &node);
+
+    /// Parse <object> node of the file.
+    void ParseNode_Object(XmlNode &node);
+
+    /// Parse <texture> node of the file.
+    void ParseNode_Texture(XmlNode &node);
+
+    /// Parse <coordinates> node of the file.
+    void ParseNode_Coordinates(XmlNode &node);
+
+    /// Parse <edge> node of the file.
+    void ParseNode_Edge(XmlNode &node);
+
+    /// Parse <mesh> node of the file.
+    void ParseNode_Mesh(XmlNode &node);
+
+    /// Parse <triangle> node of the file.
+    void ParseNode_Triangle(XmlNode &node);
+
+    /// Parse <vertex> node of the file.
+    void ParseNode_Vertex(XmlNode &node);
+
+    /// Parse <vertices> node of the file.
+    void ParseNode_Vertices(XmlNode &node);
+
+    /// Parse <volume> node of the file.
+    void ParseNode_Volume(XmlNode &node);
+
+    /// Parse <color> node of the file.
+    void ParseNode_Color(XmlNode &node);
+
+    /// Parse <texmap> of <map> node of the file.
+    /// \param [in] pUseOldName - if true then use old name of node(and children) - <map>, instead of new name - <texmap>.
+    void ParseNode_TexMap(XmlNode &node, const bool pUseOldName = false);
 
 public:
-	/// Default constructor.
-	AMFImporter() AI_NO_EXCEPT
-    : mNodeElement_Cur(nullptr)
-    , mReader(nullptr) {
-        // empty
-    }
-
-	/// Default destructor.
-	~AMFImporter();
-
-	/// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
-	/// Also exception can be thrown if trouble will found.
-	/// \param [in] pFile - name of file to be parsed.
-	/// \param [in] pIOHandler - pointer to IO helper object.
-	void ParseFile(const std::string& pFile, IOSystem* pIOHandler);
-
-	bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const;
-	void GetExtensionList(std::set<std::string>& pExtensionList);
-	void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
-	const aiImporterDesc* GetInfo ()const;
-
-    AMFImporter(const AMFImporter& pScene) = delete;
-    AMFImporter& operator=(const AMFImporter& pScene) = delete;
+    /// Default constructor.
+    AMFImporter() AI_NO_EXCEPT;
+
+    /// Default destructor.
+    ~AMFImporter();
+
+    /// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
+    /// Also exception can be thrown if trouble will found.
+    /// \param [in] pFile - name of file to be parsed.
+    /// \param [in] pIOHandler - pointer to IO helper object.
+    void ParseFile(const std::string &pFile, IOSystem *pIOHandler);
+    void ParseHelper_Node_Enter(AMFNodeElementBase *child);
+    void ParseHelper_Node_Exit();
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const;
+    void GetExtensionList(std::set<std::string> &pExtensionList);
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
+    const aiImporterDesc *GetInfo() const;
+    bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const;
+    bool Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const;
+    bool Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const;
+    void Throw_CloseNotFound(const std::string &nodeName);
+    void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName);
+    void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName);
+    void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription);
+    void Throw_ID_NotFound(const std::string &pID) const;
+    void XML_CheckNode_MustHaveChildren(pugi::xml_node &node);
+    bool XML_SearchNode(const std::string &nodeName);
+    void ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString);
+    AMFImporter(const AMFImporter &pScene) = delete;
+    AMFImporter &operator=(const AMFImporter &pScene) = delete;
 
 private:
     static const aiImporterDesc Description;
 
-    CAMFImporter_NodeElement* mNodeElement_Cur;///< Current element.
-    std::list<CAMFImporter_NodeElement*> mNodeElement_List;///< All elements of scene graph.
-    irr::io::IrrXMLReader* mReader;///< Pointer to XML-reader object
+    AMFNodeElementBase *mNodeElement_Cur; ///< Current element.
+    std::list<AMFNodeElementBase *> mNodeElement_List; ///< All elements of scene graph.
+    XmlParser *mXmlParser;
     std::string mUnit;
-    std::list<SPP_Material> mMaterial_Converted;///< List of converted materials for postprocessing step.
-    std::list<SPP_Texture> mTexture_Converted;///< List of converted textures for postprocessing step.
-
+    std::string mVersion;
+    std::list<SPP_Material> mMaterial_Converted; ///< List of converted materials for postprocessing step.
+    std::list<SPP_Texture> mTexture_Converted; ///< List of converted textures for postprocessing step.
 };
 
-}// namespace Assimp
+} // namespace Assimp
 
 #endif // INCLUDED_AI_AMF_IMPORTER_H

+ 179 - 247
code/AssetLib/AMF/AMFImporter_Geometry.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,
@@ -51,48 +49,47 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "AMFImporter.hpp"
 #include "AMFImporter_Macro.hpp"
 
-namespace Assimp
-{
+#include <assimp/ParsingUtils.h>
+
+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.
+void AMFImporter::ParseNode_Mesh(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // create new mesh object.
+    ne = new AMFMesh(mNodeElement_Cur);
+    // Check for child nodes
+    if (0 != ASSIMP_stricmp(node.name(), "mesh")) {
+        return;
+    }
+    bool found_verts = false, found_volumes = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        pugi::xml_node vertNode = node.child("vertices");
+        if (!vertNode.empty()) {
+            ParseNode_Vertices(vertNode);
+            found_verts = true;
+        }
+
+        pugi::xml_node volumeNode = node.child("volume");
+        if (!volumeNode.empty()) {
+            ParseNode_Volume(volumeNode);
+            found_volumes = true;
+        }
+        ParseHelper_Node_Exit();
+    } 
+
+    if (!found_verts && !found_volumes) {
+        mNodeElement_Cur->Child.push_back(ne);
+    } // if(!mReader->isEmptyElement()) else
+
+    // and to node element list because its a new object in graph.
+    mNodeElement_List.push_back(ne);
 }
 
 // <vertices>
@@ -100,27 +97,25 @@ CAMFImporter_NodeElement* ne;
 // 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.
+void AMFImporter::ParseNode_Vertices(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // create new mesh object.
+    ne = new AMFVertices(mNodeElement_Cur);
+    // Check for child nodes
+    pugi::xml_node vertexNode = node.child("vertex");
+    if (!vertexNode.empty()) {
+        ParseHelper_Node_Enter(ne);
+
+        ParseNode_Vertex(vertexNode);
+
+        ParseHelper_Node_Exit();
+
+    } 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>
@@ -128,52 +123,35 @@ CAMFImporter_NodeElement* ne;
 // 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.
+void AMFImporter::ParseNode_Vertex(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // create new mesh object.
+    ne = new AMFVertex(mNodeElement_Cur);
+
+    // Check for child nodes
+    pugi::xml_node colorNode = node.child("color");
+    bool col_read = false;
+    bool coord_read = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        if (!colorNode.empty()) {
+            ParseNode_Color(colorNode);
+            col_read = true;
+        }
+        pugi::xml_node coordNode = node.child("coordinates");
+        if (!coordNode.empty()) {
+            ParseNode_Coordinates(coordNode);
+            coord_read = true;
+        }
+        ParseHelper_Node_Exit();
+    }
+
+    if (!coord_read && !col_read) {
+        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.
 }
 
 // <coordinates>
@@ -186,37 +164,32 @@ CAMFImporter_NodeElement* ne;
 //   <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.
+void AMFImporter::ParseNode_Coordinates(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // create new color object.
+    ne = new AMFCoordinates(mNodeElement_Cur);
+
+    AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (XmlNode &currentNode : node.children()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == "X") {
+                XmlParser::getValueAsFloat(currentNode, als.Coordinate.x);
+            } else if (currentName == "Y") {
+                XmlParser::getValueAsFloat(currentNode, als.Coordinate.y);
+            } else if (currentName == "Z") {
+                XmlParser::getValueAsFloat(currentNode, als.Coordinate.z);
+            }
+        }
+
+        ParseHelper_Node_Exit();
+    } else {
+        mNodeElement_Cur->Child.push_back(ne);
+    }
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
 }
 
 // <volume
@@ -228,52 +201,41 @@ CAMFImporter_NodeElement* ne;
 // 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.
+void AMFImporter::ParseNode_Volume(XmlNode &node) {
+    std::string materialid;
+    std::string type;
+    AMFNodeElementBase *ne = new AMFVolume(mNodeElement_Cur);
+
+    // Read attributes for node <color>.
+    // and assign read data
+   
+    ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string();
+     
+    ((AMFVolume *)ne)->Type = type;
+    // Check for child nodes
+    bool col_read = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string currentName = currentNode.name();
+            if (currentName == "color") {
+                if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for <volume>.");
+                ParseNode_Color(currentNode);
+                col_read = true;
+            } else if (currentName == "triangle") {
+                ParseNode_Triangle(currentNode);
+            } else if (currentName == "metadata") {
+                ParseNode_Metadata(currentNode);
+            } else if (currentName == "volume") {
+                ParseNode_Metadata(currentNode);
+            }
+        }
+        ParseHelper_Node_Exit();
+    } else {
+        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.
 }
 
 // <triangle>
@@ -286,72 +248,42 @@ CAMFImporter_NodeElement* ne;
 //   <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.
+void AMFImporter::ParseNode_Triangle(XmlNode &node) {
+    AMFNodeElementBase *ne = new AMFTriangle(mNodeElement_Cur);
+
+    // create new triangle object.
+
+    AMFTriangle &als = *((AMFTriangle *)ne); // alias for convenience
+
+    bool col_read = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string currentName = currentNode.name();
+            if (currentName == "color") {
+                if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for <triangle>.");
+                ParseNode_Color(currentNode);
+                col_read = true;
+            } else if (currentName == "texmap") {
+                ParseNode_TexMap(currentNode);
+            } else if (currentName == "map") {
+                ParseNode_TexMap(currentNode, true);
+            } else if (currentName == "v1") {
+                als.V[0] = std::atoi(currentNode.value());
+            } else if (currentName == "v2") {
+                als.V[1] = std::atoi(currentNode.value());
+            } else if (currentName == "v3") {
+                als.V[2] = std::atoi(currentNode.value());
+            }
+        }
+        ParseHelper_Node_Exit();
+    } else {
+        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.
 }
 
-}// namespace Assimp
+} // namespace Assimp
 
 #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

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

@@ -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,

+ 148 - 147
code/AssetLib/AMF/AMFImporter_Material.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,
@@ -49,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
 
 #include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
 
-namespace Assimp
-{
+namespace Assimp {
 
 // <color
 // profile="" - The ICC color space used to interpret the three color channels <r>, <g> and <b>.
@@ -68,46 +64,44 @@ namespace Assimp
 //   Multi elements - No.
 //   Red, Greed, Blue and Alpha (transparency) component of a color in sRGB space, values ranging from 0 to 1. The
 //   values can be specified as constants, or as a formula depending on the coordinates.
-void AMFImporter::ParseNode_Color() {
-    std::string profile;
-    CAMFImporter_NodeElement* ne;
-
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("profile", profile, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
+void AMFImporter::ParseNode_Color(XmlNode &node) {
+    std::string profile = node.attribute("profile").as_string();
+    
 	// create new color object.
-	ne = new CAMFImporter_NodeElement_Color(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Color& als = *((CAMFImporter_NodeElement_Color*)ne);// alias for convenience
+	AMFNodeElementBase *ne = new AMFColor(mNodeElement_Cur);
+	AMFColor& als = *((AMFColor*)ne);// alias for convenience
 
 	als.Profile = profile;
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
+	if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
 		bool read_flag[4] = { false, false, false, false };
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("color");
-			MACRO_NODECHECK_READCOMP_F("r", read_flag[0], als.Color.r);
-			MACRO_NODECHECK_READCOMP_F("g", read_flag[1], als.Color.g);
-			MACRO_NODECHECK_READCOMP_F("b", read_flag[2], als.Color.b);
-			MACRO_NODECHECK_READCOMP_F("a", read_flag[3], als.Color.a);
-		MACRO_NODECHECK_LOOPEND("color");
-		ParseHelper_Node_Exit();
-		// check that all components was defined
-        if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
-            throw DeadlyImportError("Not all color components are defined.");
+		for (pugi::xml_node &child : node.children()) {
+            std::string name = child.name();
+            if ( name == "r") {
+				read_flag[0] = true;
+                XmlParser::getValueAsFloat(child, als.Color.r);
+            } else if (name == "g") {
+				read_flag[1] = true;
+                XmlParser::getValueAsFloat(child, als.Color.g);
+            } else if (name == "b") {
+				read_flag[2] = true;
+                XmlParser::getValueAsFloat(child, als.Color.b);
+            } else if (name == "a") {
+			    read_flag[3] = true;
+                XmlParser::getValueAsFloat(child, als.Color.a);
+            }
+            ParseHelper_Node_Exit();
         }
-
-        // check if <a> is absent. Then manually add "a == 1".
-        if (!read_flag[3]) {
-            als.Color.a = 1;
-        }
-	}
-	else
-	{
+		// check that all components was defined
+		if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
+			throw DeadlyImportError("Not all color components are defined.");
+		}
+
+		// check if <a> is absent. Then manually add "a == 1".
+		if (!read_flag[3]) {
+			als.Color.a = 1;
+		}
+	} else {
 		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
 	}
 
@@ -122,45 +116,25 @@ void AMFImporter::ParseNode_Color() {
 // An available material.
 // Multi elements - Yes.
 // Parent element - <amf>.
-void AMFImporter::ParseNode_Material() {
-    std::string id;
-    CAMFImporter_NodeElement* ne;
-
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new object.
-	ne = new CAMFImporter_NodeElement_Material(mNodeElement_Cur);
-
-    // and assign read data
-	((CAMFImporter_NodeElement_Material*)ne)->ID = id;
+void AMFImporter::ParseNode_Material(XmlNode &node) {
+    // create new object and assign read data
+	std::string id = node.attribute("id").as_string();
+	AMFNodeElementBase *ne = new AMFMaterial(mNodeElement_Cur);
+	((AMFMaterial*)ne)->ID = id;
 
     // Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool col_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("material");
-			if(XML_CheckNode_NameEqual("color"))
-			{
-				// Check if data already defined.
-				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <material>.");
-				// read data and set flag about it
-				ParseNode_Color();
-				col_read = true;
-
-				continue;
+	if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (pugi::xml_node &child : node.children()) {
+            const std::string name = child.name();
+            if (name == "color") {
+				ParseNode_Color(child);
+            } else if (name == "metadata") {
+				ParseNode_Metadata(child);
 			}
-
-			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
-		MACRO_NODECHECK_LOOPEND("material");
-		ParseHelper_Node_Exit();
-	}
-	else
-	{
+		}
+        ParseHelper_Node_Exit();
+	} else {
 		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
 	}
 
@@ -183,51 +157,41 @@ void AMFImporter::ParseNode_Material() {
 // then layer by layer.
 // Multi elements - Yes.
 // Parent element - <amf>.
-void AMFImporter::ParseNode_Texture()
-{
-    std::string id;
-    uint32_t width = 0;
-    uint32_t height = 0;
-    uint32_t depth = 1;
-    std::string type;
-    bool tiled = false;
-    std::string enc64_data;
-
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("width", width, XML_ReadNode_GetAttrVal_AsU32);
-		MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsU32);
-		MACRO_ATTRREAD_CHECK_RET("depth", depth, XML_ReadNode_GetAttrVal_AsU32);
-		MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("tiled", tiled, XML_ReadNode_GetAttrVal_AsBool);
-	MACRO_ATTRREAD_LOOPEND;
+void AMFImporter::ParseNode_Texture(XmlNode &node) {
+    std::string id = node.attribute("id").as_string();
+	uint32_t width = node.attribute("width").as_uint();
+	uint32_t height = node.attribute("height").as_uint();
+	uint32_t depth = node.attribute("depth").as_uint();
+	std::string type = node.attribute("type").as_string();
+	bool tiled = node.attribute("tiled").as_bool();
 
 	// create new texture object.
-    CAMFImporter_NodeElement *ne = new CAMFImporter_NodeElement_Texture(mNodeElement_Cur);
+    AMFNodeElementBase *ne = new AMFTexture(mNodeElement_Cur);
 
-	CAMFImporter_NodeElement_Texture& als = *((CAMFImporter_NodeElement_Texture*)ne);// alias for convenience
+	AMFTexture& als = *((AMFTexture*)ne);// alias for convenience
 
-	// Check for child nodes
-    if (!mReader->isEmptyElement()) {
-        XML_ReadNode_GetVal_AsString(enc64_data);
+    if (node.empty()) {
+		return;
     }
 
+    std::string enc64_data = node.value();
+	// Check for child nodes
+
 	// check that all components was defined
     if (id.empty()) {
-        throw DeadlyImportError("ID for texture must be defined.");
+		throw DeadlyImportError("ID for texture must be defined.");
     }
     if (width < 1) {
-        Throw_IncorrectAttrValue("width");
+		throw DeadlyImportError("INvalid width for texture.");
     }
     if (height < 1) {
-        Throw_IncorrectAttrValue("height");
-    }
+		throw DeadlyImportError("Invalid height for texture.");
+	}
     if (depth < 1) {
-        Throw_IncorrectAttrValue("depth");
+		throw DeadlyImportError("Invalid depth for texture.");
     }
     if (type != "grayscale") {
-        Throw_IncorrectAttrValue("type");
+		throw DeadlyImportError("Invalid type for texture.");
     }
     if (enc64_data.empty()) {
         throw DeadlyImportError("Texture data not defined.");
@@ -263,57 +227,94 @@ void AMFImporter::ParseNode_Texture()
 //   <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>. Old name: <u1>, <u2>, <u3>, <v1>, <v2>, <v3>.
 //   Multi elements - No.
 //   Texture coordinates for every vertex of triangle.
-void AMFImporter::ParseNode_TexMap(const bool pUseOldName) {
-    std::string rtexid, gtexid, btexid, atexid;
-
+void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
 	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("rtexid", rtexid, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("gtexid", gtexid, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("btexid", btexid, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("atexid", atexid, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new texture coordinates object.
-    CAMFImporter_NodeElement *ne = new CAMFImporter_NodeElement_TexMap(mNodeElement_Cur);
+    AMFNodeElementBase *ne = new AMFTexMap(mNodeElement_Cur);
+    AMFTexMap &als = *((AMFTexMap *)ne); //
+    std::string rtexid, gtexid, btexid, atexid;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (XmlNode &currentNode : node.children()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == "rtexid") {
+                XmlParser::getValueAsString(node, rtexid);
+            } else if (currentName == "gtexid") {
+                XmlParser::getValueAsString(node, gtexid);
+            } else if (currentName == "btexid") {
+                XmlParser::getValueAsString(node, btexid);
+            } else if (currentName == "atexid") {
+                XmlParser::getValueAsString(node, atexid);
+            }
+        }
+        ParseHelper_Node_Exit();
+    }
 
-	CAMFImporter_NodeElement_TexMap& als = *((CAMFImporter_NodeElement_TexMap*)ne);// alias for convenience
+	// create new texture coordinates object, alias for convenience
 	// check data
-	if(rtexid.empty() && gtexid.empty() && btexid.empty()) throw DeadlyImportError("ParseNode_TexMap. At least one texture ID must be defined.");
+	if (rtexid.empty() && gtexid.empty() && btexid.empty()) {
+		throw DeadlyImportError("ParseNode_TexMap. At least one texture ID must be defined.");
+	}
+
 	// Check for children nodes
-	XML_CheckNode_MustHaveChildren();
+	//XML_CheckNode_MustHaveChildren();
+	if (node.children().begin() == node.children().end()) {
+		throw DeadlyImportError("Invalid children definition.");
+	}
 	// read children nodes
 	bool read_flag[6] = { false, false, false, false, false, false };
 
-	ParseHelper_Node_Enter(ne);
-	if(!pUseOldName)
-	{
-		MACRO_NODECHECK_LOOPBEGIN("texmap");
-			MACRO_NODECHECK_READCOMP_F("utex1", read_flag[0], als.TextureCoordinate[0].x);
-			MACRO_NODECHECK_READCOMP_F("utex2", read_flag[1], als.TextureCoordinate[1].x);
-			MACRO_NODECHECK_READCOMP_F("utex3", read_flag[2], als.TextureCoordinate[2].x);
-			MACRO_NODECHECK_READCOMP_F("vtex1", read_flag[3], als.TextureCoordinate[0].y);
-			MACRO_NODECHECK_READCOMP_F("vtex2", read_flag[4], als.TextureCoordinate[1].y);
-			MACRO_NODECHECK_READCOMP_F("vtex3", read_flag[5], als.TextureCoordinate[2].y);
-		MACRO_NODECHECK_LOOPEND("texmap");
+	if (!pUseOldName) {
+		for (pugi::xml_attribute &attr : node.attributes()) {
+            const std::string name = attr.name();
+            if (name == "utex1") {
+				read_flag[0] = true;
+				als.TextureCoordinate[0].x = attr.as_float();
+            } else if (name == "utex2") {
+				read_flag[1] = true;
+				als.TextureCoordinate[1].x = attr.as_float();
+            } else if (name == "utex3") {
+				read_flag[2] = true;
+				als.TextureCoordinate[2].x = attr.as_float();
+            } else if (name == "vtex1") {
+				read_flag[3] = true;
+				als.TextureCoordinate[0].y = attr.as_float();
+            } else if (name == "vtex2") {
+				read_flag[4] = true;
+				als.TextureCoordinate[1].y = attr.as_float();
+            } else if (name == "vtex3") {
+				read_flag[5] = true;
+				als.TextureCoordinate[0].y = attr.as_float();
+			}
+		}
+	} else {
+		for (pugi::xml_attribute &attr : node.attributes()) {
+            const std::string name = attr.name();
+            if (name == "u") {
+				read_flag[0] = true;
+				als.TextureCoordinate[0].x = attr.as_float();
+            } else if (name == "u2") {
+				read_flag[1] = true;
+				als.TextureCoordinate[1].x = attr.as_float();
+            } else if (name == "u3") {
+				read_flag[2] = true;
+				als.TextureCoordinate[2].x = attr.as_float();
+            } else if (name == "v1") {
+				read_flag[3] = true;
+				als.TextureCoordinate[0].y = attr.as_float();
+            } else if (name == "v2") {
+				read_flag[4] = true;
+				als.TextureCoordinate[1].y = attr.as_float();
+            } else if (name == "v3") {
+				read_flag[5] = true;
+				als.TextureCoordinate[0].y = attr.as_float();
+			}
+		}
 	}
-	else
-	{
-		MACRO_NODECHECK_LOOPBEGIN("map");
-			MACRO_NODECHECK_READCOMP_F("u1", read_flag[0], als.TextureCoordinate[0].x);
-			MACRO_NODECHECK_READCOMP_F("u2", read_flag[1], als.TextureCoordinate[1].x);
-			MACRO_NODECHECK_READCOMP_F("u3", read_flag[2], als.TextureCoordinate[2].x);
-			MACRO_NODECHECK_READCOMP_F("v1", read_flag[3], als.TextureCoordinate[0].y);
-			MACRO_NODECHECK_READCOMP_F("v2", read_flag[4], als.TextureCoordinate[1].y);
-			MACRO_NODECHECK_READCOMP_F("v3", read_flag[5], als.TextureCoordinate[2].y);
-		MACRO_NODECHECK_LOOPEND("map");
-	}// if(!pUseOldName) else
-
-	ParseHelper_Node_Exit();
 
 	// check that all components was defined
-	if(!(read_flag[0] && read_flag[1] && read_flag[2] && read_flag[3] && read_flag[4] && read_flag[5]))
+	if (!(read_flag[0] && read_flag[1] && read_flag[2] && read_flag[3] && read_flag[4] && read_flag[5])) {
 		throw DeadlyImportError("Not all texture coordinates are defined.");
+	}
 
 	// copy attributes data
 	als.TextureID_R = rtexid;
@@ -321,7 +322,7 @@ void AMFImporter::ParseNode_TexMap(const bool pUseOldName) {
 	als.TextureID_B = btexid;
 	als.TextureID_A = atexid;
 
-	mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
+	mNodeElement_List.push_back(ne);
 }
 
 }// namespace Assimp

+ 116 - 150
code/AssetLib/AMF/AMFImporter_Node.hpp

@@ -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,
@@ -56,80 +54,76 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 
 // Header files, Assimp.
-#include "assimp/types.h"
 #include "assimp/scene.h"
+#include "assimp/types.h"
 
 /// \class CAMFImporter_NodeElement
 /// Base class for elements of nodes.
-class CAMFImporter_NodeElement {
+class AMFNodeElementBase {
 public:
 	/// Define what data type contain node element.
 	enum EType {
-		ENET_Color,        ///< Color element: <color>.
-		ENET_Constellation,///< Grouping element: <constellation>.
-		ENET_Coordinates,  ///< Coordinates element: <coordinates>.
-		ENET_Edge,         ///< Edge element: <edge>.
-		ENET_Instance,     ///< Grouping element: <constellation>.
-		ENET_Material,     ///< Material element: <material>.
-		ENET_Metadata,     ///< Metadata element: <metadata>.
-		ENET_Mesh,         ///< Metadata element: <mesh>.
-		ENET_Object,       ///< Element which hold object: <object>.
-		ENET_Root,         ///< Root element: <amf>.
-		ENET_Triangle,     ///< Triangle element: <triangle>.
-		ENET_TexMap,       ///< Texture coordinates element: <texmap> or <map>.
-		ENET_Texture,      ///< Texture element: <texture>.
-		ENET_Vertex,       ///< Vertex element: <vertex>.
-		ENET_Vertices,     ///< Vertex element: <vertices>.
-		ENET_Volume,       ///< Volume element: <volume>.
-
-		ENET_Invalid       ///< Element has invalid type and possible contain invalid data.
+		ENET_Color, ///< Color element: <color>.
+		ENET_Constellation, ///< Grouping element: <constellation>.
+		ENET_Coordinates, ///< Coordinates element: <coordinates>.
+		ENET_Edge, ///< Edge element: <edge>.
+		ENET_Instance, ///< Grouping element: <constellation>.
+		ENET_Material, ///< Material element: <material>.
+		ENET_Metadata, ///< Metadata element: <metadata>.
+		ENET_Mesh, ///< Metadata element: <mesh>.
+		ENET_Object, ///< Element which hold object: <object>.
+		ENET_Root, ///< Root element: <amf>.
+		ENET_Triangle, ///< Triangle element: <triangle>.
+		ENET_TexMap, ///< Texture coordinates element: <texmap> or <map>.
+		ENET_Texture, ///< Texture element: <texture>.
+		ENET_Vertex, ///< Vertex element: <vertex>.
+		ENET_Vertices, ///< Vertex element: <vertices>.
+		ENET_Volume, ///< Volume element: <volume>.
+
+		ENET_Invalid ///< Element has invalid type and possible contain invalid data.
 	};
 
-	const EType Type;///< Type of element.
-	std::string ID;///< ID of element.
-	CAMFImporter_NodeElement* Parent;///< Parent element. If nullptr then this node is root.
-	std::list<CAMFImporter_NodeElement*> Child;///< Child elements.
+	const EType Type; ///< Type of element.
+	std::string ID; ///< ID of element.
+	AMFNodeElementBase *Parent; ///< Parent element. If nullptr then this node is root.
+	std::list<AMFNodeElementBase *> Child; ///< Child elements.
 
-public:                                               /// Destructor, virtual..
-    virtual ~CAMFImporter_NodeElement() {
-        // empty
-    }
+public: /// Destructor, virtual..
+	virtual ~AMFNodeElementBase() {
+		// empty
+	}
 
 	/// Disabled copy constructor and co.
-	CAMFImporter_NodeElement(const CAMFImporter_NodeElement& pNodeElement) = delete;
-    CAMFImporter_NodeElement(CAMFImporter_NodeElement&&) = delete;
-    CAMFImporter_NodeElement& operator=(const CAMFImporter_NodeElement& pNodeElement) = delete;
-	CAMFImporter_NodeElement() = delete;
+	AMFNodeElementBase(const AMFNodeElementBase &pNodeElement) = delete;
+	AMFNodeElementBase(AMFNodeElementBase &&) = delete;
+	AMFNodeElementBase &operator=(const AMFNodeElementBase &pNodeElement) = delete;
+	AMFNodeElementBase() = delete;
 
 protected:
 	/// In constructor inheritor must set element type.
 	/// \param [in] pType - element type.
 	/// \param [in] pParent - parent element.
-	CAMFImporter_NodeElement(const EType pType, CAMFImporter_NodeElement* pParent)
-	: Type(pType)
-    , ID()
-    , Parent(pParent)
-    , Child() {
-        // empty
-    }
-};// class IAMFImporter_NodeElement
+	AMFNodeElementBase(const EType pType, AMFNodeElementBase *pParent) :
+			Type(pType), ID(), Parent(pParent), Child() {
+		// empty
+	}
+}; // class IAMFImporter_NodeElement
 
 /// \struct CAMFImporter_NodeElement_Constellation
 /// A collection of objects or constellations with specific relative locations.
-struct CAMFImporter_NodeElement_Constellation : public CAMFImporter_NodeElement {
+struct AMFConstellation : public AMFNodeElementBase {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Constellation(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Constellation, pParent)
-	{}
+	AMFConstellation(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Constellation, pParent) {}
 
-};// struct CAMFImporter_NodeElement_Constellation
+}; // struct CAMFImporter_NodeElement_Constellation
 
 /// \struct CAMFImporter_NodeElement_Instance
 /// Part of constellation.
-struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement {
+struct AMFInstance : public AMFNodeElementBase {
 
-	std::string ObjectID;///< ID of object for instantiation.
+	std::string ObjectID; ///< ID of object for instantiation.
 	/// \var Delta - The distance of translation in the x, y, or z direction, respectively, in the referenced object's coordinate system, to
 	/// create an instance of the object in the current constellation.
 	aiVector3D Delta;
@@ -140,201 +134,173 @@ struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement {
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Instance(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Instance, pParent)
-	{}
+	AMFInstance(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Instance, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Metadata
 /// Structure that define metadata node.
-struct CAMFImporter_NodeElement_Metadata : public CAMFImporter_NodeElement {
+struct AMFMetadata : public AMFNodeElementBase {
 
-	std::string Type;///< Type of "Value". 
-	std::string Value;///< Value.
+	std::string Type; ///< Type of "Value".
+	std::string Value; ///< Value.
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Metadata(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Metadata, pParent)
-	{}
+	AMFMetadata(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Metadata, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Root
 /// Structure that define root node.
-struct CAMFImporter_NodeElement_Root : public CAMFImporter_NodeElement {
+struct AMFRoot : public AMFNodeElementBase {
 
-	std::string Unit;///< The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
-	std::string Version;///< Version of format.
+	std::string Unit; ///< The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
+	std::string Version; ///< Version of format.
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Root(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Root, pParent)
-	{}
+	AMFRoot(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Root, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Color
 /// Structure that define object node.
-struct CAMFImporter_NodeElement_Color : public CAMFImporter_NodeElement {
-	bool Composed;                  ///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color.
-	std::string Color_Composed[4];  ///< By components formulas of composed color. [0..3] - RGBA.
-	aiColor4D Color;                ///< Constant color.
-	std::string Profile;            ///< The ICC color space used to interpret the three color channels r, g and b..
+struct AMFColor : public AMFNodeElementBase {
+	bool Composed; ///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color.
+	std::string Color_Composed[4]; ///< By components formulas of composed color. [0..3] - RGBA.
+	aiColor4D Color; ///< Constant color.
+	std::string Profile; ///< The ICC color space used to interpret the three color channels r, g and b..
 
 	/// @brief  Constructor.
 	/// @param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Color(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_Color, pParent)
-    , Composed( false )
-    , Color()
-    , Profile() {
-        // empty
-    }
+	AMFColor(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Color, pParent), Composed(false), Color(), Profile() {
+		// empty
+	}
 };
 
 /// \struct CAMFImporter_NodeElement_Material
 /// Structure that define material node.
-struct CAMFImporter_NodeElement_Material : public CAMFImporter_NodeElement {
-	
+struct AMFMaterial : public AMFNodeElementBase {
+
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Material(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Material, pParent)
-	{}
-
+	AMFMaterial(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Material, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Object
 /// Structure that define object node.
-struct CAMFImporter_NodeElement_Object : public CAMFImporter_NodeElement {
+struct AMFObject : public AMFNodeElementBase {
 
-    /// Constructor.
+	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Object(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Object, pParent)
-	{}
+	AMFObject(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Object, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Mesh
 /// Structure that define mesh node.
-struct CAMFImporter_NodeElement_Mesh : public CAMFImporter_NodeElement {
+struct AMFMesh : public AMFNodeElementBase {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Mesh(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Mesh, pParent)
-	{}
+	AMFMesh(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Mesh, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Vertex
 /// Structure that define vertex node.
-struct CAMFImporter_NodeElement_Vertex : public CAMFImporter_NodeElement {
+struct AMFVertex : public AMFNodeElementBase {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Vertex(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Vertex, pParent)
-	{}
+	AMFVertex(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Vertex, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Edge
 /// Structure that define edge node.
-struct CAMFImporter_NodeElement_Edge : public CAMFImporter_NodeElement {
+struct AMFEdge : public AMFNodeElementBase {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Edge(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Edge, pParent)
-	{}
-
+	AMFEdge(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Edge, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Vertices
 /// Structure that define vertices node.
-struct CAMFImporter_NodeElement_Vertices : public CAMFImporter_NodeElement {
+struct AMFVertices : public AMFNodeElementBase {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Vertices(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Vertices, pParent)
-	{}
+	AMFVertices(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Vertices, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Volume
 /// Structure that define volume node.
-struct CAMFImporter_NodeElement_Volume : public CAMFImporter_NodeElement {
-	std::string MaterialID;///< Which material to use.
-	std::string Type;///< What this volume describes can be “region” or “support”. If none specified, “object” is assumed.
+struct AMFVolume : public AMFNodeElementBase {
+	std::string MaterialID; ///< Which material to use.
+	std::string Type; ///< What this volume describes can be “region” or “support”. If none specified, “object” is assumed.
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Volume(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Volume, pParent)
-	{}
+	AMFVolume(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Volume, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_Coordinates
 /// Structure that define coordinates node.
-struct CAMFImporter_NodeElement_Coordinates : public CAMFImporter_NodeElement
-{
-	aiVector3D Coordinate;///< Coordinate.
+struct AMFCoordinates : public AMFNodeElementBase {
+	aiVector3D Coordinate; ///< Coordinate.
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Coordinates(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Coordinates, pParent)
-	{}
-
+	AMFCoordinates(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Coordinates, pParent) {}
 };
 
 /// \struct CAMFImporter_NodeElement_TexMap
 /// Structure that define texture coordinates node.
-struct CAMFImporter_NodeElement_TexMap : public CAMFImporter_NodeElement {
-	aiVector3D TextureCoordinate[3];///< Texture coordinates.
-	std::string TextureID_R;///< Texture ID for red color component.
-	std::string TextureID_G;///< Texture ID for green color component.
-	std::string TextureID_B;///< Texture ID for blue color component.
-	std::string TextureID_A;///< Texture ID for alpha color component.
+struct AMFTexMap : public AMFNodeElementBase {
+	aiVector3D TextureCoordinate[3]; ///< Texture coordinates.
+	std::string TextureID_R; ///< Texture ID for red color component.
+	std::string TextureID_G; ///< Texture ID for green color component.
+	std::string TextureID_B; ///< Texture ID for blue color component.
+	std::string TextureID_A; ///< Texture ID for alpha color component.
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_TexMap(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_TexMap, pParent)
-    , TextureCoordinate{}
-    , TextureID_R()
-    , TextureID_G()
-    , TextureID_B()
-    , TextureID_A()	{
-        // empty
-    }
+	AMFTexMap(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_TexMap, pParent), TextureCoordinate{}, TextureID_R(), TextureID_G(), TextureID_B(), TextureID_A() {
+		// empty
+	}
 };
 
 /// \struct CAMFImporter_NodeElement_Triangle
 /// Structure that define triangle node.
-struct CAMFImporter_NodeElement_Triangle : public CAMFImporter_NodeElement {
-	size_t V[3];///< Triangle vertices.
+struct AMFTriangle : public AMFNodeElementBase {
+	size_t V[3]; ///< Triangle vertices.
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Triangle(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_Triangle, pParent) {
-        // empty
-    }
+	AMFTriangle(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Triangle, pParent) {
+		// empty
+	}
 };
 
 /// Structure that define texture node.
-struct CAMFImporter_NodeElement_Texture : public CAMFImporter_NodeElement {
-	size_t Width, Height, Depth;///< Size of the texture.
-	std::vector<uint8_t> Data;///< Data of the texture.
+struct AMFTexture : public AMFNodeElementBase {
+	size_t Width, Height, Depth; ///< Size of the texture.
+	std::vector<uint8_t> Data; ///< Data of the texture.
 	bool Tiled;
 
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Texture(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_Texture, pParent)
-    , Width( 0 )
-    , Height( 0 )
-    , Depth( 0 )
-    , Data()
-    , Tiled( false ){
-        // empty
-    }
+	AMFTexture(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Texture, pParent), Width(0), Height(0), Depth(0), Data(), Tiled(false) {
+		// empty
+	}
 };
 
 #endif // INCLUDED_AI_AMF_IMPORTER_NODE_H

+ 176 - 156
code/AssetLib/AMF/AMFImporter_Postprocess.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,12 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #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 {
@@ -83,61 +79,61 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*
     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;
+void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElement, std::vector<aiVector3D> &pVertexCoordinateArray,
+        std::vector<AMFColor *> &pVertexColorArray) const {
+    AMFVertices  *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;
+    for (AMFNodeElementBase *ne_child : pNodeElement.Child) {
+        if (ne_child->Type == AMFNodeElementBase::ENET_Vertices) {
+            vn = (AMFVertices*)ne_child;
+        }
     }
 
     // If "vertices" not found then no work for us.
-    if (vn == nullptr) return;
+    if (vn == nullptr) {
+        return;
+    }
+
+    // all coordinates stored as child and we need to reserve space for future push_back's.
+    pVertexCoordinateArray.reserve(vn->Child.size()); 
 
-    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.
+    // colors count equal vertices count.
+    pVertexColorArray.resize(vn->Child.size()); 
     col_idx = 0;
+
     // Inside vertices collect all data and place to arrays
-    for (CAMFImporter_NodeElement *vn_child : vn->Child) {
+    for (AMFNodeElementBase *vn_child : vn->Child) {
         // vertices, colors
-        if (vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex) {
+        if (vn_child->Type == AMFNodeElementBase::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);
-
+            for (AMFNodeElementBase *vtx : vn_child->Child) {
+                if (vtx->Type == AMFNodeElementBase::ENET_Coordinates) {
+                    pVertexCoordinateArray.push_back(((AMFCoordinates *)vtx)->Coordinate);
                     continue;
                 }
 
-                if (vtx->Type == CAMFImporter_NodeElement::ENET_Color) {
-                    pVertexColorArray[col_idx] = (CAMFImporter_NodeElement_Color *)vtx;
-
+                if (vtx->Type == AMFNodeElementBase::ENET_Color) {
+                    pVertexColorArray[col_idx] = (AMFColor *)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)
+            ++col_idx;
+        }
+    }
 }
 
-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())
+size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &r, const std::string &g, const std::string &b, const std::string &a) {
+    if (r.empty() && g.empty() && b.empty() && 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;
+    std::string TextureConverted_ID = r + "_" + g + "_" + b + "_" + a;
+    size_t TextureConverted_Index = 0;
     for (const SPP_Texture &tex_convd : mTexture_Converted) {
         if (tex_convd.ID == TextureConverted_ID) {
             return TextureConverted_Index;
@@ -146,52 +142,60 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &
         }
     }
 
-    //
     // Converted texture not found, create it.
-    //
-    CAMFImporter_NodeElement_Texture *src_texture[4]{ nullptr };
-    std::vector<CAMFImporter_NodeElement_Texture *> src_texture_4check;
+    AMFTexture *src_texture[4] {
+        nullptr
+    };
+    std::vector<AMFTexture *> src_texture_4check;
     SPP_Texture converted_texture;
 
     { // find all specified source textures
-        CAMFImporter_NodeElement *t_tex;
+        AMFNodeElementBase *t_tex = nullptr;
 
         // R
-        if (!pID_R.empty()) {
-            if (!Find_NodeElement(pID_R, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R);
+        if (!r.empty()) {
+            if (!Find_NodeElement(r, AMFNodeElementBase::EType::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(r);
+            }
 
-            src_texture[0] = (CAMFImporter_NodeElement_Texture *)t_tex;
-            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+            src_texture[0] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)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);
+        if (!g.empty()) {
+            if (!Find_NodeElement(g, AMFNodeElementBase::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(g);
+            }
 
-            src_texture[1] = (CAMFImporter_NodeElement_Texture *)t_tex;
-            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+            src_texture[1] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)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);
+        if (!b.empty()) {
+            if (!Find_NodeElement(b, AMFNodeElementBase::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(b);
+            }
 
-            src_texture[2] = (CAMFImporter_NodeElement_Texture *)t_tex;
-            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+            src_texture[2] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)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);
+        if (!a.empty()) {
+            if (!Find_NodeElement(a, AMFNodeElementBase::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(a);
+            }
 
-            src_texture[3] = (CAMFImporter_NodeElement_Texture *)t_tex;
-            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+            src_texture[3] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)t_tex);
         } else {
             src_texture[3] = nullptr;
         }
@@ -213,38 +217,37 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &
     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++)
+    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';
+    if (!r.empty()) converted_texture.FormatHint[4] = '8';
+    if (!g.empty()) converted_texture.FormatHint[5] = '8';
+    if (!b.empty()) converted_texture.FormatHint[6] = '8';
+    if (!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]) {
+    if (!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]) {
+    if (!g.empty() && nullptr != src_texture[1]) {
         tex_size += src_texture[1]->Data.size();
         step++, off_b++;
     }
-    if (!pID_B.empty() && nullptr != src_texture[2]) {
+    if (!b.empty() && nullptr != src_texture[2]) {
         tex_size += src_texture[2]->Data.size();
         step++;
     }
-    if (!pID_A.empty() && nullptr != src_texture[3]) {
+    if (!a.empty() && nullptr != src_texture[3]) {
         tex_size += src_texture[3]->Data.size();
         step++;
     }
@@ -255,17 +258,17 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &
     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];
+                AMFTexture *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);
+    CopyTextureData(r, 0, step, 0);
+    CopyTextureData(g, off_g, step, 1);
+    CopyTextureData(b, off_b, step, 2);
+    CopyTextureData(a, step - 1, step, 3);
 
     // Store new converted texture ID
     converted_texture.ID = TextureConverted_ID;
@@ -276,7 +279,7 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &
 }
 
 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 {
+    auto texmap_is_equal = [](const AMFTexMap *pTexMap1, const AMFTexMap *pTexMap2) -> bool {
         if ((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true;
         if (pTexMap1 == nullptr) return false;
         if (pTexMap2 == nullptr) return false;
@@ -313,73 +316,80 @@ void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace
     } 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.");
+void AMFImporter::Postprocess_AddMetadata(const AMFMetaDataArray &metadataList, aiNode &sceneNode) const {
+    if (metadataList.empty()) {
+        return;
+    }
+
+    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);
+    // 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())
+    for (const AMFMetadata &metadata : metadataList) {
+        sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata.Type, aiString(metadata.Value));
+    }
 }
 
-void AMFImporter::Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object &pNodeElement, std::list<aiMesh *> &pMeshList, aiNode **pSceneNode) {
-    CAMFImporter_NodeElement_Color *object_color = nullptr;
+void AMFImporter::Postprocess_BuildNodeAndObject(const AMFObject &pNodeElement, MeshArray &meshList, aiNode **pSceneNode) {
+    AMFColor *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) {
+    for (const AMFNodeElementBase *ne_child : pNodeElement.Child) {
         std::vector<aiVector3D> vertex_arr;
-        std::vector<CAMFImporter_NodeElement_Color *> color_arr;
+        std::vector<AMFColor *> color_arr;
 
         // color for object
-        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Color) object_color = (CAMFImporter_NodeElement_Color *)ne_child;
+        if (ne_child->Type == AMFNodeElementBase::ENET_Color) {
+            object_color = (AMFColor *) ne_child;
+        }
 
-        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Mesh) {
+        if (ne_child->Type == AMFNodeElementBase::ENET_Mesh) {
             // Create arrays from children of mesh: vertices.
-            PostprocessHelper_CreateMeshDataArray(*((CAMFImporter_NodeElement_Mesh *)ne_child), vertex_arr, color_arr);
+            PostprocessHelper_CreateMeshDataArray(*((AMFMesh *)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);
+            Postprocess_BuildMeshSet(*((AMFMesh *)ne_child), vertex_arr, color_arr, object_color, meshList, **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) {
+void AMFImporter::Postprocess_BuildMeshSet(const AMFMesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray,
+        const std::vector<AMFColor *> &pVertexColorArray, const AMFColor *pObjectColor, MeshArray &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;
+    for (const AMFNodeElementBase *ne_child : pNodeElement.Child) {
+        const AMFColor *ne_volume_color = nullptr;
         const SPP_Material *cur_mat = nullptr;
 
-        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Volume) {
+        if (ne_child->Type == AMFNodeElementBase::ENET_Volume) {
             /******************* Get faces *******************/
-            const CAMFImporter_NodeElement_Volume *ne_volume = reinterpret_cast<const CAMFImporter_NodeElement_Volume *>(ne_child);
+            const AMFVolume *ne_volume = reinterpret_cast<const AMFVolume *>(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);
+                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) {
+            for (const AMFNodeElementBase *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
+                if (ne_volume_child->Type == AMFNodeElementBase::ENET_Color) {
+                    ne_volume_color = reinterpret_cast<const AMFColor *>(ne_volume_child);
+                } else if (ne_volume_child->Type == AMFNodeElementBase::ENET_Triangle) // triangles, triangles colors
                 {
-                    const CAMFImporter_NodeElement_Triangle &tri_al = *reinterpret_cast<const CAMFImporter_NodeElement_Triangle *>(ne_volume_child);
+                    const AMFTriangle &tri_al = *reinterpret_cast<const AMFTriangle *>(ne_volume_child);
 
                     SComplexFace complex_face;
 
@@ -388,11 +398,11 @@ void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &
                     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);
+                        for (const AMFNodeElementBase *ne_triangle_child : tri_al.Child) {
+                            if (ne_triangle_child->Type == AMFNodeElementBase::ENET_Color)
+                                complex_face.Color = reinterpret_cast<const AMFColor *>(ne_triangle_child);
+                            else if (ne_triangle_child->Type == AMFNodeElementBase::ENET_TexMap)
+                                complex_face.TexMap = reinterpret_cast<const AMFTexMap *>(ne_triangle_child);
                         }
                     } // if(tri_al.Child.size())
 
@@ -422,15 +432,18 @@ void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &
                                 if (face.Face.mIndices[idx_vert] > *pBiggerThan) {
                                     rv = face.Face.mIndices[idx_vert];
                                     found = true;
-
                                     break;
                                 }
                             }
 
-                            if (found) break;
+                            if (found) {
+                                break;
+                            }
                         }
 
-                        if (!found) return *pBiggerThan;
+                        if (!found) {
+                            return *pBiggerThan;
+                        }
                     } else {
                         rv = pFaceList.front().Face.mIndices[0];
                     } // if(pBiggerThan != nullptr) else
@@ -505,9 +518,9 @@ void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &
                 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
+                // Create vertices list and optimize indices. Optimization 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.
+                // Do you need all this thousands of garbage? Of course no. So, optimization step transform 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;
@@ -566,7 +579,7 @@ void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &
                     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.
+                    /// optimization.
                     bool *idx_vert_used;
 
                     idx_vert_used = new bool[VertexCount_Max * 2];
@@ -639,15 +652,15 @@ void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &
     } // if(mesh_idx.size() > 0)
 }
 
-void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material &pMaterial) {
+void AMFImporter::Postprocess_BuildMaterial(const AMFMaterial &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 AMFNodeElementBase *mat_child : pMaterial.Child) {
+        if (mat_child->Type == AMFNodeElementBase::ENET_Color) {
+            new_mat.Color = (AMFColor*)mat_child;
+        } else if (mat_child->Type == AMFNodeElementBase::ENET_Metadata) {
+            new_mat.Metadata.push_back((AMFMetadata *)mat_child);
         }
     } // for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
 
@@ -655,7 +668,7 @@ void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Mater
     mMaterial_Converted.push_back(new_mat);
 }
 
-void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation &pConstellation, std::list<aiNode *> &pNodeList) const {
+void AMFImporter::Postprocess_BuildConstellation(AMFConstellation &pConstellation, NodeArray &nodeArray) const {
     aiNode *con_node;
     std::list<aiNode *> ch_node;
 
@@ -667,18 +680,18 @@ void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Conste
     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) {
+    for (const AMFNodeElementBase *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>.");
+        if (ne->Type == AMFNodeElementBase::ENET_Metadata) continue;
+        if (ne->Type != AMFNodeElementBase::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
 
         // create alias for conveniance
-        CAMFImporter_NodeElement_Instance &als = *((CAMFImporter_NodeElement_Instance *)ne);
+        AMFInstance &als = *((AMFInstance *)ne);
         // find referenced object
-        if (!Find_ConvertedNode(als.ObjectID, pNodeList, &found_node)) Throw_ID_NotFound(als.ObjectID);
+        if (!Find_ConvertedNode(als.ObjectID, nodeArray, &found_node)) Throw_ID_NotFound(als.ObjectID);
 
         // create node for applying transformation
         t_node = new aiNode;
@@ -707,13 +720,13 @@ void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Conste
         con_node->mChildren[ch_idx++] = node;
 
     // and place "root" of <constellation> node to node list
-    pNodeList.push_back(con_node);
+    nodeArray.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;
+    NodeArray nodeArray;
+    MeshArray mesh_list;
+    AMFMetaDataArray meta_list;
 
     //
     // Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
@@ -723,18 +736,21 @@ void AMFImporter::Postprocess_BuildScene(aiScene *pScene) {
     pScene->mRootNode->mParent = nullptr;
     pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
     // search for root(<amf>) element
-    CAMFImporter_NodeElement *root_el = nullptr;
+    AMFNodeElementBase *root_el = nullptr;
 
-    for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
-        if (ne->Type != CAMFImporter_NodeElement::ENET_Root) continue;
+    for (AMFNodeElementBase *ne : mNodeElement_List) {
+        if (ne->Type != AMFNodeElementBase::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.");
+    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
@@ -742,34 +758,38 @@ void AMFImporter::Postprocess_BuildScene(aiScene *pScene) {
     //
     // 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));
+    for (const AMFNodeElementBase *root_child : root_el->Child) {
+        if (root_child->Type == AMFNodeElementBase::ENET_Material) {
+            Postprocess_BuildMaterial(*((AMFMaterial *)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) {
+    for (const AMFNodeElementBase *root_child : root_el->Child) {
+        if (root_child->Type == AMFNodeElementBase::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);
+            Postprocess_BuildNodeAndObject(*((AMFObject *)root_child), mesh_list, &tnode);
+            if (tnode != nullptr) {
+                nodeArray.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) {
+    for (const AMFNodeElementBase *root_child : root_el->Child) {
         // 4. <constellation>
-        if (root_child->Type == CAMFImporter_NodeElement::ENET_Constellation) {
+        if (root_child->Type == AMFNodeElementBase::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);
+            Postprocess_BuildConstellation(*((AMFConstellation *)root_child), nodeArray);
         }
 
         // 5, <metadata>
-        if (root_child->Type == CAMFImporter_NodeElement::ENET_Metadata) meta_list.push_back((CAMFImporter_NodeElement_Metadata *)root_child);
+        if (root_child->Type == AMFNodeElementBase::ENET_Metadata) meta_list.push_back((AMFMetadata *)root_child);
     } // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
 
     // at now we can add collected metadata to root node
@@ -783,17 +803,17 @@ void AMFImporter::Postprocess_BuildScene(aiScene *pScene) {
     // And at this step we are checking that relations.
 nl_clean_loop:
 
-    if (node_list.size() > 1) {
+    if (nodeArray.size() > 1) {
         // walk through all nodes
-        for (std::list<aiNode *>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it) {
+        for (NodeArray::iterator nl_it = nodeArray.begin(); nl_it != nodeArray.end(); ++nl_it) {
             // and try to find them in another top nodes.
-            std::list<aiNode *>::const_iterator next_it = nl_it;
+            NodeArray::const_iterator next_it = nl_it;
 
             ++next_it;
-            for (; next_it != node_list.end(); ++next_it) {
+            for (; next_it != nodeArray.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);
+                    nodeArray.erase(nl_it);
 
                     goto nl_clean_loop;
                 }
@@ -806,10 +826,10 @@ nl_clean_loop:
     //
     //
     // Nodes
-    if (!node_list.empty()) {
-        std::list<aiNode *>::const_iterator nl_it = node_list.begin();
+    if (!nodeArray.empty()) {
+        NodeArray::const_iterator nl_it = nodeArray.begin();
 
-        pScene->mRootNode->mNumChildren = static_cast<unsigned int>(node_list.size());
+        pScene->mRootNode->mNumChildren = static_cast<unsigned int>(nodeArray.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
@@ -822,7 +842,7 @@ nl_clean_loop:
     //
     // Meshes
     if (!mesh_list.empty()) {
-        std::list<aiMesh *>::const_iterator ml_it = mesh_list.begin();
+        MeshArray::const_iterator ml_it = mesh_list.begin();
 
         pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
         pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -83,9 +83,9 @@ class ObjectCache;
  *  ancestry. */
 // -------------------------------------------------------------------------------
 struct Error : DeadlyImportError {
-    Error(const std::string &s) :
-            DeadlyImportError(s) {
-        // empty
+    template <typename... T>
+    explicit Error(T &&...args) :
+            DeadlyImportError(args...) {
     }
 };
 
@@ -186,7 +186,7 @@ struct Field {
 };
 
 // -------------------------------------------------------------------------------
-/** Range of possible behaviours for fields absend in the input file. Some are
+/** Range of possible behaviors for fields absence in the input file. Some are
  *  mission critical so we need them, while others can silently be default
  *  initialized and no animations are harmed. */
 // -------------------------------------------------------------------------------
@@ -394,7 +394,7 @@ private:
 
 // --------------------------------------------------------
 template <>
-struct Structure ::_defaultInitializer<ErrorPolicy_Warn> {
+struct Structure::_defaultInitializer<ErrorPolicy_Warn> {
 
     template <typename T>
     void operator()(T &out, const char *reason = "<add reason>") {
@@ -406,7 +406,7 @@ struct Structure ::_defaultInitializer<ErrorPolicy_Warn> {
 };
 
 template <>
-struct Structure ::_defaultInitializer<ErrorPolicy_Fail> {
+struct Structure::_defaultInitializer<ErrorPolicy_Fail> {
 
     template <typename T>
     void operator()(T & /*out*/, const char * = "") {

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

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

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

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

+ 5 - 38
code/AssetLib/Blender/BlenderLoader.h

@@ -106,51 +106,18 @@ class BlenderImporter : public BaseImporter, public LogFunctions<BlenderImporter
 public:
     BlenderImporter();
     ~BlenderImporter();
-
-public:
-
-    // --------------------
-    bool CanRead( const std::string& pFile,
-        IOSystem* pIOHandler,
-        bool checkSig
-    ) const;
+    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
 
 protected:
-
-    // --------------------
     const aiImporterDesc* GetInfo () const;
-
-    // --------------------
     void GetExtensionList(std::set<std::string>& app);
-
-    // --------------------
     void SetupProperties(const Importer* pImp);
-
-    // --------------------
-    void InternReadFile( const std::string& pFile,
-        aiScene* pScene,
-        IOSystem* pIOHandler
-    );
-
-    // --------------------
-    void ParseBlendFile(Blender::FileDatabase& out,
-        std::shared_ptr<IOStream> stream
-    );
-
-    // --------------------
-    void ExtractScene(Blender::Scene& out,
-        const Blender::FileDatabase& file
-    );
-
-    // --------------------
-    void ConvertBlendFile(aiScene* out,
-        const Blender::Scene& in,
-        const Blender::FileDatabase& file
-    );
+    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+    void ParseBlendFile(Blender::FileDatabase& out, std::shared_ptr<IOStream> stream);
+    void ExtractScene(Blender::Scene& out, const Blender::FileDatabase& file);
+    void ConvertBlendFile(aiScene* out, const Blender::Scene& in, const Blender::FileDatabase& file);
 
 private:
-
-    // --------------------
     aiNode* ConvertNode(const Blender::Scene& in,
         const Blender::Object* obj,
         Blender::ConversionData& conv_info,

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

@@ -155,7 +155,7 @@ 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; // read as short and divided through / 32767.f
     char flag;
     int mat_nr WARN;
     int bweight;
@@ -228,7 +228,10 @@ struct TFace : ElemBase {
 // -------------------------------------------------------------------------------
 struct MTFace : ElemBase {
     MTFace() :
-            flag(0), mode(0), tile(0), unwrap(0) {
+            flag(0),
+            mode(0),
+            tile(0),
+            unwrap(0) {
     }
 
     float uv[4][2] FAIL;

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

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

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

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

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

@@ -573,7 +573,7 @@ bool ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial &
             index_str = index_str.substr(1, std::string::npos);
 
             try {
-                index = (unsigned int)strtoul10_64(index_str.c_str());
+                index = (unsigned int)strtoul10_64<DeadlyExportError>(index_str.c_str());
             } catch (std::exception &error) {
                 throw DeadlyExportError(error.what());
             }

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

@@ -1,5 +1,3 @@
-/** Helper structures for the Collada loader */
-
 /*
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
@@ -40,6 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ----------------------------------------------------------------------
 */
+/** Helper structures for the Collada loader */
 
 #include "ColladaHelper.h"
 

+ 109 - 119
code/AssetLib/Collada/ColladaHelper.h

@@ -1,12 +1,9 @@
-/** Helper structures for the Collada loader */
-
 /*
 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,
@@ -42,12 +39,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
+/** Helper structures for the Collada loader */
+
 #ifndef AI_COLLADAHELPER_H_INC
 #define AI_COLLADAHELPER_H_INC
 
 #include <assimp/light.h>
 #include <assimp/material.h>
 #include <assimp/mesh.h>
+
 #include <stdint.h>
 #include <map>
 #include <set>
@@ -58,14 +58,14 @@ struct aiMaterial;
 namespace Assimp {
 namespace Collada {
 
-/** Collada file versions which evolved during the years ... */
+/// Collada file versions which evolved during the years ...
 enum FormatVersion {
     FV_1_5_n,
     FV_1_4_n,
     FV_1_3_n
 };
 
-/** Transformation types that can be applied to a node */
+/// Transformation types that can be applied to a node
 enum TransformType {
     TF_LOOKAT,
     TF_ROTATE,
@@ -75,7 +75,7 @@ enum TransformType {
     TF_MATRIX
 };
 
-/** Different types of input data to a vertex or face */
+/// Different types of input data to a vertex or face
 enum InputType {
     IT_Invalid,
     IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
@@ -87,38 +87,39 @@ enum InputType {
     IT_Bitangent
 };
 
-/** Supported controller types */
+/// Supported controller types
 enum ControllerType {
     Skin,
     Morph
 };
 
-/** Supported morph methods */
+/// Supported morph methods
 enum MorphMethod {
     Normalized,
     Relative
 };
 
-/** Common metadata keys as <Collada, Assimp> */
-typedef std::pair<std::string, std::string> MetaKeyPair;
-typedef std::vector<MetaKeyPair> MetaKeyPairVector;
+/// Common metadata keys as <Collada, Assimp>
+using MetaKeyPair = std::pair<std::string, std::string>;
+using MetaKeyPairVector = std::vector<MetaKeyPair>;
 
-// Collada as lower_case (native)
+/// Collada as lower_case (native)
 const MetaKeyPairVector &GetColladaAssimpMetaKeys();
+
 // Collada as CamelCase (used by Assimp for consistency)
 const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase();
 
-/** Convert underscore_separated to CamelCase "authoring_tool" becomes "AuthoringTool" */
+/// Convert underscore_separated to CamelCase "authoring_tool" becomes "AuthoringTool"
 void ToCamelCase(std::string &text);
 
-/** Contains all data for one of the different transformation types */
+/// 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
     TransformType mType;
     ai_real f[16]; ///< Interpretation of data depends on the type of the transformation
 };
 
-/** A collada camera. */
+/// A collada camera.
 struct Camera {
     Camera() :
             mOrtho(false),
@@ -128,22 +129,22 @@ struct Camera {
             mZNear(0.1f),
             mZFar(1000.f) {}
 
-    // Name of camera
+    /// Name of camera
     std::string mName;
 
-    // True if it is an orthografic camera
+    /// True if it is an orthographic camera
     bool mOrtho;
 
-    //! Horizontal field of view in degrees
+    /// Horizontal field of view in degrees
     ai_real mHorFov;
 
-    //! Vertical field of view in degrees
+    /// Vertical field of view in degrees
     ai_real mVerFov;
 
-    //! Screen aspect
+    /// Screen aspect
     ai_real mAspect;
 
-    //! Near& far z
+    /// Near& far z
     ai_real mZNear, mZFar;
 };
 
@@ -162,27 +163,27 @@ struct Light {
             mOuterAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET),
             mIntensity(1.f) {}
 
-    //! Type of the light source aiLightSourceType + ambient
+    /// Type of the light source aiLightSourceType + ambient
     unsigned int mType;
 
-    //! Color of the light
+    /// Color of the light
     aiColor3D mColor;
 
-    //! Light attenuation
+    /// Light attenuation
     ai_real mAttConstant, mAttLinear, mAttQuadratic;
 
-    //! Spot light falloff
+    /// Spot light falloff
     ai_real mFalloffAngle;
     ai_real mFalloffExponent;
 
     // -----------------------------------------------------
     // FCOLLADA extension from here
 
-    //! ... related stuff from maja and max extensions
+    /// ... related stuff from maja and max extensions
     ai_real mPenumbraAngle;
     ai_real mOuterAngle;
 
-    //! Common light intensity
+    /// Common light intensity
     ai_real mIntensity;
 };
 
@@ -192,30 +193,29 @@ struct InputSemanticMapEntry {
             mSet(0),
             mType(IT_Invalid) {}
 
-    //! Index of set, optional
+    /// Index of set, optional
     unsigned int mSet;
 
-    //! Type of referenced vertex input
+    /// Type of referenced vertex input
     InputType mType;
 };
 
-/** Table to map from effect to vertex input semantics */
+/// Table to map from effect to vertex input semantics
 struct SemanticMappingTable {
-    //! Name of material
+    /// Name of material
     std::string mMatName;
 
-    //! List of semantic map commands, grouped by effect semantic name
+    /// List of semantic map commands, grouped by effect semantic name
     std::map<std::string, InputSemanticMapEntry> mMap;
 
-    //! For std::find
+    /// For std::find
     bool operator==(const std::string &s) const {
         return s == mMatName;
     }
 };
 
-/** 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
- */
+/// 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 {
     ///< ID of the mesh or controller to be instanced
     std::string mMeshOrController;
@@ -224,25 +224,25 @@ struct MeshInstance {
     std::map<std::string, SemanticMappingTable> mMaterials;
 };
 
-/** A reference to a camera inside a node*/
+/// A reference to a camera inside a node
 struct CameraInstance {
     ///< ID of the camera
     std::string mCamera;
 };
 
-/** A reference to a light inside a node*/
+/// A reference to a light inside a node
 struct LightInstance {
     ///< ID of the camera
     std::string mLight;
 };
 
-/** A reference to a node inside a node*/
+/// A reference to a node inside a node
 struct NodeInstance {
     ///< ID of the node
     std::string mNode;
 };
 
-/** A node in a scene hierarchy */
+/// A node in a scene hierarchy
 struct Node {
     std::string mName;
     std::string mID;
@@ -250,52 +250,53 @@ struct Node {
     Node *mParent;
     std::vector<Node *> mChildren;
 
-    /** Operations in order to calculate the resulting transformation to parent. */
+    /// Operations in order to calculate the resulting transformation to parent.
     std::vector<Transform> mTransforms;
 
-    /** Meshes at this node */
+    /// Meshes at this node
     std::vector<MeshInstance> mMeshes;
 
-    /** Lights at this node */
+    /// Lights at this node
     std::vector<LightInstance> mLights;
 
-    /** Cameras at this node */
+    /// Cameras at this node
     std::vector<CameraInstance> mCameras;
 
-    /** Node instances at this node */
+    /// Node instances at this node
     std::vector<NodeInstance> mNodeInstances;
 
-    /** Root-nodes: Name of primary camera, if any */
+    /// Root-nodes: Name of primary camera, if any
     std::string mPrimaryCamera;
 
-    //! Constructor. Begin with a zero parent
+    /// Constructor. Begin with a zero parent
     Node() :
             mParent(nullptr) {
         // empty
     }
 
-    //! Destructor: delete all children subsequently
+    /// 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 */
+/// Data source array: either floats or strings
 struct Data {
     bool mIsStringArray;
     std::vector<ai_real> mValues;
     std::vector<std::string> mStrings;
 };
 
-/** Accessor to a data array */
+/// 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
     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.
+    size_t mSubOffset[4]; // Sub-offset 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. nullptr else
@@ -310,12 +311,12 @@ struct Accessor {
     }
 };
 
-/** A single face in a mesh */
+/// A single face in a mesh
 struct Face {
     std::vector<size_t> mIndices;
 };
 
-/** An input channel for mesh data, referring to a single accessor */
+/// 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
@@ -331,18 +332,19 @@ struct InputChannel {
     }
 };
 
-/** Subset of a mesh with a certain material */
+/// Subset of a mesh with a certain material
 struct SubMesh {
     std::string mMaterial; ///< subgroup identifier
-    size_t mNumFaces; ///< number of faces in this submesh
+    size_t mNumFaces; ///< number of faces in this sub-mesh
 };
 
-/** Contains data for a single mesh */
+/// Contains data for a single mesh
 struct Mesh {
     Mesh(const std::string &id) :
             mId(id) {
-        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i)
+        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
             mNumUVComponents[i] = 2;
+        }
     }
 
     const std::string mId;
@@ -373,11 +375,11 @@ struct Mesh {
     // necessary for bone weight assignment
     std::vector<size_t> mFacePosIndices;
 
-    // Submeshes in this mesh, each with a given material
+    // Sub-meshes in this mesh, each with a given material
     std::vector<SubMesh> mSubMeshes;
 };
 
-/** Which type of primitives the ReadPrimitives() function is going to read */
+/// Which type of primitives the ReadPrimitives() function is going to read
 enum PrimitiveType {
     Prim_Invalid,
     Prim_Lines,
@@ -389,7 +391,7 @@ enum PrimitiveType {
     Prim_Polygon
 };
 
-/** A skeleton controller to deform a mesh with the use of joints */
+/// A skeleton controller to deform a mesh with the use of joints
 struct Controller {
     // controller type
     ControllerType mType;
@@ -424,25 +426,25 @@ struct Controller {
     std::string mMorphWeight;
 };
 
-/** A collada material. Pretty much the only member is a reference to an effect. */
+/// A collada material. Pretty much the only member is a reference to an effect.
 struct Material {
     std::string mName;
     std::string mEffect;
 };
 
-/** Type of the effect param */
+/// Type of the effect param
 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 */
+/// A param for an effect. Might be of several types, but they all just refer to each other, so I summarize them
 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 */
+/// Shading type supported by the standard effect spec of Collada
 enum ShadeType {
     Shade_Invalid,
     Shade_Constant,
@@ -451,7 +453,7 @@ enum ShadeType {
     Shade_Blinn
 };
 
-/** Represents a texture sampler in collada */
+/// Represents a texture sampler in collada
 struct Sampler {
     Sampler() :
             mWrapU(true),
@@ -463,77 +465,66 @@ struct Sampler {
             mWeighting(1.f),
             mMixWithPrevious(1.f) {}
 
-    /** Name of image reference
-     */
+    /// Name of image reference
     std::string mName;
 
-    /** Wrap U?
-     */
+    /// Wrap U?
     bool mWrapU;
 
-    /** Wrap V?
-     */
+    /// Wrap V?
     bool mWrapV;
 
-    /** Mirror U?
-     */
+    /// Mirror U?
     bool mMirrorU;
 
-    /** Mirror V?
-     */
+    /// Mirror V?
     bool mMirrorV;
 
-    /** Blend mode
-     */
+    /// Blend mode
     aiTextureOp mOp;
 
-    /** UV transformation
-     */
+    /// UV transformation
     aiUVTransform mTransform;
 
-    /** Name of source UV channel
-     */
+    /// Name of source UV channel
     std::string mUVChannel;
 
-    /** Resolved UV channel index or UINT_MAX if not known
-     */
+    /// Resolved UV channel index or UINT_MAX if not known
     unsigned int mUVId;
 
     // OKINO/MAX3D extensions from here
     // -------------------------------------------------------
 
-    /** Weighting factor
-     */
+    /// Weighting factor
     ai_real mWeighting;
 
-    /** Mixing factor from OKINO
-     */
+    /// Mixing factor from OKINO
     ai_real mMixWithPrevious;
 };
 
-/** A collada effect. Can contain about anything according to the Collada spec,
-    but we limit our version to a reasonable subset. */
+/// A collada effect. Can contain about anything according to the Collada spec,
+/// but we limit our version to a reasonable subset.
 struct Effect {
-    // Shading mode
+    /// Shading mode
     ShadeType mShadeType;
 
-    // Colors
+    /// Colors
     aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular,
             mTransparent, mReflective;
 
-    // Textures
+    /// Textures
     Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular,
             mTexTransparent, mTexBump, mTexReflective;
 
-    // Scalar factory
+    /// Scalar factory
     ai_real mShininess, mRefractIndex, mReflectivity;
     ai_real mTransparency;
     bool mHasTransparency;
     bool mRGBTransparency;
     bool mInvertTransparency;
 
-    // local params referring to each other by their SID
-    typedef std::map<std::string, Collada::EffectParam> ParamLibrary;
+    /// local params referring to each other by their SID
+    using ParamLibrary = std::map<std::string, Collada::EffectParam>;
     ParamLibrary mParams;
 
     // MAX3D extensions
@@ -561,65 +552,64 @@ struct Effect {
     }
 };
 
-/** An image, meaning texture */
+/// An image, meaning texture
 struct Image {
     std::string mFileName;
 
-    /** Embedded image data */
+    /// Embedded image data
     std::vector<uint8_t> mImageData;
 
-    /** File format hint of embedded image data */
+    /// File format hint of embedded image data
     std::string mEmbeddedFormat;
 };
 
-/** An animation channel. */
+/// An animation channel.
 struct AnimationChannel {
-    /** URL of the data to animate. Could be about anything, but we support only the
-     * "NodeID/TransformID.SubElement" notation
-     */
+    /// URL of the data to animate. Could be about anything, but we support only the
+    /// "NodeID/TransformID.SubElement" notation
     std::string mTarget;
 
-    /** Source URL of the time values. Collada calls them "input". Meh. */
+    /// Source URL of the time values. Collada calls them "input". Meh.
     std::string mSourceTimes;
-    /** Source URL of the value values. Collada calls them "output". */
+    /// Source URL of the value values. Collada calls them "output".
     std::string mSourceValues;
-    /** Source URL of the IN_TANGENT semantic values. */
+    /// Source URL of the IN_TANGENT semantic values.
     std::string mInTanValues;
-    /** Source URL of the OUT_TANGENT semantic values. */
+    /// Source URL of the OUT_TANGENT semantic values.
     std::string mOutTanValues;
-    /** Source URL of the INTERPOLATION semantic values. */
+    /// Source URL of the INTERPOLATION semantic values.
     std::string mInterpolationValues;
 };
 
-/** An animation. Container for 0-x animation channels or 0-x animations */
+/// An animation. Container for 0-x animation channels or 0-x animations
 struct Animation {
-    /** Anim name */
+    /// Anim name
     std::string mName;
 
-    /** the animation channels, if any */
+    /// the animation channels, if any
     std::vector<AnimationChannel> mChannels;
 
-    /** the sub-animations, if any */
+    /// the sub-animations, if any
     std::vector<Animation *> mSubAnims;
 
-    /** Destructor */
+    /// Destructor
     ~Animation() {
-        for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it)
+        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. */
+    /// 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);
-
             pAnim->CollectChannelsRecursively(channels);
         }
     }
 
-    /** Combine all single-channel animations' channel into the same (parent) animation channel list. */
+    /// Combine all single-channel animations' channel into the same (parent) animation channel list.
     void CombineSingleChannelAnimations() {
         CombineSingleChannelAnimationsRecursively(this);
     }
@@ -658,9 +648,9 @@ struct Animation {
     }
 };
 
-/** Description of a collada animation channel which has been determined to affect the current node */
+/// Description of a collada animation channel which has been determined to affect the current node
 struct ChannelEntry {
-    const Collada::AnimationChannel *mChannel; ///> the source channel
+    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
     size_t mTransformIndex; // Index into the node's transform chain to apply the channel to

+ 49 - 52
code/AssetLib/Collada/ColladaLoader.cpp

@@ -45,25 +45,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "ColladaLoader.h"
 #include "ColladaParser.h"
-
 #include <assimp/ColladaMetaData.h>
+#include <assimp/CreateAnimMesh.h>
 #include <assimp/Defines.h>
+#include <assimp/ParsingUtils.h>
+#include <assimp/SkeletonMeshBuilder.h>
+#include <assimp/ZipArchiveIOSystem.h>
 #include <assimp/anim.h>
+#include <assimp/fast_atof.h>
 #include <assimp/importerdesc.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Importer.hpp>
 
-#include <assimp/CreateAnimMesh.h>
-#include <assimp/ParsingUtils.h>
-#include <assimp/SkeletonMeshBuilder.h>
-#include <assimp/ZipArchiveIOSystem.h>
-#include <assimp/fast_atof.h>
-
-#include "math.h"
-#include "time.h"
-#include <algorithm>
-#include <memory>
 #include <numeric>
 
 namespace Assimp {
@@ -125,20 +119,17 @@ ColladaLoader::~ColladaLoader() {
 bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
     // check file extension
     const std::string extension = GetExtension(pFile);
-
-    bool readSig = checkSig && (pIOHandler != nullptr);
-
+    const bool readSig = checkSig && (pIOHandler != nullptr);
     if (!readSig) {
         if (extension == "dae" || extension == "zae") {
             return true;
         }
-    }
-
-    if (readSig) {
+    } else {
         // Look for a DAE file inside, but don't extract it
         ZipArchiveIOSystem zip_archive(pIOHandler, pFile);
-        if (zip_archive.isOpen())
+        if (zip_archive.isOpen()) {
             return !ColladaParser::ReadZaeManifest(zip_archive).empty();
+        }
     }
 
     // XML - too generic, we need to open the file and search for typical keywords
@@ -337,13 +328,15 @@ void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Col
 // Resolve UV channels
 void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler, const Collada::SemanticMappingTable &table) {
     std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
-    if (it != table.mMap.end()) {
-        if (it->second.mType != Collada::IT_Texcoord) {
-            ASSIMP_LOG_ERROR("Collada: Unexpected effect input mapping");
-        }
+    if (it == table.mMap.end()) {
+        return;
+    }
 
-        sampler.mUVId = it->second.mSet;
+    if (it->second.mType != Collada::IT_Texcoord) {
+        ASSIMP_LOG_ERROR("Collada: Unexpected effect input mapping");
     }
+
+    sampler.mUVId = it->second.mSet;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -390,7 +383,11 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Colla
                 if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - 1e-6f)) {
                     // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
                     // epsilon chosen to be 0.1
-                    out->mAngleOuterCone = std::acos(std::pow(0.1f, 1.f / srcLight->mFalloffExponent)) +
+                    float f = 1.0f;
+                    if ( 0.0f != srcLight->mFalloffExponent ) {
+                        f = 1.f / srcLight->mFalloffExponent;
+                    }
+                    out->mAngleOuterCone = std::acos(std::pow(0.1f, f)) +
                                            out->mAngleInnerCone;
                 } else {
                     out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD(srcLight->mPenumbraAngle);
@@ -585,10 +582,10 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Colla
 // ------------------------------------------------------------------------------------------------
 // Find mesh from either meshes or morph target meshes
 aiMesh *ColladaLoader::findMesh(const std::string &meshid) {
-    if ( meshid.empty()) {
+    if (meshid.empty()) {
         return nullptr;
     }
-    
+
     for (unsigned int i = 0; i < mMeshes.size(); ++i) {
         if (std::string(mMeshes[i]->mName.data) == meshid) {
             return mMeshes[i];
@@ -1251,7 +1248,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
 
             // time count and value count must match
             if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
-                throw DeadlyImportError(format() << "Time count / value count mismatch in animation channel \"" << e.mChannel->mTarget << "\".");
+                throw DeadlyImportError("Time count / value count mismatch in animation channel \"", e.mChannel->mTarget, "\".");
 
             if (e.mTimeAccessor->mCount > 0) {
                 // find bounding times
@@ -1377,9 +1374,9 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
                 double time = double(mat.d4); // remember? time is stored in mat.d4
                 mat.d4 = 1.0f;
 
-                dstAnim->mPositionKeys[a].mTime = time * kMillisecondsFromSeconds ;
-                dstAnim->mRotationKeys[a].mTime = time * kMillisecondsFromSeconds ;
-                dstAnim->mScalingKeys[a].mTime = time * kMillisecondsFromSeconds ;
+                dstAnim->mPositionKeys[a].mTime = time * kMillisecondsFromSeconds;
+                dstAnim->mRotationKeys[a].mTime = time * kMillisecondsFromSeconds;
+                dstAnim->mScalingKeys[a].mTime = time * kMillisecondsFromSeconds;
                 mat.Decompose(dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
             }
 
@@ -1400,7 +1397,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
                 if (e.mTargetId.find("morph-weights") != std::string::npos)
                     morphChannels.push_back(e);
             }
-            if (!morphChannels.empty() ) {
+            if (!morphChannels.empty()) {
                 // either 1) morph weight animation count should contain morph target count channels
                 // or     2) one channel with morph target count arrays
                 // assume first
@@ -1434,8 +1431,8 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
                     morphAnim->mKeys[key].mValues = new unsigned int[morphChannels.size()];
                     morphAnim->mKeys[key].mWeights = new double[morphChannels.size()];
 
-                    morphAnim->mKeys[key].mTime = morphTimeValues[key].mTime * kMillisecondsFromSeconds ;
-                    for (unsigned int valueIndex = 0; valueIndex < morphChannels.size(); ++valueIndex ) {
+                    morphAnim->mKeys[key].mTime = morphTimeValues[key].mTime * kMillisecondsFromSeconds;
+                    for (unsigned int valueIndex = 0; valueIndex < morphChannels.size(); ++valueIndex) {
                         morphAnim->mKeys[key].mValues[valueIndex] = valueIndex;
                         morphAnim->mKeys[key].mWeights[valueIndex] = getWeightAtKey(morphTimeValues, key, valueIndex);
                     }
@@ -1468,7 +1465,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
         for (size_t a = 0; a < morphAnims.size(); ++a) {
             anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys - 1].mTime);
         }
-        anim->mTicksPerSecond = 1;
+        anim->mTicksPerSecond = 1000.0;
         mAnims.push_back(anim);
     }
 }
@@ -1552,23 +1549,23 @@ void ColladaLoader::FillMaterials(const ColladaParser &pParser, aiScene * /*pSce
             shadeMode = aiShadingMode_Flat;
         } else {
             switch (effect.mShadeType) {
-                case Collada::Shade_Constant:
-                    shadeMode = aiShadingMode_NoShading;
-                    break;
-                case Collada::Shade_Lambert:
-                    shadeMode = aiShadingMode_Gouraud;
-                    break;
-                case Collada::Shade_Blinn:
-                    shadeMode = aiShadingMode_Blinn;
-                    break;
-                case Collada::Shade_Phong:
-                    shadeMode = aiShadingMode_Phong;
-                    break;
+            case Collada::Shade_Constant:
+                shadeMode = aiShadingMode_NoShading;
+                break;
+            case Collada::Shade_Lambert:
+                shadeMode = aiShadingMode_Gouraud;
+                break;
+            case Collada::Shade_Blinn:
+                shadeMode = aiShadingMode_Blinn;
+                break;
+            case Collada::Shade_Phong:
+                shadeMode = aiShadingMode_Phong;
+                break;
 
-                default:
-                    ASSIMP_LOG_WARN("Collada: Unrecognized shading mode, using gouraud shading");
-                    shadeMode = aiShadingMode_Gouraud;
-                    break;
+            default:
+                ASSIMP_LOG_WARN("Collada: Unrecognized shading mode, using gouraud shading");
+                shadeMode = aiShadingMode_Gouraud;
+                break;
             }
         }
         mat.AddProperty<int>(&shadeMode, 1, AI_MATKEY_SHADING_MODEL);
@@ -1658,7 +1655,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/)
         const Collada::Material &material = matIt->second;
         // a material is only a reference to an effect
         ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect);
-        if (effIt == pParser.mEffectLibrary.end())
+        if (effIt == pParser.mEffectLibrary.end())  
             continue;
         Collada::Effect &effect = effIt->second;
 
@@ -1734,7 +1731,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse
         // and add this texture to the list
         mTextures.push_back(tex);
         return result;
-    } 
+    }
 
     if (imIt->second.mFileName.empty()) {
         throw DeadlyImportError("Collada: Invalid texture, no data or file reference given");

File diff suppressed because it is too large
+ 890 - 1320
code/AssetLib/Collada/ColladaParser.cpp


+ 66 - 111
code/AssetLib/Collada/ColladaParser.h

@@ -4,7 +4,6 @@
 
  Copyright (c) 2006-2020, assimp team
 
-
  All rights reserved.
 
  Redistribution and use of this software in source and binary forms,
@@ -50,9 +49,12 @@
 #include "ColladaHelper.h"
 #include <assimp/TinyFormatter.h>
 #include <assimp/ai_assert.h>
-#include <assimp/irrXMLWrapper.h>
+#include <assimp/XmlParser.h>
+
+#include <map>
 
 namespace Assimp {
+
 class ZipArchiveIOSystem;
 
 // ------------------------------------------------------------------------------------------
@@ -81,25 +83,25 @@ protected:
     static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
 
     /** Reads the contents of the file */
-    void ReadContents();
+    void ReadContents(XmlNode &node);
 
     /** Reads the structure of the file */
-    void ReadStructure();
+    void ReadStructure(XmlNode &node);
 
     /** Reads asset information such as coordinate system information and legal blah */
-    void ReadAssetInfo();
+    void ReadAssetInfo(XmlNode &node);
 
     /** Reads contributor information such as author and legal blah */
-    void ReadContributorInfo();
+    void ReadContributorInfo(XmlNode &node);
 
     /** Reads generic metadata into provided map and renames keys for Assimp */
-    void ReadMetaDataItem(StringMetaData &metadata);
+    void ReadMetaDataItem(XmlNode &node, StringMetaData &metadata);
 
     /** Reads the animation library */
-    void ReadAnimationLibrary();
+    void ReadAnimationLibrary(XmlNode &node);
 
     /** Reads the animation clip library */
-    void ReadAnimationClipLibrary();
+    void ReadAnimationClipLibrary(XmlNode &node);
 
     /** Unwrap controllers dependency hierarchy */
     void PostProcessControllers();
@@ -108,103 +110,103 @@ protected:
     void PostProcessRootAnimations();
 
     /** Reads an animation into the given parent structure */
-    void ReadAnimation(Collada::Animation *pParent);
+    void ReadAnimation(XmlNode &node, Collada::Animation *pParent);
 
     /** Reads an animation sampler into the given anim channel */
-    void ReadAnimationSampler(Collada::AnimationChannel &pChannel);
+    void ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel);
 
     /** Reads the skeleton controller library */
-    void ReadControllerLibrary();
+    void ReadControllerLibrary(XmlNode &node);
 
     /** Reads a controller into the given mesh structure */
-    void ReadController(Collada::Controller &pController);
+    void ReadController(XmlNode &node, Collada::Controller &pController);
 
     /** Reads the joint definitions for the given controller */
-    void ReadControllerJoints(Collada::Controller &pController);
+    void ReadControllerJoints(XmlNode &node, Collada::Controller &pController);
 
     /** Reads the joint weights for the given controller */
-    void ReadControllerWeights(Collada::Controller &pController);
+    void ReadControllerWeights(XmlNode &node, Collada::Controller &pController);
 
     /** Reads the image library contents */
-    void ReadImageLibrary();
+    void ReadImageLibrary(XmlNode &node);
 
     /** Reads an image entry into the given image */
-    void ReadImage(Collada::Image &pImage);
+    void ReadImage(XmlNode &node, Collada::Image &pImage);
 
     /** Reads the material library */
-    void ReadMaterialLibrary();
+    void ReadMaterialLibrary(XmlNode &node);
 
     /** Reads a material entry into the given material */
-    void ReadMaterial(Collada::Material &pMaterial);
+    void ReadMaterial(XmlNode &node, Collada::Material &pMaterial);
 
     /** Reads the camera library */
-    void ReadCameraLibrary();
+    void ReadCameraLibrary(XmlNode &node);
 
     /** Reads a camera entry into the given camera */
-    void ReadCamera(Collada::Camera &pCamera);
+    void ReadCamera(XmlNode &node, Collada::Camera &pCamera);
 
     /** Reads the light library */
-    void ReadLightLibrary();
+    void ReadLightLibrary(XmlNode &node);
 
     /** Reads a light entry into the given light */
-    void ReadLight(Collada::Light &pLight);
+    void ReadLight(XmlNode &node, Collada::Light &pLight);
 
     /** Reads the effect library */
-    void ReadEffectLibrary();
+    void ReadEffectLibrary(XmlNode &node);
 
     /** Reads an effect entry into the given effect*/
-    void ReadEffect(Collada::Effect &pEffect);
+    void ReadEffect(XmlNode &node, Collada::Effect &pEffect);
 
     /** Reads an COMMON effect profile */
-    void ReadEffectProfileCommon(Collada::Effect &pEffect);
+    void ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect);
 
     /** Read sampler properties */
-    void ReadSamplerProperties(Collada::Sampler &pSampler);
+    void ReadSamplerProperties(XmlNode &node, Collada::Sampler &pSampler);
 
     /** Reads an effect entry containing a color or a texture defining that color */
-    void ReadEffectColor(aiColor4D &pColor, Collada::Sampler &pSampler);
+    void ReadEffectColor(XmlNode &node, aiColor4D &pColor, Collada::Sampler &pSampler);
 
     /** Reads an effect entry containing a float */
-    void ReadEffectFloat(ai_real &pFloat);
+    void ReadEffectFloat(XmlNode &node, ai_real &pFloat);
 
     /** Reads an effect parameter specification of any kind */
-    void ReadEffectParam(Collada::EffectParam &pParam);
+    void ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam);
 
     /** Reads the geometry library contents */
-    void ReadGeometryLibrary();
+    void ReadGeometryLibrary(XmlNode &node);
 
     /** Reads a geometry from the geometry library. */
-    void ReadGeometry(Collada::Mesh &pMesh);
+    void ReadGeometry(XmlNode &node, Collada::Mesh &pMesh);
 
     /** Reads a mesh from the geometry library */
-    void ReadMesh(Collada::Mesh &pMesh);
+    void ReadMesh(XmlNode &node, 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();
+    void ReadSource(XmlNode &node);
 
     /** 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();
+    void ReadDataArray(XmlNode &node);
 
     /** 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);
+    void ReadAccessor(XmlNode &node, const std::string &pID);
 
     /** Reads input declarations of per-vertex mesh data into the given mesh */
-    void ReadVertexData(Collada::Mesh &pMesh);
+    void ReadVertexData(XmlNode &node, Collada::Mesh &pMesh);
 
     /** Reads input declarations of per-index mesh data into the given mesh */
-    void ReadIndexData(Collada::Mesh &pMesh);
+    void ReadIndexData(XmlNode &node, 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);
+    void ReadInputChannel(XmlNode &node, 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 ReadPrimitives(XmlNode &node, 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 */
@@ -220,68 +222,27 @@ protected:
     void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh);
 
     /** Reads the library of node hierarchies and scene parts */
-    void ReadSceneLibrary();
+    void ReadSceneLibrary(XmlNode &node);
 
     /** Reads a scene node's contents including children and stores it in the given node */
-    void ReadSceneNode(Collada::Node *pNode);
+    void ReadSceneNode(XmlNode &node, 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);
+    void ReadNodeTransformation(XmlNode &node, 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);
+    void ReadNodeGeometry(XmlNode &node, Collada::Node *pNode);
 
     /** Reads the collada scene */
-    void ReadScene();
+    void ReadScene(XmlNode &node);
 
     // Processes bind_vertex_input and bind elements
-    void ReadMaterialVertexInputBinding(Collada::SemanticMappingTable &tbl);
+    void ReadMaterialVertexInputBinding(XmlNode &node, 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 nullptr 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;
 
@@ -293,59 +254,59 @@ protected:
     const Type &ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const;
 
 protected:
-    /** Filename, for a verbose error message */
+    // Filename, for a verbose error message
     std::string mFileName;
 
-    /** XML reader, member for everyday use */
-    irr::io::IrrXMLReader *mReader;
+    // XML reader, member for everyday use
+    XmlParser mXmlParser;
 
     /** 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;
+    using DataLibrary = std::map<std::string, Collada::Data> ;
     DataLibrary mDataLibrary;
 
     /** Same for accessors which define how the data in a data array is accessed. */
-    typedef std::map<std::string, Collada::Accessor> AccessorLibrary;
+    using AccessorLibrary = std::map<std::string, Collada::Accessor> ;
     AccessorLibrary mAccessorLibrary;
 
     /** Mesh library: mesh by ID */
-    typedef std::map<std::string, Collada::Mesh *> MeshLibrary;
+    using MeshLibrary = std::map<std::string, Collada::Mesh *>;
     MeshLibrary mMeshLibrary;
 
     /** node library: root node of the hierarchy part by ID */
-    typedef std::map<std::string, Collada::Node *> NodeLibrary;
+    using NodeLibrary = std::map<std::string, Collada::Node *>;
     NodeLibrary mNodeLibrary;
 
     /** Image library: stores texture properties by ID */
-    typedef std::map<std::string, Collada::Image> ImageLibrary;
+    using ImageLibrary = std::map<std::string, Collada::Image> ;
     ImageLibrary mImageLibrary;
 
     /** Effect library: surface attributes by ID */
-    typedef std::map<std::string, Collada::Effect> EffectLibrary;
+    using EffectLibrary = std::map<std::string, Collada::Effect> ;
     EffectLibrary mEffectLibrary;
 
     /** Material library: surface material by ID */
-    typedef std::map<std::string, Collada::Material> MaterialLibrary;
+    using MaterialLibrary = std::map<std::string, Collada::Material> ;
     MaterialLibrary mMaterialLibrary;
 
     /** Light library: surface light by ID */
-    typedef std::map<std::string, Collada::Light> LightLibrary;
+    using LightLibrary = std::map<std::string, Collada::Light> ;
     LightLibrary mLightLibrary;
 
     /** Camera library: surface material by ID */
-    typedef std::map<std::string, Collada::Camera> CameraLibrary;
+    using CameraLibrary = std::map<std::string, Collada::Camera> ;
     CameraLibrary mCameraLibrary;
 
     /** Controller library: joint controllers by ID */
-    typedef std::map<std::string, Collada::Controller> ControllerLibrary;
+    using ControllerLibrary = std::map<std::string, Collada::Controller> ;
     ControllerLibrary mControllerLibrary;
 
     /** Animation library: animation references by ID */
-    typedef std::map<std::string, Collada::Animation *> AnimationLibrary;
+    using AnimationLibrary = std::map<std::string, Collada::Animation *> ;
     AnimationLibrary mAnimationLibrary;
 
     /** Animation clip library: clip animation references by ID */
-    typedef std::vector<std::pair<std::string, std::vector<std::string>>> AnimationClipLibrary;
+    using AnimationClipLibrary = std::vector<std::pair<std::string, std::vector<std::string>>> ;
     AnimationClipLibrary mAnimationClipLibrary;
 
     /** Pointer to the root node. Don't delete, it just points to one of
@@ -370,20 +331,14 @@ protected:
     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 << "\".");
+    if (it == pLibrary.end()) {
+        throw DeadlyImportError("Unable to resolve library reference \"", pURL, "\".");
+    }
     return it->second;
 }
 

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

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

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

@@ -127,7 +127,7 @@ namespace {
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset)
 {
-    throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
+    throw DeadlyImportError("FBX-Tokenize", Util::GetOffsetText(offset), message);
 }
 
 
@@ -468,7 +468,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
     catch (const DeadlyImportError& e)
     {
         if (!is64bits && (length > std::numeric_limits<std::uint32_t>::max())) {
-            throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (" + to_string(version) + ") of the FBX format. (" + e.what() + ")");
+            throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (", to_string(version), ") of the FBX format. (", e.what(), ")");
         }
         throw;
     }

+ 5 - 4
code/AssetLib/FBX/FBXConverter.cpp

@@ -3481,10 +3481,11 @@ void FBXConverter::ConvertOrphanedEmbeddedTextures() {
                 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;
-                    }
+                    if (const Texture *texture = static_cast<const Texture *>(object->Get())) {
+                        if (texture->Media() && texture->Media()->ContentLength() > 0) {
+                            realTexture = texture;
+                        }
+                    }    
                 }
             } catch (...) {
                 // do nothing

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

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

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

@@ -130,6 +130,7 @@ void FBXImporter::SetupProperties(const Importer *pImp) {
 	settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
 	settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
 	settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
+	settings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true);
 	settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
 	settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
 	settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
@@ -141,7 +142,10 @@ void FBXImporter::SetupProperties(const Importer *pImp) {
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
-	std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
+	auto streamCloser = [&](IOStream *pStream) {
+		pIOHandler->Close(pStream);
+	};
+	std::unique_ptr<IOStream, decltype(streamCloser)> stream(pIOHandler->Open(pFile, "rb"), streamCloser);
 	if (!stream) {
 		ThrowException("Could not open file for reading");
 	}
@@ -184,6 +188,11 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
 		// size relative to cm
 		float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
+        if (size_relative_to_cm == 0.0)
+        {
+			// BaseImporter later asserts that fileScale is non-zero.
+			ThrowException("The UnitScaleFactor must be non-zero");
+        }
 
 		// Set FBX file scale is relative to CM must be converted to M for
 		// assimp universal format (M)

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

@@ -86,7 +86,7 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con
     std::string templateName;
 
     // lower-case shading because Blender (for example) writes "Phong"
-    std::transform(shading.begin(), shading.end(), shading.begin(), Assimp::ToLower<char>);
+    std::transform(shading.data(), shading.data() + shading.size(), std::addressof(shading[0]), Assimp::ToLower<char>);
     if(shading == "phong") {
         templateName = "Material.FbxSurfacePhong";
     }

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

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

+ 10 - 1
code/AssetLib/FBX/FBXProperties.cpp

@@ -76,23 +76,30 @@ Property* ReadTypedProperty(const Element& element)
     ai_assert(element.KeyToken().StringContents() == "P");
 
     const TokenList& tok = element.Tokens();
-    ai_assert(tok.size() >= 5);
+    if (tok.size() < 2) {
+        return nullptr;
+    }
 
     const std::string& s = ParseTokenAsString(*tok[1]);
     const char* const cs = s.c_str();
     if (!strcmp(cs,"KString")) {
+        ai_assert(tok.size() >= 5);
         return new TypedProperty<std::string>(ParseTokenAsString(*tok[4]));
     }
     else if (!strcmp(cs,"bool") || !strcmp(cs,"Bool")) {
+        ai_assert(tok.size() >= 5);
         return new TypedProperty<bool>(ParseTokenAsInt(*tok[4]) != 0);
     }
     else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) {
+        ai_assert(tok.size() >= 5);
         return new TypedProperty<int>(ParseTokenAsInt(*tok[4]));
     }
     else if (!strcmp(cs, "ULongLong")) {
+        ai_assert(tok.size() >= 5);
         return new TypedProperty<uint64_t>(ParseTokenAsID(*tok[4]));
     }
     else if (!strcmp(cs, "KTime")) {
+        ai_assert(tok.size() >= 5);
         return new TypedProperty<int64_t>(ParseTokenAsInt64(*tok[4]));
     }
     else if (!strcmp(cs,"Vector3D") ||
@@ -103,6 +110,7 @@ Property* ReadTypedProperty(const Element& element)
         !strcmp(cs,"Lcl Rotation") ||
         !strcmp(cs,"Lcl Scaling")
         ) {
+        ai_assert(tok.size() >= 7);
         return new TypedProperty<aiVector3D>(aiVector3D(
             ParseTokenAsFloat(*tok[4]),
             ParseTokenAsFloat(*tok[5]),
@@ -110,6 +118,7 @@ Property* ReadTypedProperty(const Element& element)
         );
     }
     else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"Float") || !strcmp(cs,"FieldOfView") || !strcmp( cs, "UnitScaleFactor" ) ) {
+        ai_assert(tok.size() >= 5);
         return new TypedProperty<float>(ParseTokenAsFloat(*tok[4]));
     }
     return nullptr;

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

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

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

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

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

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

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

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

+ 1212 - 1226
code/AssetLib/Irr/IRRLoader.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,
@@ -67,200 +65,198 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory>
 
 using namespace Assimp;
-using namespace irr;
-using namespace irr::io;
 
 static const aiImporterDesc desc = {
-    "Irrlicht Scene Reader",
-    "",
-    "",
-    "http://irrlicht.sourceforge.net/",
-    aiImporterFlags_SupportTextFlavour,
-    0,
-    0,
-    0,
-    0,
-    "irr xml"
+	"Irrlicht Scene Reader",
+	"",
+	"",
+	"http://irrlicht.sourceforge.net/",
+	aiImporterFlags_SupportTextFlavour,
+	0,
+	0,
+	0,
+	0,
+	"irr xml"
 };
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 IRRImporter::IRRImporter() :
-        fps(), configSpeedFlag() {
-    // empty
+		fps(), configSpeedFlag() {
+	// empty
 }
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
 IRRImporter::~IRRImporter() {
-    // empty
+	// empty
 }
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
-    const std::string extension = GetExtension(pFile);
-    if (extension == "irr") {
-        return true;
-    } else if (extension == "xml" || checkSig) {
-        /*  If CanRead() is called in order to check whether we
+	const std::string extension = GetExtension(pFile);
+	if (extension == "irr") {
+		return true;
+	} else if (extension == "xml" || checkSig) {
+		/*  If CanRead() is called in order to check whether we
          *  support a specific file extension in general pIOHandler
          *  might be nullptr and it's our duty to return true here.
          */
-        if (nullptr == pIOHandler) {
-            return true;
-        }
-        const char *tokens[] = { "irr_scene" };
-        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
-    }
-
-    return false;
+		if (nullptr == pIOHandler) {
+			return true;
+		}
+		const char *tokens[] = { "irr_scene" };
+		return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+	}
+
+	return false;
 }
 
 // ------------------------------------------------------------------------------------------------
 const aiImporterDesc *IRRImporter::GetInfo() const {
-    return &desc;
+	return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
 void IRRImporter::SetupProperties(const Importer *pImp) {
-    // read the output frame rate of all node animation channels
-    fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS, 100);
-    if (fps < 10.) {
-        ASSIMP_LOG_ERROR("IRR: Invalid FPS configuration");
-        fps = 100;
-    }
-
-    // AI_CONFIG_FAVOUR_SPEED
-    configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED, 0));
+	// read the output frame rate of all node animation channels
+	fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS, 100);
+	if (fps < 10.) {
+		ASSIMP_LOG_ERROR("IRR: Invalid FPS configuration");
+		fps = 100;
+	}
+
+	// AI_CONFIG_FAVOUR_SPEED
+	configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED, 0));
 }
 
 // ------------------------------------------------------------------------------------------------
 // Build a mesh tha consists of a single squad (a side of a skybox)
 aiMesh *IRRImporter::BuildSingleQuadMesh(const SkyboxVertex &v1,
-        const SkyboxVertex &v2,
-        const SkyboxVertex &v3,
-        const SkyboxVertex &v4) {
-    // allocate and prepare the mesh
-    aiMesh *out = new aiMesh();
-
-    out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
-    out->mNumFaces = 1;
-
-    // build the face
-    out->mFaces = new aiFace[1];
-    aiFace &face = out->mFaces[0];
-
-    face.mNumIndices = 4;
-    face.mIndices = new unsigned int[4];
-    for (unsigned int i = 0; i < 4; ++i)
-        face.mIndices[i] = i;
-
-    out->mNumVertices = 4;
-
-    // copy vertex positions
-    aiVector3D *vec = out->mVertices = new aiVector3D[4];
-    *vec++ = v1.position;
-    *vec++ = v2.position;
-    *vec++ = v3.position;
-    *vec = v4.position;
-
-    // copy vertex normals
-    vec = out->mNormals = new aiVector3D[4];
-    *vec++ = v1.normal;
-    *vec++ = v2.normal;
-    *vec++ = v3.normal;
-    *vec = v4.normal;
-
-    // copy texture coordinates
-    vec = out->mTextureCoords[0] = new aiVector3D[4];
-    *vec++ = v1.uv;
-    *vec++ = v2.uv;
-    *vec++ = v3.uv;
-    *vec = v4.uv;
-    return out;
+		const SkyboxVertex &v2,
+		const SkyboxVertex &v3,
+		const SkyboxVertex &v4) {
+	// allocate and prepare the mesh
+	aiMesh *out = new aiMesh();
+
+	out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
+	out->mNumFaces = 1;
+
+	// build the face
+	out->mFaces = new aiFace[1];
+	aiFace &face = out->mFaces[0];
+
+	face.mNumIndices = 4;
+	face.mIndices = new unsigned int[4];
+	for (unsigned int i = 0; i < 4; ++i)
+		face.mIndices[i] = i;
+
+	out->mNumVertices = 4;
+
+	// copy vertex positions
+	aiVector3D *vec = out->mVertices = new aiVector3D[4];
+	*vec++ = v1.position;
+	*vec++ = v2.position;
+	*vec++ = v3.position;
+	*vec = v4.position;
+
+	// copy vertex normals
+	vec = out->mNormals = new aiVector3D[4];
+	*vec++ = v1.normal;
+	*vec++ = v2.normal;
+	*vec++ = v3.normal;
+	*vec = v4.normal;
+
+	// copy texture coordinates
+	vec = out->mTextureCoords[0] = new aiVector3D[4];
+	*vec++ = v1.uv;
+	*vec++ = v2.uv;
+	*vec++ = v3.uv;
+	*vec = v4.uv;
+	return out;
 }
 
 // ------------------------------------------------------------------------------------------------
 void IRRImporter::BuildSkybox(std::vector<aiMesh *> &meshes, std::vector<aiMaterial *> materials) {
-    // Update the material of the skybox - replace the name and disable shading for skyboxes.
-    for (unsigned int i = 0; i < 6; ++i) {
-        aiMaterial *out = (aiMaterial *)(*(materials.end() - (6 - i)));
-
-        aiString s;
-        s.length = ::ai_snprintf(s.data, MAXLEN, "SkyboxSide_%u", i);
-        out->AddProperty(&s, AI_MATKEY_NAME);
-
-        int shading = aiShadingMode_NoShading;
-        out->AddProperty(&shading, 1, AI_MATKEY_SHADING_MODEL);
-    }
-
-    // Skyboxes are much more difficult. They are represented
-    // by six single planes with different textures, so we'll
-    // need to build six meshes.
-
-    const ai_real l = 10.0; // the size used by Irrlicht
-
-    // FRONT SIDE
-    meshes.push_back(BuildSingleQuadMesh(
-            SkyboxVertex(-l, -l, -l, 0, 0, 1, 1.0, 1.0),
-            SkyboxVertex(l, -l, -l, 0, 0, 1, 0.0, 1.0),
-            SkyboxVertex(l, l, -l, 0, 0, 1, 0.0, 0.0),
-            SkyboxVertex(-l, l, -l, 0, 0, 1, 1.0, 0.0)));
-    meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 6u);
-
-    // LEFT SIDE
-    meshes.push_back(BuildSingleQuadMesh(
-            SkyboxVertex(l, -l, -l, -1, 0, 0, 1.0, 1.0),
-            SkyboxVertex(l, -l, l, -1, 0, 0, 0.0, 1.0),
-            SkyboxVertex(l, l, l, -1, 0, 0, 0.0, 0.0),
-            SkyboxVertex(l, l, -l, -1, 0, 0, 1.0, 0.0)));
-    meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 5u);
-
-    // BACK SIDE
-    meshes.push_back(BuildSingleQuadMesh(
-            SkyboxVertex(l, -l, l, 0, 0, -1, 1.0, 1.0),
-            SkyboxVertex(-l, -l, l, 0, 0, -1, 0.0, 1.0),
-            SkyboxVertex(-l, l, l, 0, 0, -1, 0.0, 0.0),
-            SkyboxVertex(l, l, l, 0, 0, -1, 1.0, 0.0)));
-    meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 4u);
-
-    // RIGHT SIDE
-    meshes.push_back(BuildSingleQuadMesh(
-            SkyboxVertex(-l, -l, l, 1, 0, 0, 1.0, 1.0),
-            SkyboxVertex(-l, -l, -l, 1, 0, 0, 0.0, 1.0),
-            SkyboxVertex(-l, l, -l, 1, 0, 0, 0.0, 0.0),
-            SkyboxVertex(-l, l, l, 1, 0, 0, 1.0, 0.0)));
-    meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 3u);
-
-    // TOP SIDE
-    meshes.push_back(BuildSingleQuadMesh(
-            SkyboxVertex(l, l, -l, 0, -1, 0, 1.0, 1.0),
-            SkyboxVertex(l, l, l, 0, -1, 0, 0.0, 1.0),
-            SkyboxVertex(-l, l, l, 0, -1, 0, 0.0, 0.0),
-            SkyboxVertex(-l, l, -l, 0, -1, 0, 1.0, 0.0)));
-    meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 2u);
-
-    // BOTTOM SIDE
-    meshes.push_back(BuildSingleQuadMesh(
-            SkyboxVertex(l, -l, l, 0, 1, 0, 0.0, 0.0),
-            SkyboxVertex(l, -l, -l, 0, 1, 0, 1.0, 0.0),
-            SkyboxVertex(-l, -l, -l, 0, 1, 0, 1.0, 1.0),
-            SkyboxVertex(-l, -l, l, 0, 1, 0, 0.0, 1.0)));
-    meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 1u);
+	// Update the material of the skybox - replace the name and disable shading for skyboxes.
+	for (unsigned int i = 0; i < 6; ++i) {
+		aiMaterial *out = (aiMaterial *)(*(materials.end() - (6 - i)));
+
+		aiString s;
+		s.length = ::ai_snprintf(s.data, MAXLEN, "SkyboxSide_%u", i);
+		out->AddProperty(&s, AI_MATKEY_NAME);
+
+		int shading = aiShadingMode_NoShading;
+		out->AddProperty(&shading, 1, AI_MATKEY_SHADING_MODEL);
+	}
+
+	// Skyboxes are much more difficult. They are represented
+	// by six single planes with different textures, so we'll
+	// need to build six meshes.
+
+	const ai_real l = 10.0; // the size used by Irrlicht
+
+	// FRONT SIDE
+	meshes.push_back(BuildSingleQuadMesh(
+			SkyboxVertex(-l, -l, -l, 0, 0, 1, 1.0, 1.0),
+			SkyboxVertex(l, -l, -l, 0, 0, 1, 0.0, 1.0),
+			SkyboxVertex(l, l, -l, 0, 0, 1, 0.0, 0.0),
+			SkyboxVertex(-l, l, -l, 0, 0, 1, 1.0, 0.0)));
+	meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 6u);
+
+	// LEFT SIDE
+	meshes.push_back(BuildSingleQuadMesh(
+			SkyboxVertex(l, -l, -l, -1, 0, 0, 1.0, 1.0),
+			SkyboxVertex(l, -l, l, -1, 0, 0, 0.0, 1.0),
+			SkyboxVertex(l, l, l, -1, 0, 0, 0.0, 0.0),
+			SkyboxVertex(l, l, -l, -1, 0, 0, 1.0, 0.0)));
+	meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 5u);
+
+	// BACK SIDE
+	meshes.push_back(BuildSingleQuadMesh(
+			SkyboxVertex(l, -l, l, 0, 0, -1, 1.0, 1.0),
+			SkyboxVertex(-l, -l, l, 0, 0, -1, 0.0, 1.0),
+			SkyboxVertex(-l, l, l, 0, 0, -1, 0.0, 0.0),
+			SkyboxVertex(l, l, l, 0, 0, -1, 1.0, 0.0)));
+	meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 4u);
+
+	// RIGHT SIDE
+	meshes.push_back(BuildSingleQuadMesh(
+			SkyboxVertex(-l, -l, l, 1, 0, 0, 1.0, 1.0),
+			SkyboxVertex(-l, -l, -l, 1, 0, 0, 0.0, 1.0),
+			SkyboxVertex(-l, l, -l, 1, 0, 0, 0.0, 0.0),
+			SkyboxVertex(-l, l, l, 1, 0, 0, 1.0, 0.0)));
+	meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 3u);
+
+	// TOP SIDE
+	meshes.push_back(BuildSingleQuadMesh(
+			SkyboxVertex(l, l, -l, 0, -1, 0, 1.0, 1.0),
+			SkyboxVertex(l, l, l, 0, -1, 0, 0.0, 1.0),
+			SkyboxVertex(-l, l, l, 0, -1, 0, 0.0, 0.0),
+			SkyboxVertex(-l, l, -l, 0, -1, 0, 1.0, 0.0)));
+	meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 2u);
+
+	// BOTTOM SIDE
+	meshes.push_back(BuildSingleQuadMesh(
+			SkyboxVertex(l, -l, l, 0, 1, 0, 0.0, 0.0),
+			SkyboxVertex(l, -l, -l, 0, 1, 0, 1.0, 0.0),
+			SkyboxVertex(-l, -l, -l, 0, 1, 0, 1.0, 1.0),
+			SkyboxVertex(-l, -l, l, 0, 1, 0, 0.0, 1.0)));
+	meshes.back()->mMaterialIndex = static_cast<unsigned int>(materials.size() - 1u);
 }
 
 // ------------------------------------------------------------------------------------------------
 void IRRImporter::CopyMaterial(std::vector<aiMaterial *> &materials,
-        std::vector<std::pair<aiMaterial *, unsigned int>> &inmaterials,
-        unsigned int &defMatIdx,
-        aiMesh *mesh) {
-    if (inmaterials.empty()) {
-        // Do we have a default material? If not we need to create one
-        if (UINT_MAX == defMatIdx) {
-            defMatIdx = (unsigned int)materials.size();
-            //TODO: add this materials to someone?
-            /*aiMaterial* mat = new aiMaterial();
+		std::vector<std::pair<aiMaterial *, unsigned int>> &inmaterials,
+		unsigned int &defMatIdx,
+		aiMesh *mesh) {
+	if (inmaterials.empty()) {
+		// Do we have a default material? If not we need to create one
+		if (UINT_MAX == defMatIdx) {
+			defMatIdx = (unsigned int)materials.size();
+			//TODO: add this materials to someone?
+			/*aiMaterial* mat = new aiMaterial();
 
             aiString s;
             s.Set(AI_DEFAULT_MATERIAL_NAME);
@@ -268,1120 +264,1110 @@ void IRRImporter::CopyMaterial(std::vector<aiMaterial *> &materials,
 
             aiColor3D c(0.6f,0.6f,0.6f);
             mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);*/
-        }
-        mesh->mMaterialIndex = defMatIdx;
-        return;
-    } else if (inmaterials.size() > 1) {
-        ASSIMP_LOG_INFO("IRR: Skipping additional materials");
-    }
-
-    mesh->mMaterialIndex = (unsigned int)materials.size();
-    materials.push_back(inmaterials[0].first);
+		}
+		mesh->mMaterialIndex = defMatIdx;
+		return;
+	} else if (inmaterials.size() > 1) {
+		ASSIMP_LOG_INFO("IRR: Skipping additional materials");
+	}
+
+	mesh->mMaterialIndex = (unsigned int)materials.size();
+	materials.push_back(inmaterials[0].first);
 }
 
 // ------------------------------------------------------------------------------------------------
 inline int ClampSpline(int idx, int size) {
-    return (idx < 0 ? size + idx : (idx >= size ? idx - size : idx));
+	return (idx < 0 ? size + idx : (idx >= size ? idx - size : idx));
 }
 
 // ------------------------------------------------------------------------------------------------
 inline void FindSuitableMultiple(int &angle) {
-    if (angle < 3)
-        angle = 3;
-    else if (angle < 10)
-        angle = 10;
-    else if (angle < 20)
-        angle = 20;
-    else if (angle < 30)
-        angle = 30;
+	if (angle < 3)
+		angle = 3;
+	else if (angle < 10)
+		angle = 10;
+	else if (angle < 20)
+		angle = 20;
+	else if (angle < 30)
+		angle = 30;
 }
 
 // ------------------------------------------------------------------------------------------------
 void IRRImporter::ComputeAnimations(Node *root, aiNode *real, std::vector<aiNodeAnim *> &anims) {
-    ai_assert(nullptr != root);
-    ai_assert(nullptr != real);
-
-    // XXX totally WIP - doesn't produce proper results, need to evaluate
-    // whether there's any use for Irrlicht's proprietary scene format
-    // outside Irrlicht ...
-    // This also applies to the above function of FindSuitableMultiple and ClampSpline which are
-    // solely used in this function
-
-    if (root->animators.empty()) {
-        return;
-    }
-    unsigned int total(0);
-    for (std::list<Animator>::iterator it = root->animators.begin(); it != root->animators.end(); ++it) {
-        if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
-            ASSIMP_LOG_WARN("IRR: Skipping unknown or unsupported animator");
-            continue;
-        }
-        ++total;
-    }
-    if (!total) {
-        return;
-    } else if (1 == total) {
-        ASSIMP_LOG_WARN("IRR: Adding dummy nodes to simulate multiple animators");
-    }
-
-    // NOTE: 1 tick == i millisecond
-
-    unsigned int cur = 0;
-    for (std::list<Animator>::iterator it = root->animators.begin();
-            it != root->animators.end(); ++it) {
-        if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) continue;
-
-        Animator &in = *it;
-        aiNodeAnim *anim = new aiNodeAnim();
-
-        if (cur != total - 1) {
-            // Build a new name - a prefix instead of a suffix because it is
-            // easier to check against
-            anim->mNodeName.length = ::ai_snprintf(anim->mNodeName.data, MAXLEN,
-                    "$INST_DUMMY_%i_%s", total - 1,
-                    (root->name.length() ? root->name.c_str() : ""));
-
-            // we'll also need to insert a dummy in the node hierarchy.
-            aiNode *dummy = new aiNode();
-
-            for (unsigned int i = 0; i < real->mParent->mNumChildren; ++i)
-                if (real->mParent->mChildren[i] == real)
-                    real->mParent->mChildren[i] = dummy;
-
-            dummy->mParent = real->mParent;
-            dummy->mName = anim->mNodeName;
-
-            dummy->mNumChildren = 1;
-            dummy->mChildren = new aiNode *[dummy->mNumChildren];
-            dummy->mChildren[0] = real;
-
-            // the transformation matrix of the dummy node is the identity
-
-            real->mParent = dummy;
-        } else
-            anim->mNodeName.Set(root->name);
-        ++cur;
-
-        switch (in.type) {
-        case Animator::ROTATION: {
-            // -----------------------------------------------------
-            // find out how long a full rotation will take
-            // This is the least common multiple of 360.f and all
-            // three euler angles. Although we'll surely find a
-            // possible multiple (haha) it could be somewhat large
-            // for our purposes. So we need to modify the angles
-            // here in order to get good results.
-            // -----------------------------------------------------
-            int angles[3];
-            angles[0] = (int)(in.direction.x * 100);
-            angles[1] = (int)(in.direction.y * 100);
-            angles[2] = (int)(in.direction.z * 100);
-
-            angles[0] %= 360;
-            angles[1] %= 360;
-            angles[2] %= 360;
-
-            if ((angles[0] * angles[1]) != 0 && (angles[1] * angles[2]) != 0) {
-                FindSuitableMultiple(angles[0]);
-                FindSuitableMultiple(angles[1]);
-                FindSuitableMultiple(angles[2]);
-            }
-
-            int lcm = 360;
-
-            if (angles[0])
-                lcm = Math::lcm(lcm, angles[0]);
-
-            if (angles[1])
-                lcm = Math::lcm(lcm, angles[1]);
-
-            if (angles[2])
-                lcm = Math::lcm(lcm, angles[2]);
-
-            if (360 == lcm)
-                break;
-
-#if 0
-                // This can be a division through zero, but we don't care
-                float f1 = (float)lcm / angles[0];
-                float f2 = (float)lcm / angles[1];
-                float f3 = (float)lcm / angles[2];
-#endif
-
-            // find out how many time units we'll need for the finest
-            // track (in seconds) - this defines the number of output
-            // keys (fps * seconds)
-            float max = 0.f;
-            if (angles[0])
-                max = (float)lcm / angles[0];
-            if (angles[1])
-                max = std::max(max, (float)lcm / angles[1]);
-            if (angles[2])
-                max = std::max(max, (float)lcm / angles[2]);
-
-            anim->mNumRotationKeys = (unsigned int)(max * fps);
-            anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
-
-            // begin with a zero angle
-            aiVector3D angle;
-            for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
-                // build the quaternion for the given euler angles
-                aiQuatKey &q = anim->mRotationKeys[i];
-
-                q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
-                q.mTime = (double)i;
-
-                // increase the angle
-                angle += in.direction;
-            }
-
-            // This animation is repeated and repeated ...
-            anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
-        } break;
-
-        case Animator::FLY_CIRCLE: {
-            // -----------------------------------------------------
-            // Find out how much time we'll need to perform a
-            // full circle.
-            // -----------------------------------------------------
-            const double seconds = (1. / in.speed) / 1000.;
-            const double tdelta = 1000. / fps;
-
-            anim->mNumPositionKeys = (unsigned int)(fps * seconds);
-            anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
-
-            // from Irrlicht, what else should we do than copying it?
-            aiVector3D vecU, vecV;
-            if (in.direction.y) {
-                vecV = aiVector3D(50, 0, 0) ^ in.direction;
-            } else
-                vecV = aiVector3D(0, 50, 00) ^ in.direction;
-            vecV.Normalize();
-            vecU = (vecV ^ in.direction).Normalize();
-
-            // build the output keys
-            for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
-                aiVectorKey &key = anim->mPositionKeys[i];
-                key.mTime = i * tdelta;
-
-                const ai_real t = (ai_real)(in.speed * key.mTime);
-                key.mValue = in.circleCenter + in.circleRadius * ((vecU * std::cos(t)) + (vecV * std::sin(t)));
-            }
-
-            // This animation is repeated and repeated ...
-            anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
-        } break;
-
-        case Animator::FLY_STRAIGHT: {
-            anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
-            const double seconds = in.timeForWay / 1000.;
-            const double tdelta = 1000. / fps;
-
-            anim->mNumPositionKeys = (unsigned int)(fps * seconds);
-            anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
-
-            aiVector3D diff = in.direction - in.circleCenter;
-            const ai_real lengthOfWay = diff.Length();
-            diff.Normalize();
-
-            const double timeFactor = lengthOfWay / in.timeForWay;
-
-            // build the output keys
-            for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
-                aiVectorKey &key = anim->mPositionKeys[i];
-                key.mTime = i * tdelta;
-                key.mValue = in.circleCenter + diff * ai_real(timeFactor * key.mTime);
-            }
-        } break;
-
-        case Animator::FOLLOW_SPLINE: {
-            // repeat outside the defined time range
-            anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
-            const int size = (int)in.splineKeys.size();
-            if (!size) {
-                // We have no point in the spline. That's bad. Really bad.
-                ASSIMP_LOG_WARN("IRR: Spline animators with no points defined");
-
-                delete anim;
-                anim = nullptr;
-                break;
-            } else if (size == 1) {
-                // We have just one point in the spline so we don't need the full calculation
-                anim->mNumPositionKeys = 1;
-                anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
-
-                anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
-                anim->mPositionKeys[0].mTime = 0.f;
-                break;
-            }
-
-            unsigned int ticksPerFull = 15;
-            anim->mNumPositionKeys = (unsigned int)(ticksPerFull * fps);
-            anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
-
-            for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
-                aiVectorKey &key = anim->mPositionKeys[i];
-
-                const ai_real dt = (i * in.speed * ai_real(0.001));
-                const ai_real u = dt - std::floor(dt);
-                const int idx = (int)std::floor(dt) % size;
-
-                // get the 4 current points to evaluate the spline
-                const aiVector3D &p0 = in.splineKeys[ClampSpline(idx - 1, size)].mValue;
-                const aiVector3D &p1 = in.splineKeys[ClampSpline(idx + 0, size)].mValue;
-                const aiVector3D &p2 = in.splineKeys[ClampSpline(idx + 1, size)].mValue;
-                const aiVector3D &p3 = in.splineKeys[ClampSpline(idx + 2, size)].mValue;
-
-                // compute polynomials
-                const ai_real u2 = u * u;
-                const ai_real u3 = u2 * 2;
-
-                const ai_real h1 = ai_real(2.0) * u3 - ai_real(3.0) * u2 + ai_real(1.0);
-                const ai_real h2 = ai_real(-2.0) * u3 + ai_real(3.0) * u3;
-                const ai_real h3 = u3 - ai_real(2.0) * u3;
-                const ai_real h4 = u3 - u2;
-
-                // compute the spline tangents
-                const aiVector3D t1 = (p2 - p0) * in.tightness;
-                aiVector3D t2 = (p3 - p1) * in.tightness;
-
-                // and use them to get the interpolated point
-                t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
-
-                // build a simple translation matrix from it
-                key.mValue = t2;
-                key.mTime = (double)i;
-            }
-        } break;
-        default:
-            // UNKNOWN , OTHER
-            break;
-        };
-        if (anim) {
-            anims.push_back(anim);
-            ++total;
-        }
-    }
+	ai_assert(nullptr != root && nullptr != real);
+
+	// XXX totally WIP - doesn't produce proper results, need to evaluate
+	// whether there's any use for Irrlicht's proprietary scene format
+	// outside Irrlicht ...
+	// This also applies to the above function of FindSuitableMultiple and ClampSpline which are
+	// solely used in this function
+
+	if (root->animators.empty()) {
+		return;
+	}
+	unsigned int total(0);
+	for (std::list<Animator>::iterator it = root->animators.begin(); it != root->animators.end(); ++it) {
+		if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
+			ASSIMP_LOG_WARN("IRR: Skipping unknown or unsupported animator");
+			continue;
+		}
+		++total;
+	}
+	if (!total) {
+		return;
+	} else if (1 == total) {
+		ASSIMP_LOG_WARN("IRR: Adding dummy nodes to simulate multiple animators");
+	}
+
+	// NOTE: 1 tick == i millisecond
+
+	unsigned int cur = 0;
+	for (std::list<Animator>::iterator it = root->animators.begin();
+			it != root->animators.end(); ++it) {
+		if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) continue;
+
+		Animator &in = *it;
+		aiNodeAnim *anim = new aiNodeAnim();
+
+		if (cur != total - 1) {
+			// Build a new name - a prefix instead of a suffix because it is
+			// easier to check against
+			anim->mNodeName.length = ::ai_snprintf(anim->mNodeName.data, MAXLEN,
+					"$INST_DUMMY_%i_%s", total - 1,
+					(root->name.length() ? root->name.c_str() : ""));
+
+			// we'll also need to insert a dummy in the node hierarchy.
+			aiNode *dummy = new aiNode();
+
+			for (unsigned int i = 0; i < real->mParent->mNumChildren; ++i)
+				if (real->mParent->mChildren[i] == real)
+					real->mParent->mChildren[i] = dummy;
+
+			dummy->mParent = real->mParent;
+			dummy->mName = anim->mNodeName;
+
+			dummy->mNumChildren = 1;
+			dummy->mChildren = new aiNode *[dummy->mNumChildren];
+			dummy->mChildren[0] = real;
+
+			// the transformation matrix of the dummy node is the identity
+
+			real->mParent = dummy;
+		} else
+			anim->mNodeName.Set(root->name);
+		++cur;
+
+		switch (in.type) {
+			case Animator::ROTATION: {
+				// -----------------------------------------------------
+				// find out how long a full rotation will take
+				// This is the least common multiple of 360.f and all
+				// three euler angles. Although we'll surely find a
+				// possible multiple (haha) it could be somewhat large
+				// for our purposes. So we need to modify the angles
+				// here in order to get good results.
+				// -----------------------------------------------------
+				int angles[3];
+				angles[0] = (int)(in.direction.x * 100);
+				angles[1] = (int)(in.direction.y * 100);
+				angles[2] = (int)(in.direction.z * 100);
+
+				angles[0] %= 360;
+				angles[1] %= 360;
+				angles[2] %= 360;
+
+				if ((angles[0] * angles[1]) != 0 && (angles[1] * angles[2]) != 0) {
+					FindSuitableMultiple(angles[0]);
+					FindSuitableMultiple(angles[1]);
+					FindSuitableMultiple(angles[2]);
+				}
+
+				int lcm = 360;
+
+				if (angles[0])
+					lcm = Math::lcm(lcm, angles[0]);
+
+				if (angles[1])
+					lcm = Math::lcm(lcm, angles[1]);
+
+				if (angles[2])
+					lcm = Math::lcm(lcm, angles[2]);
+
+				if (360 == lcm)
+					break;
+
+
+				// find out how many time units we'll need for the finest
+				// track (in seconds) - this defines the number of output
+				// keys (fps * seconds)
+				float max = 0.f;
+				if (angles[0])
+					max = (float)lcm / angles[0];
+				if (angles[1])
+					max = std::max(max, (float)lcm / angles[1]);
+				if (angles[2])
+					max = std::max(max, (float)lcm / angles[2]);
+
+				anim->mNumRotationKeys = (unsigned int)(max * fps);
+				anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
+
+				// begin with a zero angle
+				aiVector3D angle;
+				for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
+					// build the quaternion for the given euler angles
+					aiQuatKey &q = anim->mRotationKeys[i];
+
+					q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
+					q.mTime = (double)i;
+
+					// increase the angle
+					angle += in.direction;
+				}
+
+				// This animation is repeated and repeated ...
+				anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+			} break;
+
+			case Animator::FLY_CIRCLE: {
+				// -----------------------------------------------------
+				// Find out how much time we'll need to perform a
+				// full circle.
+				// -----------------------------------------------------
+				const double seconds = (1. / in.speed) / 1000.;
+				const double tdelta = 1000. / fps;
+
+				anim->mNumPositionKeys = (unsigned int)(fps * seconds);
+				anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+				// from Irrlicht, what else should we do than copying it?
+				aiVector3D vecU, vecV;
+				if (in.direction.y) {
+					vecV = aiVector3D(50, 0, 0) ^ in.direction;
+				} else
+					vecV = aiVector3D(0, 50, 00) ^ in.direction;
+				vecV.Normalize();
+				vecU = (vecV ^ in.direction).Normalize();
+
+				// build the output keys
+				for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
+					aiVectorKey &key = anim->mPositionKeys[i];
+					key.mTime = i * tdelta;
+
+					const ai_real t = (ai_real)(in.speed * key.mTime);
+					key.mValue = in.circleCenter + in.circleRadius * ((vecU * std::cos(t)) + (vecV * std::sin(t)));
+				}
+
+				// This animation is repeated and repeated ...
+				anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+			} break;
+
+			case Animator::FLY_STRAIGHT: {
+				anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
+				const double seconds = in.timeForWay / 1000.;
+				const double tdelta = 1000. / fps;
+
+				anim->mNumPositionKeys = (unsigned int)(fps * seconds);
+				anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+				aiVector3D diff = in.direction - in.circleCenter;
+				const ai_real lengthOfWay = diff.Length();
+				diff.Normalize();
+
+				const double timeFactor = lengthOfWay / in.timeForWay;
+
+				// build the output keys
+				for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
+					aiVectorKey &key = anim->mPositionKeys[i];
+					key.mTime = i * tdelta;
+					key.mValue = in.circleCenter + diff * ai_real(timeFactor * key.mTime);
+				}
+			} break;
+
+			case Animator::FOLLOW_SPLINE: {
+				// repeat outside the defined time range
+				anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+				const int size = (int)in.splineKeys.size();
+				if (!size) {
+					// We have no point in the spline. That's bad. Really bad.
+					ASSIMP_LOG_WARN("IRR: Spline animators with no points defined");
+
+					delete anim;
+					anim = nullptr;
+					break;
+				} else if (size == 1) {
+					// We have just one point in the spline so we don't need the full calculation
+					anim->mNumPositionKeys = 1;
+					anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+					anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
+					anim->mPositionKeys[0].mTime = 0.f;
+					break;
+				}
+
+				unsigned int ticksPerFull = 15;
+				anim->mNumPositionKeys = (unsigned int)(ticksPerFull * fps);
+				anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+				for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
+					aiVectorKey &key = anim->mPositionKeys[i];
+
+					const ai_real dt = (i * in.speed * ai_real(0.001));
+					const ai_real u = dt - std::floor(dt);
+					const int idx = (int)std::floor(dt) % size;
+
+					// get the 4 current points to evaluate the spline
+					const aiVector3D &p0 = in.splineKeys[ClampSpline(idx - 1, size)].mValue;
+					const aiVector3D &p1 = in.splineKeys[ClampSpline(idx + 0, size)].mValue;
+					const aiVector3D &p2 = in.splineKeys[ClampSpline(idx + 1, size)].mValue;
+					const aiVector3D &p3 = in.splineKeys[ClampSpline(idx + 2, size)].mValue;
+
+					// compute polynomials
+					const ai_real u2 = u * u;
+					const ai_real u3 = u2 * 2;
+
+					const ai_real h1 = ai_real(2.0) * u3 - ai_real(3.0) * u2 + ai_real(1.0);
+					const ai_real h2 = ai_real(-2.0) * u3 + ai_real(3.0) * u3;
+					const ai_real h3 = u3 - ai_real(2.0) * u3;
+					const ai_real h4 = u3 - u2;
+
+					// compute the spline tangents
+					const aiVector3D t1 = (p2 - p0) * in.tightness;
+					aiVector3D t2 = (p3 - p1) * in.tightness;
+
+					// and use them to get the interpolated point
+					t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
+
+					// build a simple translation matrix from it
+					key.mValue = t2;
+					key.mTime = (double)i;
+				}
+			} break;
+			default:
+				// UNKNOWN , OTHER
+				break;
+		};
+		if (anim) {
+			anims.push_back(anim);
+			++total;
+		}
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // This function is maybe more generic than we'd need it here
 void SetupMapping(aiMaterial *mat, aiTextureMapping mode, const aiVector3D &axis = aiVector3D(0.f, 0.f, -1.f)) {
-    // Check whether there are texture properties defined - setup
-    // the desired texture mapping mode for all of them and ignore
-    // all UV settings we might encounter. WE HAVE NO UVS!
-
-    std::vector<aiMaterialProperty *> p;
-    p.reserve(mat->mNumProperties + 1);
-
-    for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
-        aiMaterialProperty *prop = mat->mProperties[i];
-        if (!::strcmp(prop->mKey.data, "$tex.file")) {
-            // Setup the mapping key
-            aiMaterialProperty *m = new aiMaterialProperty();
-            m->mKey.Set("$tex.mapping");
-            m->mIndex = prop->mIndex;
-            m->mSemantic = prop->mSemantic;
-            m->mType = aiPTI_Integer;
-
-            m->mDataLength = 4;
-            m->mData = new char[4];
-            *((int *)m->mData) = mode;
-
-            p.push_back(prop);
-            p.push_back(m);
-
-            // Setup the mapping axis
-            if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
-                m = new aiMaterialProperty();
-                m->mKey.Set("$tex.mapaxis");
-                m->mIndex = prop->mIndex;
-                m->mSemantic = prop->mSemantic;
-                m->mType = aiPTI_Float;
-
-                m->mDataLength = 12;
-                m->mData = new char[12];
-                *((aiVector3D *)m->mData) = axis;
-                p.push_back(m);
-            }
-        } else if (!::strcmp(prop->mKey.data, "$tex.uvwsrc")) {
-            delete mat->mProperties[i];
-        } else
-            p.push_back(prop);
-    }
-
-    if (p.empty()) return;
+	if (nullptr == mat) {
+		return;
+	}
 
-    // rebuild the output array
-    if (p.size() > mat->mNumAllocated) {
-        delete[] mat->mProperties;
-        mat->mProperties = new aiMaterialProperty *[p.size() * 2];
-
-        mat->mNumAllocated = static_cast<unsigned int>(p.size() * 2);
-    }
-    mat->mNumProperties = (unsigned int)p.size();
-    ::memcpy(mat->mProperties, &p[0], sizeof(void *) * mat->mNumProperties);
+    // Check whether there are texture properties defined - setup
+	// the desired texture mapping mode for all of them and ignore
+	// all UV settings we might encounter. WE HAVE NO UVS!
+
+	std::vector<aiMaterialProperty *> p;
+	p.reserve(mat->mNumProperties + 1);
+
+	for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
+		aiMaterialProperty *prop = mat->mProperties[i];
+		if (!::strcmp(prop->mKey.data, "$tex.file")) {
+			// Setup the mapping key
+			aiMaterialProperty *m = new aiMaterialProperty();
+			m->mKey.Set("$tex.mapping");
+			m->mIndex = prop->mIndex;
+			m->mSemantic = prop->mSemantic;
+			m->mType = aiPTI_Integer;
+
+			m->mDataLength = 4;
+			m->mData = new char[4];
+			*((int *)m->mData) = mode;
+
+			p.push_back(prop);
+			p.push_back(m);
+
+			// Setup the mapping axis
+			if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
+				m = new aiMaterialProperty();
+				m->mKey.Set("$tex.mapaxis");
+				m->mIndex = prop->mIndex;
+				m->mSemantic = prop->mSemantic;
+				m->mType = aiPTI_Float;
+
+				m->mDataLength = 12;
+				m->mData = new char[12];
+				*((aiVector3D *)m->mData) = axis;
+				p.push_back(m);
+			}
+		} else if (!::strcmp(prop->mKey.data, "$tex.uvwsrc")) {
+			delete mat->mProperties[i];
+		} else
+			p.push_back(prop);
+	}
+
+	if (p.empty()) return;
+
+	// rebuild the output array
+	if (p.size() > mat->mNumAllocated) {
+		delete[] mat->mProperties;
+		mat->mProperties = new aiMaterialProperty *[p.size() * 2];
+
+		mat->mNumAllocated = static_cast<unsigned int>(p.size() * 2);
+	}
+	mat->mNumProperties = (unsigned int)p.size();
+	::memcpy(mat->mProperties, &p[0], sizeof(void *) * mat->mNumProperties);
 }
 
 // ------------------------------------------------------------------------------------------------
 void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
-        BatchLoader &batch,
-        std::vector<aiMesh *> &meshes,
-        std::vector<aiNodeAnim *> &anims,
-        std::vector<AttachmentInfo> &attach,
-        std::vector<aiMaterial *> &materials,
-        unsigned int &defMatIdx) {
-    unsigned int oldMeshSize = (unsigned int)meshes.size();
-    //unsigned int meshTrafoAssign = 0;
-
-    // Now determine the type of the node
-    switch (root->type) {
-    case Node::ANIMMESH:
-    case Node::MESH: {
-        if (!root->meshPath.length())
-            break;
-
-        // Get the loaded mesh from the scene and add it to
-        // the list of all scenes to be attached to the
-        // graph we're currently building
-        aiScene *localScene = batch.GetImport(root->id);
-        if (!localScene) {
-            ASSIMP_LOG_ERROR("IRR: Unable to load external file: " + root->meshPath);
-            break;
-        }
-        attach.push_back(AttachmentInfo(localScene, rootOut));
-
-        // Now combine the material we've loaded for this mesh
-        // with the real materials we got from the file. As we
-        // don't execute any pp-steps on the file, the numbers
-        // should be equal. If they are not, we can impossibly
-        // do this  ...
-        if (root->materials.size() != (unsigned int)localScene->mNumMaterials) {
-            ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
-                            "with the materials found in the IRR scene file");
-
-            break;
-        }
-        for (unsigned int i = 0; i < localScene->mNumMaterials; ++i) {
-            // Delete the old material, we don't need it anymore
-            delete localScene->mMaterials[i];
-
-            std::pair<aiMaterial *, unsigned int> &src = root->materials[i];
-            localScene->mMaterials[i] = src.first;
-        }
-
-        // NOTE: Each mesh should have exactly one material assigned,
-        // but we do it in a separate loop if this behaviour changes
-        // in future.
-        for (unsigned int i = 0; i < localScene->mNumMeshes; ++i) {
-            // Process material flags
-            aiMesh *mesh = localScene->mMeshes[i];
-
-            // If "trans_vertex_alpha" mode is enabled, search all vertex colors
-            // and check whether they have a common alpha value. This is quite
-            // often the case so we can simply extract it to a shared oacity
-            // value.
-            std::pair<aiMaterial *, unsigned int> &src = root->materials[mesh->mMaterialIndex];
-            aiMaterial *mat = (aiMaterial *)src.first;
-
-            if (mesh->HasVertexColors(0) && src.second & AI_IRRMESH_MAT_trans_vertex_alpha) {
-                bool bdo = true;
-                for (unsigned int a = 1; a < mesh->mNumVertices; ++a) {
-
-                    if (mesh->mColors[0][a].a != mesh->mColors[0][a - 1].a) {
-                        bdo = false;
-                        break;
-                    }
-                }
-                if (bdo) {
-                    ASSIMP_LOG_INFO("IRR: Replacing mesh vertex alpha with common opacity");
-
-                    for (unsigned int a = 0; a < mesh->mNumVertices; ++a)
-                        mesh->mColors[0][a].a = 1.f;
-
-                    mat->AddProperty(&mesh->mColors[0][0].a, 1, AI_MATKEY_OPACITY);
-                }
-            }
-
-            // If we have a second texture coordinate set and a second texture
-            // (either lightmap, normalmap, 2layered material) we need to
-            // setup the correct UV index for it. The texture can either
-            // be diffuse (lightmap & 2layer) or a normal map (normal & parallax)
-            if (mesh->HasTextureCoords(1)) {
-
-                int idx = 1;
-                if (src.second & (AI_IRRMESH_MAT_solid_2layer | AI_IRRMESH_MAT_lightmap)) {
-                    mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(0));
-                } else if (src.second & AI_IRRMESH_MAT_normalmap_solid) {
-                    mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0));
-                }
-            }
-        }
-    } break;
-
-    case Node::LIGHT:
-    case Node::CAMERA:
-
-        // We're already finished with lights and cameras
-        break;
-
-    case Node::SPHERE: {
-        // Generate the sphere model. Our input parameter to
-        // the sphere generation algorithm is the number of
-        // subdivisions of each triangle - but here we have
-        // the number of poylgons on a specific axis. Just
-        // use some hardcoded limits to approximate this ...
-        unsigned int mul = root->spherePolyCountX * root->spherePolyCountY;
-        if (mul < 100)
-            mul = 2;
-        else if (mul < 300)
-            mul = 3;
-        else
-            mul = 4;
-
-        meshes.push_back(StandardShapes::MakeMesh(mul,
-                &StandardShapes::MakeSphere));
-
-        // Adjust scaling
-        root->scaling *= root->sphereRadius / 2;
-
-        // Copy one output material
-        CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
-
-        // Now adjust this output material - if there is a first texture
-        // set, setup spherical UV mapping around the Y axis.
-        SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_SPHERE);
-    } break;
-
-    case Node::CUBE: {
-        // Generate an unit cube first
-        meshes.push_back(StandardShapes::MakeMesh(
-                &StandardShapes::MakeHexahedron));
-
-        // Adjust scaling
-        root->scaling *= root->sphereRadius;
-
-        // Copy one output material
-        CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
-
-        // Now adjust this output material - if there is a first texture
-        // set, setup cubic UV mapping
-        SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_BOX);
-    } break;
-
-    case Node::SKYBOX: {
-        // A skybox is defined by six materials
-        if (root->materials.size() < 6) {
-            ASSIMP_LOG_ERROR("IRR: There should be six materials for a skybox");
-            break;
-        }
-
-        // copy those materials and generate 6 meshes for our new skybox
-        materials.reserve(materials.size() + 6);
-        for (unsigned int i = 0; i < 6; ++i)
-            materials.insert(materials.end(), root->materials[i].first);
-
-        BuildSkybox(meshes, materials);
-
-        // *************************************************************
-        // Skyboxes will require a different code path for rendering,
-        // so there must be a way for the user to add special support
-        // for IRR skyboxes. We add a 'IRR.SkyBox_' prefix to the node.
-        // *************************************************************
-        root->name = "IRR.SkyBox_" + root->name;
-        ASSIMP_LOG_INFO("IRR: Loading skybox, this will "
-                        "require special handling to be displayed correctly");
-    } break;
-
-    case Node::TERRAIN: {
-        // to support terrains, we'd need to have a texture decoder
-        ASSIMP_LOG_ERROR("IRR: Unsupported node - TERRAIN");
-    } break;
-    default:
-        // DUMMY
-        break;
-    };
-
-    // Check whether we added a mesh (or more than one ...). In this case
-    // we'll also need to attach it to the node
-    if (oldMeshSize != (unsigned int)meshes.size()) {
-
-        rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
-        rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
-
-        for (unsigned int a = 0; a < rootOut->mNumMeshes; ++a) {
-            rootOut->mMeshes[a] = oldMeshSize + a;
-        }
-    }
-
-    // Setup the name of this node
-    rootOut->mName.Set(root->name);
-
-    // Now compute the final local transformation matrix of the
-    // node from the given translation, rotation and scaling values.
-    // (the rotation is given in Euler angles, XYZ order)
-    //std::swap((float&)root->rotation.z,(float&)root->rotation.y);
-    rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation));
-
-    // apply scaling
-    aiMatrix4x4 &mat = rootOut->mTransformation;
-    mat.a1 *= root->scaling.x;
-    mat.b1 *= root->scaling.x;
-    mat.c1 *= root->scaling.x;
-    mat.a2 *= root->scaling.y;
-    mat.b2 *= root->scaling.y;
-    mat.c2 *= root->scaling.y;
-    mat.a3 *= root->scaling.z;
-    mat.b3 *= root->scaling.z;
-    mat.c3 *= root->scaling.z;
-
-    // apply translation
-    mat.a4 += root->position.x;
-    mat.b4 += root->position.y;
-    mat.c4 += root->position.z;
-
-    // now compute animations for the node
-    ComputeAnimations(root, rootOut, anims);
-
-    // Add all children recursively. First allocate enough storage
-    // for them, then call us again
-    rootOut->mNumChildren = (unsigned int)root->children.size();
-    if (rootOut->mNumChildren) {
-
-        rootOut->mChildren = new aiNode *[rootOut->mNumChildren];
-        for (unsigned int i = 0; i < rootOut->mNumChildren; ++i) {
-
-            aiNode *node = rootOut->mChildren[i] = new aiNode();
-            node->mParent = rootOut;
-            GenerateGraph(root->children[i], node, scene, batch, meshes,
-                    anims, attach, materials, defMatIdx);
-        }
-    }
+		BatchLoader &batch,
+		std::vector<aiMesh *> &meshes,
+		std::vector<aiNodeAnim *> &anims,
+		std::vector<AttachmentInfo> &attach,
+		std::vector<aiMaterial *> &materials,
+		unsigned int &defMatIdx) {
+	unsigned int oldMeshSize = (unsigned int)meshes.size();
+	//unsigned int meshTrafoAssign = 0;
+
+	// Now determine the type of the node
+	switch (root->type) {
+		case Node::ANIMMESH:
+		case Node::MESH: {
+			if (!root->meshPath.length())
+				break;
+
+			// Get the loaded mesh from the scene and add it to
+			// the list of all scenes to be attached to the
+			// graph we're currently building
+			aiScene *localScene = batch.GetImport(root->id);
+			if (!localScene) {
+				ASSIMP_LOG_ERROR("IRR: Unable to load external file: " + root->meshPath);
+				break;
+			}
+			attach.push_back(AttachmentInfo(localScene, rootOut));
+
+			// Now combine the material we've loaded for this mesh
+			// with the real materials we got from the file. As we
+			// don't execute any pp-steps on the file, the numbers
+			// should be equal. If they are not, we can impossibly
+			// do this  ...
+			if (root->materials.size() != (unsigned int)localScene->mNumMaterials) {
+				ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
+								"with the materials found in the IRR scene file");
+
+				break;
+			}
+			for (unsigned int i = 0; i < localScene->mNumMaterials; ++i) {
+				// Delete the old material, we don't need it anymore
+				delete localScene->mMaterials[i];
+
+				std::pair<aiMaterial *, unsigned int> &src = root->materials[i];
+				localScene->mMaterials[i] = src.first;
+			}
+
+			// NOTE: Each mesh should have exactly one material assigned,
+			// but we do it in a separate loop if this behavior changes
+			// in future.
+			for (unsigned int i = 0; i < localScene->mNumMeshes; ++i) {
+				// Process material flags
+				aiMesh *mesh = localScene->mMeshes[i];
+
+				// If "trans_vertex_alpha" mode is enabled, search all vertex colors
+				// and check whether they have a common alpha value. This is quite
+				// often the case so we can simply extract it to a shared oacity
+				// value.
+				std::pair<aiMaterial *, unsigned int> &src = root->materials[mesh->mMaterialIndex];
+				aiMaterial *mat = (aiMaterial *)src.first;
+
+				if (mesh->HasVertexColors(0) && src.second & AI_IRRMESH_MAT_trans_vertex_alpha) {
+					bool bdo = true;
+					for (unsigned int a = 1; a < mesh->mNumVertices; ++a) {
+
+						if (mesh->mColors[0][a].a != mesh->mColors[0][a - 1].a) {
+							bdo = false;
+							break;
+						}
+					}
+					if (bdo) {
+						ASSIMP_LOG_INFO("IRR: Replacing mesh vertex alpha with common opacity");
+
+						for (unsigned int a = 0; a < mesh->mNumVertices; ++a)
+							mesh->mColors[0][a].a = 1.f;
+
+						mat->AddProperty(&mesh->mColors[0][0].a, 1, AI_MATKEY_OPACITY);
+					}
+				}
+
+				// If we have a second texture coordinate set and a second texture
+				// (either light-map, normal-map, 2layered material) we need to
+				// setup the correct UV index for it. The texture can either
+				// be diffuse (light-map & 2layer) or a normal map (normal & parallax)
+				if (mesh->HasTextureCoords(1)) {
+
+					int idx = 1;
+					if (src.second & (AI_IRRMESH_MAT_solid_2layer | AI_IRRMESH_MAT_lightmap)) {
+						mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(0));
+					} else if (src.second & AI_IRRMESH_MAT_normalmap_solid) {
+						mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0));
+					}
+				}
+			}
+		} break;
+
+		case Node::LIGHT:
+		case Node::CAMERA:
+
+			// We're already finished with lights and cameras
+			break;
+
+		case Node::SPHERE: {
+			// Generate the sphere model. Our input parameter to
+			// the sphere generation algorithm is the number of
+			// subdivisions of each triangle - but here we have
+			// the number of polygons on a specific axis. Just
+			// use some hard-coded limits to approximate this ...
+			unsigned int mul = root->spherePolyCountX * root->spherePolyCountY;
+			if (mul < 100)
+				mul = 2;
+			else if (mul < 300)
+				mul = 3;
+			else
+				mul = 4;
+
+			meshes.push_back(StandardShapes::MakeMesh(mul,
+					&StandardShapes::MakeSphere));
+
+			// Adjust scaling
+			root->scaling *= root->sphereRadius / 2;
+
+			// Copy one output material
+			CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+			// Now adjust this output material - if there is a first texture
+			// set, setup spherical UV mapping around the Y axis.
+			SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_SPHERE);
+		} break;
+
+		case Node::CUBE: {
+			// Generate an unit cube first
+			meshes.push_back(StandardShapes::MakeMesh(
+					&StandardShapes::MakeHexahedron));
+
+			// Adjust scaling
+			root->scaling *= root->sphereRadius;
+
+			// Copy one output material
+			CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+			// Now adjust this output material - if there is a first texture
+			// set, setup cubic UV mapping
+			SetupMapping((aiMaterial *)materials.back(), aiTextureMapping_BOX);
+		} break;
+
+		case Node::SKYBOX: {
+			// A sky-box is defined by six materials
+			if (root->materials.size() < 6) {
+				ASSIMP_LOG_ERROR("IRR: There should be six materials for a skybox");
+				break;
+			}
+
+			// copy those materials and generate 6 meshes for our new sky-box
+			materials.reserve(materials.size() + 6);
+			for (unsigned int i = 0; i < 6; ++i)
+				materials.insert(materials.end(), root->materials[i].first);
+
+			BuildSkybox(meshes, materials);
+
+			// *************************************************************
+			// Skyboxes will require a different code path for rendering,
+			// so there must be a way for the user to add special support
+			// for IRR skyboxes. We add a 'IRR.SkyBox_' prefix to the node.
+			// *************************************************************
+			root->name = "IRR.SkyBox_" + root->name;
+			ASSIMP_LOG_INFO("IRR: Loading skybox, this will "
+							"require special handling to be displayed correctly");
+		} break;
+
+		case Node::TERRAIN: {
+			// to support terrains, we'd need to have a texture decoder
+			ASSIMP_LOG_ERROR("IRR: Unsupported node - TERRAIN");
+		} break;
+		default:
+			// DUMMY
+			break;
+	};
+
+	// Check whether we added a mesh (or more than one ...). In this case
+	// we'll also need to attach it to the node
+	if (oldMeshSize != (unsigned int)meshes.size()) {
+
+		rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
+		rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
+
+		for (unsigned int a = 0; a < rootOut->mNumMeshes; ++a) {
+			rootOut->mMeshes[a] = oldMeshSize + a;
+		}
+	}
+
+	// Setup the name of this node
+	rootOut->mName.Set(root->name);
+
+	// Now compute the final local transformation matrix of the
+	// node from the given translation, rotation and scaling values.
+	// (the rotation is given in Euler angles, XYZ order)
+	//std::swap((float&)root->rotation.z,(float&)root->rotation.y);
+	rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation));
+
+	// apply scaling
+	aiMatrix4x4 &mat = rootOut->mTransformation;
+	mat.a1 *= root->scaling.x;
+	mat.b1 *= root->scaling.x;
+	mat.c1 *= root->scaling.x;
+	mat.a2 *= root->scaling.y;
+	mat.b2 *= root->scaling.y;
+	mat.c2 *= root->scaling.y;
+	mat.a3 *= root->scaling.z;
+	mat.b3 *= root->scaling.z;
+	mat.c3 *= root->scaling.z;
+
+	// apply translation
+	mat.a4 += root->position.x;
+	mat.b4 += root->position.y;
+	mat.c4 += root->position.z;
+
+	// now compute animations for the node
+	ComputeAnimations(root, rootOut, anims);
+
+	// Add all children recursively. First allocate enough storage
+	// for them, then call us again
+	rootOut->mNumChildren = (unsigned int)root->children.size();
+	if (rootOut->mNumChildren) {
+
+		rootOut->mChildren = new aiNode *[rootOut->mNumChildren];
+		for (unsigned int i = 0; i < rootOut->mNumChildren; ++i) {
+
+			aiNode *node = rootOut->mChildren[i] = new aiNode();
+			node->mParent = rootOut;
+			GenerateGraph(root->children[i], node, scene, batch, meshes,
+					anims, attach, materials, defMatIdx);
+		}
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void IRRImporter::InternReadFile(const std::string &pFile,
-        aiScene *pScene, IOSystem *pIOHandler) {
-    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+void IRRImporter::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 IRR file " + pFile + "");
-    }
+	// Check whether we can read from the file
+	if (file.get() == nullptr) {
+		throw DeadlyImportError("Failed to open IRR file " + pFile + "");
+	}
 
-    // Construct the irrXML parser
-    CIrrXML_IOStreamReader st(file.get());
-    reader = createIrrXMLReader((IFileReadCallBack *)&st);
-
-    // The root node of the scene
-    Node *root = new Node(Node::DUMMY);
-    root->parent = nullptr;
-    root->name = "<IRRSceneRoot>";
-
-    // Current node parent
-    Node *curParent = root;
-
-    // Scenegraph node we're currently working on
-    Node *curNode = nullptr;
-
-    // List of output cameras
-    std::vector<aiCamera *> cameras;
-
-    // List of output lights
-    std::vector<aiLight *> lights;
-
-    // Batch loader used to load external models
-    BatchLoader batch(pIOHandler);
-    //  batch.SetBasePath(pFile);
-
-    cameras.reserve(5);
-    lights.reserve(5);
-
-    bool inMaterials = false, inAnimator = false;
-    unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
-
-    // Parse the XML file
-    while (reader->read()) {
-        switch (reader->getNodeType()) {
-        case EXN_ELEMENT:
-
-            if (!ASSIMP_stricmp(reader->getNodeName(), "node")) {
-                // ***********************************************************************
-                /*  What we're going to do with the node depends
-                 *  on its type:
-                 *
-                 *  "mesh" - Load a mesh from an external file
-                 *  "cube" - Generate a cube
-                 *  "skybox" - Generate a skybox
-                 *  "light" - A light source
-                 *  "sphere" - Generate a sphere mesh
-                 *  "animatedMesh" - Load an animated mesh from an external file
-                 *    and join its animation channels with ours.
-                 *  "empty" - A dummy node
-                 *  "camera" - A camera
-                 *  "terrain" - a terrain node (data comes from a heightmap)
-                 *  "billboard", ""
-                 *
-                 *  Each of these nodes can be animated and all can have multiple
-                 *  materials assigned (except lights, cameras and dummies, of course).
-                 */
-                // ***********************************************************************
-                const char *sz = reader->getAttributeValueSafe("type");
-                Node *nd;
-                if (!ASSIMP_stricmp(sz, "mesh") || !ASSIMP_stricmp(sz, "octTree")) {
-                    // OctTree's and meshes are treated equally
-                    nd = new Node(Node::MESH);
-                } else if (!ASSIMP_stricmp(sz, "cube")) {
-                    nd = new Node(Node::CUBE);
-                    ++guessedMeshCnt;
-                    // meshes.push_back(StandardShapes::MakeMesh(&StandardShapes::MakeHexahedron));
-                } else if (!ASSIMP_stricmp(sz, "skybox")) {
-                    nd = new Node(Node::SKYBOX);
-                    guessedMeshCnt += 6;
-                } else if (!ASSIMP_stricmp(sz, "camera")) {
-                    nd = new Node(Node::CAMERA);
-
-                    // Setup a temporary name for the camera
-                    aiCamera *cam = new aiCamera();
-                    cam->mName.Set(nd->name);
-                    cameras.push_back(cam);
-                } else if (!ASSIMP_stricmp(sz, "light")) {
-                    nd = new Node(Node::LIGHT);
-
-                    // Setup a temporary name for the light
-                    aiLight *cam = new aiLight();
-                    cam->mName.Set(nd->name);
-                    lights.push_back(cam);
-                } else if (!ASSIMP_stricmp(sz, "sphere")) {
-                    nd = new Node(Node::SPHERE);
-                    ++guessedMeshCnt;
-                } else if (!ASSIMP_stricmp(sz, "animatedMesh")) {
-                    nd = new Node(Node::ANIMMESH);
-                } else if (!ASSIMP_stricmp(sz, "empty")) {
-                    nd = new Node(Node::DUMMY);
-                } else if (!ASSIMP_stricmp(sz, "terrain")) {
-                    nd = new Node(Node::TERRAIN);
-                } else if (!ASSIMP_stricmp(sz, "billBoard")) {
-                    // We don't support billboards, so ignore them
-                    ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
-                    nd = new Node(Node::DUMMY);
-                } else {
-                    ASSIMP_LOG_WARN("IRR: Found unknown node: " + std::string(sz));
-
-                    /*  We skip the contents of nodes we don't know.
-                     *  We parse the transformation and all animators
-                     *  and skip the rest.
+	// Construct the irrXML parser
+	XmlParser st;
+    if (!st.parse( file.get() )) {
+        return;
+    }
+    pugi::xml_node rootElement = st.getRootNode();
+
+	// The root node of the scene
+	Node *root = new Node(Node::DUMMY);
+	root->parent = nullptr;
+	root->name = "<IRRSceneRoot>";
+
+	// Current node parent
+	Node *curParent = root;
+
+	// Scene-graph node we're currently working on
+	Node *curNode = nullptr;
+
+	// List of output cameras
+	std::vector<aiCamera *> cameras;
+
+	// List of output lights
+	std::vector<aiLight *> lights;
+
+	// Batch loader used to load external models
+	BatchLoader batch(pIOHandler);
+	//  batch.SetBasePath(pFile);
+
+	cameras.reserve(5);
+	lights.reserve(5);
+
+	bool inMaterials = false, inAnimator = false;
+	unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
+
+	// Parse the XML file
+
+	//while (reader->read())  {
+	for (pugi::xml_node child : rootElement.children())
+		switch (child.type()) {
+			case pugi::node_element:
+				if (!ASSIMP_stricmp(child.name(), "node")) {
+					// ***********************************************************************
+					/*  What we're going to do with the node depends
+                     *  on its type:
+                     *
+                     *  "mesh" - Load a mesh from an external file
+                     *  "cube" - Generate a cube
+                     *  "skybox" - Generate a skybox
+                     *  "light" - A light source
+                     *  "sphere" - Generate a sphere mesh
+                     *  "animatedMesh" - Load an animated mesh from an external file
+                     *    and join its animation channels with ours.
+                     *  "empty" - A dummy node
+                     *  "camera" - A camera
+                     *  "terrain" - a terrain node (data comes from a heightmap)
+                     *  "billboard", ""
+                     *
+                     *  Each of these nodes can be animated and all can have multiple
+                     *  materials assigned (except lights, cameras and dummies, of course).
                      */
-                    nd = new Node(Node::DUMMY);
-                }
-
-                /* Attach the newly created node to the scenegraph
-                 */
-                curNode = nd;
-                nd->parent = curParent;
-                curParent->children.push_back(nd);
-            } else if (!ASSIMP_stricmp(reader->getNodeName(), "materials")) {
-                inMaterials = true;
-            } else if (!ASSIMP_stricmp(reader->getNodeName(), "animators")) {
-                inAnimator = true;
-            } else if (!ASSIMP_stricmp(reader->getNodeName(), "attributes")) {
-                /*  We should have a valid node here
-                 *  FIX: no ... the scene root node is also contained in an attributes block
-                 */
-                if (!curNode) {
-#if 0
-                    ASSIMP_LOG_ERROR("IRR: Encountered <attributes> element, but "
-                        "there is no node active");
-#endif
-                    continue;
-                }
-
-                Animator *curAnim = nullptr;
-
-                // Materials can occur for nearly any type of node
-                if (inMaterials && curNode->type != Node::DUMMY) {
-                    /*  This is a material description - parse it!
+					// ***********************************************************************
+					//const char *sz = reader->getAttributeValueSafe("type");
+					pugi::xml_attribute attrib = child.attribute("type");
+					Node *nd;
+					if (!ASSIMP_stricmp(attrib.name(), "mesh") || !ASSIMP_stricmp(attrib.name(), "octTree")) {
+						// OctTree's and meshes are treated equally
+						nd = new Node(Node::MESH);
+					} else if (!ASSIMP_stricmp(attrib.name(), "cube")) {
+						nd = new Node(Node::CUBE);
+						++guessedMeshCnt;
+					} else if (!ASSIMP_stricmp(attrib.name(), "skybox")) {
+						nd = new Node(Node::SKYBOX);
+						guessedMeshCnt += 6;
+					} else if (!ASSIMP_stricmp(attrib.name(), "camera")) {
+						nd = new Node(Node::CAMERA);
+
+						// Setup a temporary name for the camera
+						aiCamera *cam = new aiCamera();
+						cam->mName.Set(nd->name);
+						cameras.push_back(cam);
+					} else if (!ASSIMP_stricmp(attrib.name(), "light")) {
+						nd = new Node(Node::LIGHT);
+
+						// Setup a temporary name for the light
+						aiLight *cam = new aiLight();
+						cam->mName.Set(nd->name);
+						lights.push_back(cam);
+					} else if (!ASSIMP_stricmp(attrib.name(), "sphere")) {
+						nd = new Node(Node::SPHERE);
+						++guessedMeshCnt;
+					} else if (!ASSIMP_stricmp(attrib.name(), "animatedMesh")) {
+						nd = new Node(Node::ANIMMESH);
+					} else if (!ASSIMP_stricmp(attrib.name(), "empty")) {
+						nd = new Node(Node::DUMMY);
+					} else if (!ASSIMP_stricmp(attrib.name(), "terrain")) {
+						nd = new Node(Node::TERRAIN);
+					} else if (!ASSIMP_stricmp(attrib.name(), "billBoard")) {
+						// We don't support billboards, so ignore them
+						ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
+						nd = new Node(Node::DUMMY);
+					} else {
+						ASSIMP_LOG_WARN("IRR: Found unknown node: " + std::string(attrib.name()));
+
+						/*  We skip the contents of nodes we don't know.
+                         *  We parse the transformation and all animators
+                         *  and skip the rest.
+                         */
+						nd = new Node(Node::DUMMY);
+					}
+
+					/* Attach the newly created node to the scene-graph
                      */
-                    curNode->materials.push_back(std::pair<aiMaterial *, unsigned int>());
-                    std::pair<aiMaterial *, unsigned int> &p = curNode->materials.back();
-
-                    p.first = ParseMaterial(p.second);
-
-                    ++guessedMatCnt;
-                    continue;
-                } else if (inAnimator) {
-                    /*  This is an animation path - add a new animator
-                     *  to the list.
+					curNode = nd;
+					nd->parent = curParent;
+					curParent->children.push_back(nd);
+				} else if (!ASSIMP_stricmp(child.name(), "materials")) {
+					inMaterials = true;
+				} else if (!ASSIMP_stricmp(child.name(), "animators")) {
+					inAnimator = true;
+				} else if (!ASSIMP_stricmp(child.name(), "attributes")) {
+					//  We should have a valid node here
+					//  FIX: no ... the scene root node is also contained in an attributes block
+					if (!curNode) {
+						continue;
+					}
+
+					Animator *curAnim = nullptr;
+
+					// Materials can occur for nearly any type of node
+					if (inMaterials && curNode->type != Node::DUMMY) {
+						//  This is a material description - parse it!
+						curNode->materials.push_back(std::pair<aiMaterial *, unsigned int>());
+						std::pair<aiMaterial *, unsigned int> &p = curNode->materials.back();
+
+						p.first = ParseMaterial(p.second);
+						++guessedMatCnt;
+						continue;
+					} else if (inAnimator) {
+						//  This is an animation path - add a new animator
+						//  to the list.
+						curNode->animators.push_back(Animator());
+						curAnim = &curNode->animators.back();
+
+						++guessedAnimCnt;
+					}
+
+					/*  Parse all elements in the attributes block
+                     *  and process them.
                      */
-                    curNode->animators.push_back(Animator());
-                    curAnim = &curNode->animators.back();
-
-                    ++guessedAnimCnt;
-                }
-
-                /*  Parse all elements in the attributes block
-                 *  and process them.
-                 */
-                while (reader->read()) {
-                    if (reader->getNodeType() == EXN_ELEMENT) {
-                        if (!ASSIMP_stricmp(reader->getNodeName(), "vector3d")) {
-                            VectorProperty prop;
-                            ReadVectorProperty(prop);
-
-                            if (inAnimator) {
-                                if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
-                                    // We store the rotation euler angles in 'direction'
-                                    curAnim->direction = prop.value;
-                                } else if (curAnim->type == Animator::FOLLOW_SPLINE) {
-                                    // Check whether the vector follows the PointN naming scheme,
-                                    // here N is the ONE-based index of the point
-                                    if (prop.name.length() >= 6 && prop.name.substr(0, 5) == "Point") {
-                                        // Add a new key to the list
-                                        curAnim->splineKeys.push_back(aiVectorKey());
-                                        aiVectorKey &key = curAnim->splineKeys.back();
-
-                                        // and parse its properties
-                                        key.mValue = prop.value;
-                                        key.mTime = strtoul10(&prop.name[5]);
-                                    }
-                                } else if (curAnim->type == Animator::FLY_CIRCLE) {
-                                    if (prop.name == "Center") {
-                                        curAnim->circleCenter = prop.value;
-                                    } else if (prop.name == "Direction") {
-                                        curAnim->direction = prop.value;
-
-                                        // From Irrlicht's source - a workaround for backward compatibility with Irrlicht 1.1
-                                        if (curAnim->direction == aiVector3D()) {
-                                            curAnim->direction = aiVector3D(0.f, 1.f, 0.f);
-                                        } else
-                                            curAnim->direction.Normalize();
-                                    }
-                                } else if (curAnim->type == Animator::FLY_STRAIGHT) {
-                                    if (prop.name == "Start") {
-                                        // We reuse the field here
-                                        curAnim->circleCenter = prop.value;
-                                    } else if (prop.name == "End") {
-                                        // We reuse the field here
-                                        curAnim->direction = prop.value;
-                                    }
-                                }
-                            } else {
-                                if (prop.name == "Position") {
-                                    curNode->position = prop.value;
-                                } else if (prop.name == "Rotation") {
-                                    curNode->rotation = prop.value;
-                                } else if (prop.name == "Scale") {
-                                    curNode->scaling = prop.value;
-                                } else if (Node::CAMERA == curNode->type) {
-                                    aiCamera *cam = cameras.back();
-                                    if (prop.name == "Target") {
-                                        cam->mLookAt = prop.value;
-                                    } else if (prop.name == "UpVector") {
-                                        cam->mUp = prop.value;
-                                    }
-                                }
-                            }
-                        } else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
-                            BoolProperty prop;
-                            ReadBoolProperty(prop);
-
-                            if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
-                                curAnim->loop = prop.value;
-                            }
-                        } else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
-                            FloatProperty prop;
-                            ReadFloatProperty(prop);
-
-                            if (inAnimator) {
-                                // The speed property exists for several animators
-                                if (prop.name == "Speed") {
-                                    curAnim->speed = prop.value;
-                                } else if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Radius") {
-                                    curAnim->circleRadius = prop.value;
-                                } else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
-                                    curAnim->tightness = prop.value;
-                                }
-                            } else {
-                                if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
-                                    curNode->framesPerSecond = prop.value;
-                                } else if (Node::CAMERA == curNode->type) {
-                                    /*  This is the vertical, not the horizontal FOV.
+					//					while (reader->read()) {
+					for (pugi::xml_node attrib : child.children()) {
+						if (attrib.type() == pugi::node_element) {
+							//if (reader->getNodeType() == EXN_ELEMENT) {
+							//if (!ASSIMP_stricmp(reader->getNodeName(), "vector3d")) {
+							if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
+								VectorProperty prop;
+								ReadVectorProperty(prop);
+
+								if (inAnimator) {
+									if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
+										// We store the rotation euler angles in 'direction'
+										curAnim->direction = prop.value;
+									} else if (curAnim->type == Animator::FOLLOW_SPLINE) {
+										// Check whether the vector follows the PointN naming scheme,
+										// here N is the ONE-based index of the point
+										if (prop.name.length() >= 6 && prop.name.substr(0, 5) == "Point") {
+											// Add a new key to the list
+											curAnim->splineKeys.push_back(aiVectorKey());
+											aiVectorKey &key = curAnim->splineKeys.back();
+
+											// and parse its properties
+											key.mValue = prop.value;
+											key.mTime = strtoul10(&prop.name[5]);
+										}
+									} else if (curAnim->type == Animator::FLY_CIRCLE) {
+										if (prop.name == "Center") {
+											curAnim->circleCenter = prop.value;
+										} else if (prop.name == "Direction") {
+											curAnim->direction = prop.value;
+
+											// From Irrlicht's source - a workaround for backward compatibility with Irrlicht 1.1
+											if (curAnim->direction == aiVector3D()) {
+												curAnim->direction = aiVector3D(0.f, 1.f, 0.f);
+											} else
+												curAnim->direction.Normalize();
+										}
+									} else if (curAnim->type == Animator::FLY_STRAIGHT) {
+										if (prop.name == "Start") {
+											// We reuse the field here
+											curAnim->circleCenter = prop.value;
+										} else if (prop.name == "End") {
+											// We reuse the field here
+											curAnim->direction = prop.value;
+										}
+									}
+								} else {
+									if (prop.name == "Position") {
+										curNode->position = prop.value;
+									} else if (prop.name == "Rotation") {
+										curNode->rotation = prop.value;
+									} else if (prop.name == "Scale") {
+										curNode->scaling = prop.value;
+									} else if (Node::CAMERA == curNode->type) {
+										aiCamera *cam = cameras.back();
+										if (prop.name == "Target") {
+											cam->mLookAt = prop.value;
+										} else if (prop.name == "UpVector") {
+											cam->mUp = prop.value;
+										}
+									}
+								}
+								//} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
+							} else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
+								BoolProperty prop;
+								ReadBoolProperty(prop);
+
+								if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
+									curAnim->loop = prop.value;
+								}
+								//} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
+							} else if (!ASSIMP_stricmp(attrib.name(), "float")) {
+								FloatProperty prop;
+								ReadFloatProperty(prop);
+
+								if (inAnimator) {
+									// The speed property exists for several animators
+									if (prop.name == "Speed") {
+										curAnim->speed = prop.value;
+									} else if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Radius") {
+										curAnim->circleRadius = prop.value;
+									} else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
+										curAnim->tightness = prop.value;
+									}
+								} else {
+									if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
+										curNode->framesPerSecond = prop.value;
+									} else if (Node::CAMERA == curNode->type) {
+										/*  This is the vertical, not the horizontal FOV.
                                     *  We need to compute the right FOV from the
                                     *  screen aspect which we don't know yet.
                                     */
-                                    if (prop.name == "Fovy") {
-                                        cameras.back()->mHorizontalFOV = prop.value;
-                                    } else if (prop.name == "Aspect") {
-                                        cameras.back()->mAspect = prop.value;
-                                    } else if (prop.name == "ZNear") {
-                                        cameras.back()->mClipPlaneNear = prop.value;
-                                    } else if (prop.name == "ZFar") {
-                                        cameras.back()->mClipPlaneFar = prop.value;
-                                    }
-                                } else if (Node::LIGHT == curNode->type) {
-                                    /*  Additional light information
+										if (prop.name == "Fovy") {
+											cameras.back()->mHorizontalFOV = prop.value;
+										} else if (prop.name == "Aspect") {
+											cameras.back()->mAspect = prop.value;
+										} else if (prop.name == "ZNear") {
+											cameras.back()->mClipPlaneNear = prop.value;
+										} else if (prop.name == "ZFar") {
+											cameras.back()->mClipPlaneFar = prop.value;
+										}
+									} else if (Node::LIGHT == curNode->type) {
+										/*  Additional light information
                                      */
-                                    if (prop.name == "Attenuation") {
-                                        lights.back()->mAttenuationLinear = prop.value;
-                                    } else if (prop.name == "OuterCone") {
-                                        lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
-                                    } else if (prop.name == "InnerCone") {
-                                        lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
-                                    }
-                                }
-                                // radius of the sphere to be generated -
-                                // or alternatively, size of the cube
-                                else if ((Node::SPHERE == curNode->type && prop.name == "Radius") || (Node::CUBE == curNode->type && prop.name == "Size")) {
-
-                                    curNode->sphereRadius = prop.value;
-                                }
-                            }
-                        } else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
-                            IntProperty prop;
-                            ReadIntProperty(prop);
-
-                            if (inAnimator) {
-                                if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
-                                    curAnim->timeForWay = prop.value;
-                                }
-                            } else {
-                                // sphere polgon numbers in each direction
-                                if (Node::SPHERE == curNode->type) {
-
-                                    if (prop.name == "PolyCountX") {
-                                        curNode->spherePolyCountX = prop.value;
-                                    } else if (prop.name == "PolyCountY") {
-                                        curNode->spherePolyCountY = prop.value;
-                                    }
-                                }
-                            }
-                        } else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
-                            StringProperty prop;
-                            ReadStringProperty(prop);
-                            if (prop.value.length()) {
-                                if (prop.name == "Name") {
-                                    curNode->name = prop.value;
-
-                                    /*  If we're either a camera or a light source
+										if (prop.name == "Attenuation") {
+											lights.back()->mAttenuationLinear = prop.value;
+										} else if (prop.name == "OuterCone") {
+											lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
+										} else if (prop.name == "InnerCone") {
+											lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
+										}
+									}
+									// radius of the sphere to be generated -
+									// or alternatively, size of the cube
+									else if ((Node::SPHERE == curNode->type && prop.name == "Radius") || (Node::CUBE == curNode->type && prop.name == "Size")) {
+
+										curNode->sphereRadius = prop.value;
+									}
+								}
+								//} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
+							} else if (!ASSIMP_stricmp(attrib.name(), "int")) {
+								IntProperty prop;
+								ReadIntProperty(prop);
+
+								if (inAnimator) {
+									if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
+										curAnim->timeForWay = prop.value;
+									}
+								} else {
+									// sphere polygon numbers in each direction
+									if (Node::SPHERE == curNode->type) {
+
+										if (prop.name == "PolyCountX") {
+											curNode->spherePolyCountX = prop.value;
+										} else if (prop.name == "PolyCountY") {
+											curNode->spherePolyCountY = prop.value;
+										}
+									}
+								}
+								//} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
+							} else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
+								StringProperty prop;
+								ReadStringProperty(prop);
+								if (prop.value.length()) {
+									if (prop.name == "Name") {
+										curNode->name = prop.value;
+
+										/*  If we're either a camera or a light source
                                      *  we need to update the name in the aiLight/
                                      *  aiCamera structure, too.
                                      */
-                                    if (Node::CAMERA == curNode->type) {
-                                        cameras.back()->mName.Set(prop.value);
-                                    } else if (Node::LIGHT == curNode->type) {
-                                        lights.back()->mName.Set(prop.value);
-                                    }
-                                } else if (Node::LIGHT == curNode->type && "LightType" == prop.name) {
-                                    if (prop.value == "Spot")
-                                        lights.back()->mType = aiLightSource_SPOT;
-                                    else if (prop.value == "Point")
-                                        lights.back()->mType = aiLightSource_POINT;
-                                    else if (prop.value == "Directional")
-                                        lights.back()->mType = aiLightSource_DIRECTIONAL;
-                                    else {
-                                        // We won't pass the validation with aiLightSourceType_UNDEFINED,
-                                        // so we remove the light and replace it with a silly dummy node
-                                        delete lights.back();
-                                        lights.pop_back();
-                                        curNode->type = Node::DUMMY;
-
-                                        ASSIMP_LOG_ERROR("Ignoring light of unknown type: " + prop.value);
-                                    }
-                                } else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
-                                           Node::ANIMMESH == curNode->type) {
-                                    /*  This is the file name of the mesh - either
+										if (Node::CAMERA == curNode->type) {
+											cameras.back()->mName.Set(prop.value);
+										} else if (Node::LIGHT == curNode->type) {
+											lights.back()->mName.Set(prop.value);
+										}
+									} else if (Node::LIGHT == curNode->type && "LightType" == prop.name) {
+										if (prop.value == "Spot")
+											lights.back()->mType = aiLightSource_SPOT;
+										else if (prop.value == "Point")
+											lights.back()->mType = aiLightSource_POINT;
+										else if (prop.value == "Directional")
+											lights.back()->mType = aiLightSource_DIRECTIONAL;
+										else {
+											// We won't pass the validation with aiLightSourceType_UNDEFINED,
+											// so we remove the light and replace it with a silly dummy node
+											delete lights.back();
+											lights.pop_back();
+											curNode->type = Node::DUMMY;
+
+											ASSIMP_LOG_ERROR("Ignoring light of unknown type: " + prop.value);
+										}
+									} else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
+											   Node::ANIMMESH == curNode->type) {
+    								/*  This is the file name of the mesh - either
                                      *  animated or not. We need to make sure we setup
                                      *  the correct post-processing settings here.
                                      */
-                                    unsigned int pp = 0;
-                                    BatchLoader::PropertyMap map;
+										unsigned int pp = 0;
+										BatchLoader::PropertyMap map;
 
-                                    /* If the mesh is a static one remove all animations from the impor data
+										/* If the mesh is a static one remove all animations from the impor data
                                      */
-                                    if (Node::ANIMMESH != curNode->type) {
-                                        pp |= aiProcess_RemoveComponent;
-                                        SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
-                                                aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
-                                    }
-
-                                    /*  TODO: maybe implement the protection against recursive
-                                    *  loading calls directly in BatchLoader? The current
-                                    *  implementation is not absolutely safe. A LWS and an IRR
-                                    *  file referencing each other *could* cause the system to
-                                    *  recurse forever.
-                                    */
-
-                                    const std::string extension = GetExtension(prop.value);
-                                    if ("irr" == extension) {
-                                        ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
-                                    } else {
-                                        curNode->id = batch.AddLoadRequest(prop.value, pp, &map);
-                                        curNode->meshPath = prop.value;
-                                    }
-                                } else if (inAnimator && prop.name == "Type") {
-                                    // type of the animator
-                                    if (prop.value == "rotation") {
-                                        curAnim->type = Animator::ROTATION;
-                                    } else if (prop.value == "flyCircle") {
-                                        curAnim->type = Animator::FLY_CIRCLE;
-                                    } else if (prop.value == "flyStraight") {
-                                        curAnim->type = Animator::FLY_CIRCLE;
-                                    } else if (prop.value == "followSpline") {
-                                        curAnim->type = Animator::FOLLOW_SPLINE;
-                                    } else {
-                                        ASSIMP_LOG_WARN("IRR: Ignoring unknown animator: " + prop.value);
-
-                                        curAnim->type = Animator::UNKNOWN;
-                                    }
-                                }
-                            }
-                        }
-                    } else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(), "attributes")) {
-                        break;
-                    }
-                }
-            }
-            break;
-
-        case EXN_ELEMENT_END:
-
-            // If we reached the end of a node, we need to continue processing its parent
-            if (!ASSIMP_stricmp(reader->getNodeName(), "node")) {
-                if (!curNode) {
-                    // currently is no node set. We need to go
-                    // back in the node hierarchy
-                    if (!curParent) {
-                        curParent = root;
-                        ASSIMP_LOG_ERROR("IRR: Too many closing <node> elements");
-                    } else
-                        curParent = curParent->parent;
-                } else
-                    curNode = nullptr;
-            }
-            // clear all flags
-            else if (!ASSIMP_stricmp(reader->getNodeName(), "materials")) {
-                inMaterials = false;
-            } else if (!ASSIMP_stricmp(reader->getNodeName(), "animators")) {
-                inAnimator = false;
-            }
-            break;
-
-        default:
-            // GCC complains that not all enumeration values are handled
-            break;
-        }
-    }
-
-    //  Now iterate through all cameras and compute their final (horizontal) FOV
-    for (aiCamera *cam : cameras) {
-        // screen aspect could be missing
-        if (cam->mAspect) {
-            cam->mHorizontalFOV *= cam->mAspect;
-        } else {
-            ASSIMP_LOG_WARN("IRR: Camera aspect is not given, can't compute horizontal FOV");
-        }
-    }
-
-    batch.LoadAll();
-
-    /* Allocate a tempoary scene data structure
-     */
-    aiScene *tempScene = new aiScene();
-    tempScene->mRootNode = new aiNode();
-    tempScene->mRootNode->mName.Set("<IRRRoot>");
-
-    /* Copy the cameras to the output array
-     */
-    if (!cameras.empty()) {
-        tempScene->mNumCameras = (unsigned int)cameras.size();
-        tempScene->mCameras = new aiCamera *[tempScene->mNumCameras];
-        ::memcpy(tempScene->mCameras, &cameras[0], sizeof(void *) * tempScene->mNumCameras);
-    }
-
-    /* Copy the light sources to the output array
-     */
-    if (!lights.empty()) {
-        tempScene->mNumLights = (unsigned int)lights.size();
-        tempScene->mLights = new aiLight *[tempScene->mNumLights];
-        ::memcpy(tempScene->mLights, &lights[0], sizeof(void *) * tempScene->mNumLights);
-    }
-
-    // temporary data
-    std::vector<aiNodeAnim *> anims;
-    std::vector<aiMaterial *> materials;
-    std::vector<AttachmentInfo> attach;
-    std::vector<aiMesh *> meshes;
-
-    // try to guess how much storage we'll need
-    anims.reserve(guessedAnimCnt + (guessedAnimCnt >> 2));
-    meshes.reserve(guessedMeshCnt + (guessedMeshCnt >> 2));
-    materials.reserve(guessedMatCnt + (guessedMatCnt >> 2));
-
-    /* Now process our scenegraph recursively: generate final
-     * meshes and generate animation channels for all nodes.
-     */
-    unsigned int defMatIdx = UINT_MAX;
-    GenerateGraph(root, tempScene->mRootNode, tempScene,
-            batch, meshes, anims, attach, materials, defMatIdx);
-
-    if (!anims.empty()) {
-        tempScene->mNumAnimations = 1;
-        tempScene->mAnimations = new aiAnimation *[tempScene->mNumAnimations];
-        aiAnimation *an = tempScene->mAnimations[0] = new aiAnimation();
-
-        // ***********************************************************
-        // This is only the global animation channel of the scene.
-        // If there are animated models, they will have separate
-        // animation channels in the scene. To display IRR scenes
-        // correctly, users will need to combine the global anim
-        // channel with all the local animations they want to play
-        // ***********************************************************
-        an->mName.Set("Irr_GlobalAnimChannel");
-
-        // copy all node animation channels to the global channel
-        an->mNumChannels = (unsigned int)anims.size();
-        an->mChannels = new aiNodeAnim *[an->mNumChannels];
-        ::memcpy(an->mChannels, &anims[0], sizeof(void *) * an->mNumChannels);
-    }
-    if (!meshes.empty()) {
-        // copy all meshes to the temporary scene
-        tempScene->mNumMeshes = (unsigned int)meshes.size();
-        tempScene->mMeshes = new aiMesh *[tempScene->mNumMeshes];
-        ::memcpy(tempScene->mMeshes, &meshes[0], tempScene->mNumMeshes * sizeof(void *));
-    }
-
-    /* Copy all materials to the output array
-     */
-    if (!materials.empty()) {
-        tempScene->mNumMaterials = (unsigned int)materials.size();
-        tempScene->mMaterials = new aiMaterial *[tempScene->mNumMaterials];
-        ::memcpy(tempScene->mMaterials, &materials[0], sizeof(void *) * tempScene->mNumMaterials);
-    }
-
-    /*  Now merge all sub scenes and attach them to the correct
-     *  attachment points in the scenegraph.
-     */
-    SceneCombiner::MergeScenes(&pScene, tempScene, attach,
-            AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
-                                                                              AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
-                                                                      0));
-
-    /*  If we have no meshes | no materials now set the INCOMPLETE
-     *  scene flag. This is necessary if we failed to load all
-     *  models from external files
-     */
-    if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
-        ASSIMP_LOG_WARN("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
-        pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
-    }
-
-    /* Finished ... everything destructs automatically and all
-     * temporary scenes have already been deleted by MergeScenes()
-     */
-
-    delete root;
-    delete reader;
+										if (Node::ANIMMESH != curNode->type) {
+											pp |= aiProcess_RemoveComponent;
+											SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
+													aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
+										}
+
+										/*  TODO: maybe implement the protection against recursive
+                                        *  loading calls directly in BatchLoader? The current
+                                        *  implementation is not absolutely safe. A LWS and an IRR
+                                        *  file referencing each other *could* cause the system to
+                                        *  recurse forever.
+                                        */
+
+										const std::string extension = GetExtension(prop.value);
+										if ("irr" == extension) {
+											ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
+										} else {
+											curNode->id = batch.AddLoadRequest(prop.value, pp, &map);
+											curNode->meshPath = prop.value;
+										}
+									} else if (inAnimator && prop.name == "Type") {
+										// type of the animator
+										if (prop.value == "rotation") {
+											curAnim->type = Animator::ROTATION;
+										} else if (prop.value == "flyCircle") {
+											curAnim->type = Animator::FLY_CIRCLE;
+										} else if (prop.value == "flyStraight") {
+											curAnim->type = Animator::FLY_CIRCLE;
+										} else if (prop.value == "followSpline") {
+											curAnim->type = Animator::FOLLOW_SPLINE;
+										} else {
+											ASSIMP_LOG_WARN("IRR: Ignoring unknown animator: " + prop.value);
+
+											curAnim->type = Animator::UNKNOWN;
+										}
+									}
+								}
+							}
+							//} else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(), "attributes")) {
+						} else if (attrib.type() == pugi::node_null && !ASSIMP_stricmp(attrib.name(), "attributes")) {
+							break;
+						}
+					}
+				}
+				break;
+
+				/*case EXN_ELEMENT_END:
+
+				// If we reached the end of a node, we need to continue processing its parent
+				if (!ASSIMP_stricmp(reader->getNodeName(), "node")) {
+					if (!curNode) {
+						// currently is no node set. We need to go
+						// back in the node hierarchy
+						if (!curParent) {
+							curParent = root;
+							ASSIMP_LOG_ERROR("IRR: Too many closing <node> elements");
+						} else
+							curParent = curParent->parent;
+					} else
+						curNode = nullptr;
+				}
+				// clear all flags
+				else if (!ASSIMP_stricmp(reader->getNodeName(), "materials")) {
+					inMaterials = false;
+				} else if (!ASSIMP_stricmp(reader->getNodeName(), "animators")) {
+					inAnimator = false;
+				}
+				break;*/
+
+			default:
+				// GCC complains that not all enumeration values are handled
+				break;
+		}
+	//}
+
+	//  Now iterate through all cameras and compute their final (horizontal) FOV
+	for (aiCamera *cam : cameras) {
+		// screen aspect could be missing
+		if (cam->mAspect) {
+			cam->mHorizontalFOV *= cam->mAspect;
+		} else {
+			ASSIMP_LOG_WARN("IRR: Camera aspect is not given, can't compute horizontal FOV");
+		}
+	}
+
+	batch.LoadAll();
+
+	// Allocate a temporary scene data structure
+	aiScene *tempScene = new aiScene();
+	tempScene->mRootNode = new aiNode();
+	tempScene->mRootNode->mName.Set("<IRRRoot>");
+
+	// Copy the cameras to the output array
+	if (!cameras.empty()) {
+		tempScene->mNumCameras = (unsigned int)cameras.size();
+		tempScene->mCameras = new aiCamera *[tempScene->mNumCameras];
+		::memcpy(tempScene->mCameras, &cameras[0], sizeof(void *) * tempScene->mNumCameras);
+	}
+
+	// Copy the light sources to the output array
+	if (!lights.empty()) {
+		tempScene->mNumLights = (unsigned int)lights.size();
+		tempScene->mLights = new aiLight *[tempScene->mNumLights];
+		::memcpy(tempScene->mLights, &lights[0], sizeof(void *) * tempScene->mNumLights);
+	}
+
+	// temporary data
+	std::vector<aiNodeAnim *> anims;
+	std::vector<aiMaterial *> materials;
+	std::vector<AttachmentInfo> attach;
+	std::vector<aiMesh *> meshes;
+
+	// try to guess how much storage we'll need
+	anims.reserve(guessedAnimCnt + (guessedAnimCnt >> 2));
+	meshes.reserve(guessedMeshCnt + (guessedMeshCnt >> 2));
+	materials.reserve(guessedMatCnt + (guessedMatCnt >> 2));
+
+	// Now process our scene-graph recursively: generate final
+	// meshes and generate animation channels for all nodes.
+	unsigned int defMatIdx = UINT_MAX;
+	GenerateGraph(root, tempScene->mRootNode, tempScene,
+			batch, meshes, anims, attach, materials, defMatIdx);
+
+	if (!anims.empty()) {
+		tempScene->mNumAnimations = 1;
+		tempScene->mAnimations = new aiAnimation *[tempScene->mNumAnimations];
+		aiAnimation *an = tempScene->mAnimations[0] = new aiAnimation();
+
+		// ***********************************************************
+		// This is only the global animation channel of the scene.
+		// If there are animated models, they will have separate
+		// animation channels in the scene. To display IRR scenes
+		// correctly, users will need to combine the global anim
+		// channel with all the local animations they want to play
+		// ***********************************************************
+		an->mName.Set("Irr_GlobalAnimChannel");
+
+		// copy all node animation channels to the global channel
+		an->mNumChannels = (unsigned int)anims.size();
+		an->mChannels = new aiNodeAnim *[an->mNumChannels];
+		::memcpy(an->mChannels, &anims[0], sizeof(void *) * an->mNumChannels);
+	}
+	if (!meshes.empty()) {
+		// copy all meshes to the temporary scene
+		tempScene->mNumMeshes = (unsigned int)meshes.size();
+		tempScene->mMeshes = new aiMesh *[tempScene->mNumMeshes];
+		::memcpy(tempScene->mMeshes, &meshes[0], tempScene->mNumMeshes * sizeof(void *));
+	}
+
+	// Copy all materials to the output array
+	if (!materials.empty()) {
+		tempScene->mNumMaterials = (unsigned int)materials.size();
+		tempScene->mMaterials = new aiMaterial *[tempScene->mNumMaterials];
+		::memcpy(tempScene->mMaterials, &materials[0], sizeof(void *) * tempScene->mNumMaterials);
+	}
+
+	//  Now merge all sub scenes and attach them to the correct
+	//  attachment points in the scenegraph.
+	SceneCombiner::MergeScenes(&pScene, tempScene, attach,
+			AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
+																			  AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
+																	  0));
+
+	// If we have no meshes | no materials now set the INCOMPLETE
+	// scene flag. This is necessary if we failed to load all
+	// models from external files
+	if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
+		ASSIMP_LOG_WARN("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
+		pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+	}
+
+	// Finished ... everything destructs automatically and all
+	// temporary scenes have already been deleted by MergeScenes()
+	delete root;
 }
 
 #endif // !! ASSIMP_BUILD_NO_IRR_IMPORTER

+ 11 - 22
code/AssetLib/Irr/IRRLoader.h

@@ -4,7 +4,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,
@@ -40,7 +39,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-
 /** @file IRRLoader.h
  *  @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
  *  importer class.
@@ -83,7 +81,7 @@ protected:
 
 private:
 
-    /** Data structure for a scenegraph node animator
+    /** Data structure for a scene-graph node animator
      */
     struct Animator {
         // Type of the animator
@@ -129,7 +127,7 @@ private:
         int timeForWay;
     };
 
-    /** Data structure for a scenegraph node in an IRR file
+    /** Data structure for a scene-graph node in an IRR file
      */
     struct Node
     {
@@ -227,8 +225,7 @@ private:
 
 
     // -------------------------------------------------------------------
-    /** Fill the scenegraph recursively
-     */
+    /// Fill the scene-graph recursively
     void GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
         BatchLoader& batch,
         std::vector<aiMesh*>& meshes,
@@ -237,27 +234,22 @@ private:
         std::vector<aiMaterial*>& materials,
         unsigned int& defaultMatIdx);
 
-
     // -------------------------------------------------------------------
-    /** Generate a mesh that consists of just a single quad
-     */
+    /// Generate a mesh that consists of just a single quad
     aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1,
         const SkyboxVertex& v2,
         const SkyboxVertex& v3,
         const SkyboxVertex& v4);
 
-
     // -------------------------------------------------------------------
-    /** Build a skybox
-     *
-     *  @param meshes Receives 6 output meshes
-     *  @param materials The last 6 materials are assigned to the newly
-     *    created meshes. The names of the materials are adjusted.
-     */
+    /// Build a sky-box
+    ///
+    /// @param meshes Receives 6 output meshes
+    /// @param materials The last 6 materials are assigned to the newly
+    ///                  created meshes. The names of the materials are adjusted.
     void BuildSkybox(std::vector<aiMesh*>& meshes,
         std::vector<aiMaterial*> materials);
 
-
     // -------------------------------------------------------------------
     /** Copy a material for a mesh to the output material list
      *
@@ -271,7 +263,6 @@ private:
         unsigned int& defMatIdx,
         aiMesh* mesh);
 
-
     // -------------------------------------------------------------------
     /** Compute animations for a specific node
      *
@@ -281,13 +272,11 @@ private:
     void ComputeAnimations(Node* root, aiNode* real,
         std::vector<aiNodeAnim*>& anims);
 
-
 private:
-
-    /** Configuration option: desired output FPS */
+    /// Configuration option: desired output FPS 
     double fps;
 
-    /** Configuration option: speed flag was set? */
+    /// Configuration option: speed flag was set?
     bool configSpeedFlag;
 };
 

+ 426 - 446
code/AssetLib/Irr/IRRMeshLoader.cpp

@@ -43,494 +43,474 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /** @file Implementation of the IrrMesh importer class */
 
-
-
 #ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
 
 #include "IRRMeshLoader.h"
 #include <assimp/ParsingUtils.h>
 #include <assimp/fast_atof.h>
-#include <memory>
-#include <assimp/IOSystem.hpp>
-#include <assimp/mesh.h>
-#include <assimp/DefaultLogger.hpp>
+#include <assimp/importerdesc.h>
 #include <assimp/material.h>
+#include <assimp/mesh.h>
 #include <assimp/scene.h>
-#include <assimp/importerdesc.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <memory>
 
 using namespace Assimp;
-using namespace irr;
-using namespace irr::io;
 
 static const aiImporterDesc desc = {
-    "Irrlicht Mesh Reader",
-    "",
-    "",
-    "http://irrlicht.sourceforge.net/",
-    aiImporterFlags_SupportTextFlavour,
-    0,
-    0,
-    0,
-    0,
-    "xml irrmesh"
+	"Irrlicht Mesh Reader",
+	"",
+	"",
+	"http://irrlicht.sourceforge.net/",
+	aiImporterFlags_SupportTextFlavour,
+	0,
+	0,
+	0,
+	0,
+	"xml irrmesh"
 };
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-IRRMeshImporter::IRRMeshImporter()
-{}
+IRRMeshImporter::IRRMeshImporter() :
+        BaseImporter(),
+        IrrlichtBase() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-IRRMeshImporter::~IRRMeshImporter()
-{}
+IRRMeshImporter::~IRRMeshImporter() {}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
-    /* NOTE: A simple check for the file extension is not enough
+bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
+	/* NOTE: A simple check for the file extension is not enough
      * here. Irrmesh and irr are easy, but xml is too generic
      * and could be collada, too. So we need to open the file and
      * search for typical tokens.
      */
-    const std::string extension = GetExtension(pFile);
+	const std::string extension = GetExtension(pFile);
 
-    if (extension == "irrmesh")return true;
-    else if (extension == "xml" || checkSig)
-    {
-        /*  If CanRead() is called to check whether the loader
+	if (extension == "irrmesh")
+		return true;
+	else if (extension == "xml" || checkSig) {
+		/*  If CanRead() is called to check whether the loader
          *  supports a specific file extension in general we
          *  must return true here.
          */
-        if (!pIOHandler)return true;
-        const char* tokens[] = {"irrmesh"};
-        return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
-    }
-    return false;
+		if (!pIOHandler) return true;
+		const char *tokens[] = { "irrmesh" };
+		return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+	}
+	return false;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get a list of all file extensions which are handled by this class
-const aiImporterDesc* IRRMeshImporter::GetInfo () const
-{
-    return &desc;
+const aiImporterDesc *IRRMeshImporter::GetInfo() const {
+	return &desc;
 }
 
-static void releaseMaterial( aiMaterial **mat ) {
-    if(*mat!= nullptr) {
-        delete *mat;
-        *mat = nullptr;
-    }
+static void releaseMaterial(aiMaterial **mat) {
+	if (*mat != nullptr) {
+		delete *mat;
+		*mat = nullptr;
+	}
 }
 
-static void releaseMesh( aiMesh **mesh ) {
-    if (*mesh != nullptr){
-        delete *mesh;
-        *mesh = nullptr;
-    }
+static void releaseMesh(aiMesh **mesh) {
+	if (*mesh != nullptr) {
+		delete *mesh;
+		*mesh = nullptr;
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void IRRMeshImporter::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 IRRMESH file " + pFile + ".");
-    }
-
-    // Construct the irrXML parser
-    CIrrXML_IOStreamReader st(file.get());
-    reader = createIrrXMLReader((IFileReadCallBack*) &st);
-
-    // final data
-    std::vector<aiMaterial*> materials;
-    std::vector<aiMesh*>     meshes;
-    materials.reserve (5);
-    meshes.reserve(5);
-
-    // temporary data - current mesh buffer
-    aiMaterial* curMat  = nullptr;
-    aiMesh* curMesh     = nullptr;
-    unsigned int curMatFlags = 0;
-
-    std::vector<aiVector3D> curVertices,curNormals,curTangents,curBitangents;
-    std::vector<aiColor4D>  curColors;
-    std::vector<aiVector3D> curUVs,curUV2s;
-
-    // some temporary variables
-    int textMeaning = 0;
-    int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
-    bool useColors = false;
-
-    // Parse the XML file
-    while (reader->read())  {
-        switch (reader->getNodeType())  {
-        case EXN_ELEMENT:
-
-            if (!ASSIMP_stricmp(reader->getNodeName(),"buffer") && (curMat || curMesh)) {
-                // end of previous buffer. A material and a mesh should be there
-                if ( !curMat || !curMesh)   {
-                    ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
-                    releaseMaterial( &curMat );
-                    releaseMesh( &curMesh );
-                } else {
-                    materials.push_back(curMat);
-                    meshes.push_back(curMesh);
-                }
-                curMat  = nullptr;
-                curMesh = nullptr;
-
-                curVertices.clear();
-                curColors.clear();
-                curNormals.clear();
-                curUV2s.clear();
-                curUVs.clear();
-                curTangents.clear();
-                curBitangents.clear();
-            }
-
-
-            if (!ASSIMP_stricmp(reader->getNodeName(),"material"))  {
-                if (curMat) {
-                    ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
-                    releaseMaterial( &curMat );
-                }
-                curMat = ParseMaterial(curMatFlags);
-            }
-            /* no else here! */ if (!ASSIMP_stricmp(reader->getNodeName(),"vertices"))
-            {
-                int num = reader->getAttributeValueAsInt("vertexCount");
-
-                if (!num)   {
-                    // This is possible ... remove the mesh from the list and skip further reading
-                    ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
-
-                    releaseMaterial( &curMat );
-                    releaseMesh( &curMesh );
-                    textMeaning = 0;
-                    continue;
-                }
-
-                curVertices.reserve(num);
-                curNormals.reserve(num);
-                curColors.reserve(num);
-                curUVs.reserve(num);
-
-                // Determine the file format
-                const char* t = reader->getAttributeValueSafe("type");
-                if (!ASSIMP_stricmp("2tcoords", t)) {
-                    curUV2s.reserve (num);
-                    vertexFormat = 1;
-
-                    if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
-                        // *********************************************************
-                        // We have a second texture! So use this UV channel
-                        // for it. The 2nd texture can be either a normal
-                        // texture (solid_2layer or lightmap_xxx) or a normal
-                        // map (normal_..., parallax_...)
-                        // *********************************************************
-                        int idx = 1;
-                        aiMaterial* mat = ( aiMaterial* ) curMat;
-
-                        if (curMatFlags & AI_IRRMESH_MAT_lightmap){
-                            mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_LIGHTMAP(0));
-                        }
-                        else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid){
-                            mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
-                        }
-                        else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) {
-                            mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
-                        }
-                    }
-                }
-                else if (!ASSIMP_stricmp("tangents", t))    {
-                    curTangents.reserve (num);
-                    curBitangents.reserve (num);
-                    vertexFormat = 2;
-                }
-                else if (ASSIMP_stricmp("standard", t)) {
-                    releaseMaterial( &curMat );
-                    ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format");
-                }
-                else vertexFormat = 0;
-                textMeaning = 1;
-            }
-            else if (!ASSIMP_stricmp(reader->getNodeName(),"indices"))  {
-                if (curVertices.empty() && curMat)  {
-                    releaseMaterial( &curMat );
-                    throw DeadlyImportError("IRRMESH: indices must come after vertices");
-                }
-
-                textMeaning = 2;
-
-                // start a new mesh
-                curMesh = new aiMesh();
-
-                // allocate storage for all faces
-                curMesh->mNumVertices = reader->getAttributeValueAsInt("indexCount");
-                if (!curMesh->mNumVertices) {
-                    // This is possible ... remove the mesh from the list and skip further reading
-                    ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero indices");
-
-                    // mesh - away
-                    releaseMesh( &curMesh );
-
-                    // material - away
-                    releaseMaterial( &curMat );
-
-                    textMeaning = 0;
-                    continue;
-                }
-
-                if (curMesh->mNumVertices % 3)  {
-                    ASSIMP_LOG_WARN("IRRMESH: Number if indices isn't divisible by 3");
-                }
-
-                curMesh->mNumFaces = curMesh->mNumVertices / 3;
-                curMesh->mFaces = new aiFace[curMesh->mNumFaces];
-
-                // setup some members
-                curMesh->mMaterialIndex = (unsigned int)materials.size();
-                curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
-
-                // allocate storage for all vertices
-                curMesh->mVertices = new aiVector3D[curMesh->mNumVertices];
-
-                if (curNormals.size() == curVertices.size())    {
-                    curMesh->mNormals = new aiVector3D[curMesh->mNumVertices];
-                }
-                if (curTangents.size() == curVertices.size())   {
-                    curMesh->mTangents = new aiVector3D[curMesh->mNumVertices];
-                }
-                if (curBitangents.size() == curVertices.size()) {
-                    curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices];
-                }
-                if (curColors.size() == curVertices.size() && useColors)    {
-                    curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices];
-                }
-                if (curUVs.size() == curVertices.size())    {
-                    curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices];
-                }
-                if (curUV2s.size() == curVertices.size())   {
-                    curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
-                }
-            }
-            break;
-
-        case EXN_TEXT:
-            {
-            const char* sz = reader->getNodeData();
-            if (textMeaning == 1)   {
-                textMeaning = 0;
-
-                // read vertices
-                do  {
-                    SkipSpacesAndLineEnd(&sz);
-                    aiVector3D temp;aiColor4D c;
-
-                    // Read the vertex position
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.x);
-                    SkipSpaces(&sz);
-
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.y);
-                    SkipSpaces(&sz);
-
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.z);
-                    SkipSpaces(&sz);
-                    curVertices.push_back(temp);
-
-                    // Read the vertex normals
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.x);
-                    SkipSpaces(&sz);
-
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.y);
-                    SkipSpaces(&sz);
-
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.z);
-                    SkipSpaces(&sz);
-                    curNormals.push_back(temp);
-
-                    // read the vertex colors
-                    uint32_t clr = strtoul16(sz,&sz);
-                    ColorFromARGBPacked(clr,c);
-
-                    if (!curColors.empty() && c != *(curColors.end()-1))
-                        useColors = true;
-
-                    curColors.push_back(c);
-                    SkipSpaces(&sz);
-
-
-                    // read the first UV coordinate set
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.x);
-                    SkipSpaces(&sz);
-
-                    sz = fast_atoreal_move<float>(sz,(float&)temp.y);
-                    SkipSpaces(&sz);
-                    temp.z = 0.f;
-                    temp.y = 1.f - temp.y;  // DX to OGL
-                    curUVs.push_back(temp);
-
-                    // read the (optional) second UV coordinate set
-                    if (vertexFormat == 1)  {
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.x);
-                        SkipSpaces(&sz);
-
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.y);
-                        temp.y = 1.f - temp.y; // DX to OGL
-                        curUV2s.push_back(temp);
-                    }
-                    // read optional tangent and bitangent vectors
-                    else if (vertexFormat == 2) {
-                        // tangents
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.x);
-                        SkipSpaces(&sz);
-
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.z);
-                        SkipSpaces(&sz);
-
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.y);
-                        SkipSpaces(&sz);
-                        temp.y *= -1.0f;
-                        curTangents.push_back(temp);
-
-                        // bitangents
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.x);
-                        SkipSpaces(&sz);
-
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.z);
-                        SkipSpaces(&sz);
-
-                        sz = fast_atoreal_move<float>(sz,(float&)temp.y);
-                        SkipSpaces(&sz);
-                        temp.y *= -1.0f;
-                        curBitangents.push_back(temp);
-                    }
-                }
-
-                /* IMPORTANT: We assume that each vertex is specified in one
-                   line. So we can skip the rest of the line - unknown vertex
-                   elements are ignored.
-                 */
-
-                while (SkipLine(&sz));
-            }
-            else if (textMeaning == 2)  {
-                textMeaning = 0;
-
-                // read indices
-                aiFace* curFace = curMesh->mFaces;
-                aiFace* const faceEnd = curMesh->mFaces  + curMesh->mNumFaces;
-
-                aiVector3D* pcV  = curMesh->mVertices;
-                aiVector3D* pcN  = curMesh->mNormals;
-                aiVector3D* pcT  = curMesh->mTangents;
-                aiVector3D* pcB  = curMesh->mBitangents;
-                aiColor4D* pcC0  = curMesh->mColors[0];
-                aiVector3D* pcT0 = curMesh->mTextureCoords[0];
-                aiVector3D* pcT1 = curMesh->mTextureCoords[1];
-
-                unsigned int curIdx = 0;
-                unsigned int total = 0;
-                while(SkipSpacesAndLineEnd(&sz))    {
-                    if (curFace >= faceEnd) {
-                        ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
-                        break;
-                    }
-                    if (!curIdx)    {
-                        curFace->mNumIndices = 3;
-                        curFace->mIndices = new unsigned int[3];
-                    }
-
-                    unsigned int idx = strtoul10(sz,&sz);
-                    if (idx >= curVertices.size())  {
-                        ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
-                        idx = 0;
-                    }
-
-                    curFace->mIndices[curIdx] = total++;
-
-                    *pcV++ = curVertices[idx];
-                    if (pcN)*pcN++ = curNormals[idx];
-                    if (pcT)*pcT++ = curTangents[idx];
-                    if (pcB)*pcB++ = curBitangents[idx];
-                    if (pcC0)*pcC0++ = curColors[idx];
-                    if (pcT0)*pcT0++ = curUVs[idx];
-                    if (pcT1)*pcT1++ = curUV2s[idx];
-
-                    if (++curIdx == 3)  {
-                        ++curFace;
-                        curIdx = 0;
-                    }
-                }
-
-                if (curFace != faceEnd)
-                    ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
-
-                // Finish processing the mesh - do some small material workarounds
-                if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors)  {
-                    // Take the opacity value of the current material
-                    // from the common vertex color alpha
-                    aiMaterial* mat = (aiMaterial*)curMat;
-                    mat->AddProperty(&curColors[0].a,1,AI_MATKEY_OPACITY);
-                }
-            }}
-            break;
-
-            default:
-                // GCC complains here ...
-                break;
-
-        };
-    }
-
-    // End of the last buffer. A material and a mesh should be there
-    if (curMat || curMesh)  {
-        if ( !curMat || !curMesh)   {
-            ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
-            releaseMaterial( &curMat );
-            releaseMesh( &curMesh );
-        }
-        else    {
-            materials.push_back(curMat);
-            meshes.push_back(curMesh);
-        }
-    }
-
-    if (materials.empty())
-        throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
-
-
-    // now generate the output scene
-    pScene->mNumMeshes = (unsigned int)meshes.size();
-    pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
-    for (unsigned int i = 0; i < pScene->mNumMeshes;++i)    {
-        pScene->mMeshes[i] = meshes[i];
-
-        // clean this value ...
-        pScene->mMeshes[i]->mNumUVComponents[3] = 0;
+void IRRMeshImporter::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() == NULL)
+		throw DeadlyImportError("Failed to open IRRMESH file " + pFile + "");
+
+	// Construct the irrXML parser
+	XmlParser parser;
+    if (!parser.parse( file.get() )) {
+        return;
     }
-
-    pScene->mNumMaterials = (unsigned int)materials.size();
-    pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
-    ::memcpy(pScene->mMaterials,&materials[0],sizeof(void*)*pScene->mNumMaterials);
-
-    pScene->mRootNode = new aiNode();
-    pScene->mRootNode->mName.Set("<IRRMesh>");
-    pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
-    pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
-
-    for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
-        pScene->mRootNode->mMeshes[i] = i;
-
-    // clean up and return
-    delete reader;
-    AI_DEBUG_INVALIDATE_PTR(reader);
+    XmlNode root = parser.getRootNode();
+
+	// final data
+	std::vector<aiMaterial *> materials;
+	std::vector<aiMesh *> meshes;
+	materials.reserve(5);
+	meshes.reserve(5);
+
+	// temporary data - current mesh buffer
+	aiMaterial *curMat = nullptr;
+	aiMesh *curMesh = nullptr;
+	unsigned int curMatFlags = 0;
+
+	std::vector<aiVector3D> curVertices, curNormals, curTangents, curBitangents;
+	std::vector<aiColor4D> curColors;
+	std::vector<aiVector3D> curUVs, curUV2s;
+
+	// some temporary variables
+	int textMeaning = 0;
+	int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
+	bool useColors = false;
+
+	// Parse the XML file
+	for (pugi::xml_node child : root.children()) {
+		if (child.type() == pugi::node_element) {
+			if (!ASSIMP_stricmp(child.name(), "buffer") && (curMat || curMesh)) {
+				// end of previous buffer. A material and a mesh should be there
+				if (!curMat || !curMesh) {
+					ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
+					releaseMaterial(&curMat);
+					releaseMesh(&curMesh);
+				} else {
+					materials.push_back(curMat);
+					meshes.push_back(curMesh);
+				}
+				curMat = nullptr;
+				curMesh = nullptr;
+
+				curVertices.clear();
+				curColors.clear();
+				curNormals.clear();
+				curUV2s.clear();
+				curUVs.clear();
+				curTangents.clear();
+				curBitangents.clear();
+			}
+
+			if (!ASSIMP_stricmp(child.name(), "material")) {
+				if (curMat) {
+					ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
+					releaseMaterial(&curMat);
+				}
+				curMat = ParseMaterial(curMatFlags);
+			}
+			/* no else here! */ if (!ASSIMP_stricmp(child.name(), "vertices")) {
+				pugi::xml_attribute attr = child.attribute("vertexCount");
+				int num = attr.as_int();
+                //int num = reader->getAttributeValueAsInt("vertexCount");
+
+				if (!num) {
+					// This is possible ... remove the mesh from the list and skip further reading
+					ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
+
+					releaseMaterial(&curMat);
+					releaseMesh(&curMesh);
+					textMeaning = 0;
+					continue;
+				}
+
+				curVertices.reserve(num);
+				curNormals.reserve(num);
+				curColors.reserve(num);
+				curUVs.reserve(num);
+
+				// Determine the file format
+				//const char *t = reader->getAttributeValueSafe("type");
+                pugi::xml_attribute t = child.attribute("type");
+				if (!ASSIMP_stricmp("2tcoords", t.name())) {
+					curUV2s.reserve(num);
+					vertexFormat = 1;
+
+					if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
+						// *********************************************************
+						// We have a second texture! So use this UV channel
+						// for it. The 2nd texture can be either a normal
+						// texture (solid_2layer or lightmap_xxx) or a normal
+						// map (normal_..., parallax_...)
+						// *********************************************************
+						int idx = 1;
+						aiMaterial *mat = (aiMaterial *)curMat;
+
+						if (curMatFlags & AI_IRRMESH_MAT_lightmap) {
+							mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_LIGHTMAP(0));
+						} else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid) {
+							mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0));
+						} else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) {
+							mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
+						}
+					}
+				} else if (!ASSIMP_stricmp("tangents", t.name())) {
+					curTangents.reserve(num);
+					curBitangents.reserve(num);
+					vertexFormat = 2;
+				} else if (ASSIMP_stricmp("standard", t.name())) {
+					releaseMaterial(&curMat);
+					ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format");
+				} else
+					vertexFormat = 0;
+				textMeaning = 1;
+			} else if (!ASSIMP_stricmp(child.name(), "indices")) {
+				if (curVertices.empty() && curMat) {
+					releaseMaterial(&curMat);
+					throw DeadlyImportError("IRRMESH: indices must come after vertices");
+				}
+
+				textMeaning = 2;
+
+				// start a new mesh
+				curMesh = new aiMesh();
+
+				// allocate storage for all faces
+				pugi::xml_attribute attr = child.attribute("indexCount");
+				curMesh->mNumVertices = attr.as_int();
+				if (!curMesh->mNumVertices) {
+					// This is possible ... remove the mesh from the list and skip further reading
+					ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero indices");
+
+					// mesh - away
+					releaseMesh(&curMesh);
+
+					// material - away
+					releaseMaterial(&curMat);
+
+					textMeaning = 0;
+					continue;
+				}
+
+				if (curMesh->mNumVertices % 3) {
+					ASSIMP_LOG_WARN("IRRMESH: Number if indices isn't divisible by 3");
+				}
+
+				curMesh->mNumFaces = curMesh->mNumVertices / 3;
+				curMesh->mFaces = new aiFace[curMesh->mNumFaces];
+
+				// setup some members
+				curMesh->mMaterialIndex = (unsigned int)materials.size();
+				curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+				// allocate storage for all vertices
+				curMesh->mVertices = new aiVector3D[curMesh->mNumVertices];
+
+				if (curNormals.size() == curVertices.size()) {
+					curMesh->mNormals = new aiVector3D[curMesh->mNumVertices];
+				}
+				if (curTangents.size() == curVertices.size()) {
+					curMesh->mTangents = new aiVector3D[curMesh->mNumVertices];
+				}
+				if (curBitangents.size() == curVertices.size()) {
+					curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices];
+				}
+				if (curColors.size() == curVertices.size() && useColors) {
+					curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices];
+				}
+				if (curUVs.size() == curVertices.size()) {
+					curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices];
+				}
+				if (curUV2s.size() == curVertices.size()) {
+					curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
+				}
+			}
+			//break;
+
+			//case EXN_TEXT: {
+			const char *sz = child.child_value();
+			if (textMeaning == 1) {
+				textMeaning = 0;
+
+				// read vertices
+				do {
+					SkipSpacesAndLineEnd(&sz);
+					aiVector3D temp;
+					aiColor4D c;
+
+					// Read the vertex position
+					sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+					SkipSpaces(&sz);
+
+					sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+					SkipSpaces(&sz);
+
+					sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+					SkipSpaces(&sz);
+					curVertices.push_back(temp);
+
+					// Read the vertex normals
+					sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+					SkipSpaces(&sz);
+
+					sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+					SkipSpaces(&sz);
+
+					sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+					SkipSpaces(&sz);
+					curNormals.push_back(temp);
+
+					// read the vertex colors
+					uint32_t clr = strtoul16(sz, &sz);
+					ColorFromARGBPacked(clr, c);
+
+					if (!curColors.empty() && c != *(curColors.end() - 1))
+						useColors = true;
+
+					curColors.push_back(c);
+					SkipSpaces(&sz);
+
+					// read the first UV coordinate set
+					sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+					SkipSpaces(&sz);
+
+					sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+					SkipSpaces(&sz);
+					temp.z = 0.f;
+					temp.y = 1.f - temp.y; // DX to OGL
+					curUVs.push_back(temp);
+
+					// read the (optional) second UV coordinate set
+					if (vertexFormat == 1) {
+						sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+						SkipSpaces(&sz);
+
+						sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+						temp.y = 1.f - temp.y; // DX to OGL
+						curUV2s.push_back(temp);
+					}
+					// read optional tangent and bitangent vectors
+					else if (vertexFormat == 2) {
+						// tangents
+						sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+						SkipSpaces(&sz);
+
+						sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+						SkipSpaces(&sz);
+
+						sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+						SkipSpaces(&sz);
+						temp.y *= -1.0f;
+						curTangents.push_back(temp);
+
+						// bitangents
+						sz = fast_atoreal_move<float>(sz, (float &)temp.x);
+						SkipSpaces(&sz);
+
+						sz = fast_atoreal_move<float>(sz, (float &)temp.z);
+						SkipSpaces(&sz);
+
+						sz = fast_atoreal_move<float>(sz, (float &)temp.y);
+						SkipSpaces(&sz);
+						temp.y *= -1.0f;
+						curBitangents.push_back(temp);
+					}
+				}
+
+				/* IMPORTANT: We assume that each vertex is specified in one
+                line. So we can skip the rest of the line - unknown vertex
+                elements are ignored.
+                */
+
+				while (SkipLine(&sz));
+			} else if (textMeaning == 2) {
+				textMeaning = 0;
+
+				// read indices
+				aiFace *curFace = curMesh->mFaces;
+				aiFace *const faceEnd = curMesh->mFaces + curMesh->mNumFaces;
+
+				aiVector3D *pcV = curMesh->mVertices;
+				aiVector3D *pcN = curMesh->mNormals;
+				aiVector3D *pcT = curMesh->mTangents;
+				aiVector3D *pcB = curMesh->mBitangents;
+				aiColor4D *pcC0 = curMesh->mColors[0];
+				aiVector3D *pcT0 = curMesh->mTextureCoords[0];
+				aiVector3D *pcT1 = curMesh->mTextureCoords[1];
+
+				unsigned int curIdx = 0;
+				unsigned int total = 0;
+				while (SkipSpacesAndLineEnd(&sz)) {
+					if (curFace >= faceEnd) {
+						ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
+						break;
+					}
+					if (!curIdx) {
+						curFace->mNumIndices = 3;
+						curFace->mIndices = new unsigned int[3];
+					}
+
+					unsigned int idx = strtoul10(sz, &sz);
+					if (idx >= curVertices.size()) {
+						ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
+						idx = 0;
+					}
+
+					curFace->mIndices[curIdx] = total++;
+
+					*pcV++ = curVertices[idx];
+					if (pcN) *pcN++ = curNormals[idx];
+					if (pcT) *pcT++ = curTangents[idx];
+					if (pcB) *pcB++ = curBitangents[idx];
+					if (pcC0) *pcC0++ = curColors[idx];
+					if (pcT0) *pcT0++ = curUVs[idx];
+					if (pcT1) *pcT1++ = curUV2s[idx];
+
+					if (++curIdx == 3) {
+						++curFace;
+						curIdx = 0;
+					}
+				}
+
+				if (curFace != faceEnd)
+					ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
+
+				// Finish processing the mesh - do some small material workarounds
+				if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
+					// Take the opacity value of the current material
+					// from the common vertex color alpha
+					aiMaterial *mat = (aiMaterial *)curMat;
+					mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
+				}
+			}
+		}
+	}
+
+	// End of the last buffer. A material and a mesh should be there
+	if (curMat || curMesh) {
+		if (!curMat || !curMesh) {
+			ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
+			releaseMaterial(&curMat);
+			releaseMesh(&curMesh);
+		} else {
+			materials.push_back(curMat);
+			meshes.push_back(curMesh);
+		}
+	}
+
+	if (materials.empty()) {
+		throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
+	}
+
+	// now generate the output scene
+	pScene->mNumMeshes = (unsigned int)meshes.size();
+	pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
+	for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+		pScene->mMeshes[i] = meshes[i];
+
+		// clean this value ...
+		pScene->mMeshes[i]->mNumUVComponents[3] = 0;
+	}
+
+	pScene->mNumMaterials = (unsigned int)materials.size();
+	pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
+	::memcpy(pScene->mMaterials, &materials[0], sizeof(void *) * pScene->mNumMaterials);
+
+	pScene->mRootNode = new aiNode();
+	pScene->mRootNode->mName.Set("<IRRMesh>");
+	pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+	pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+	for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+		pScene->mRootNode->mMeshes[i] = i;
+	}
 }
 
 #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER

+ 8 - 15
code/AssetLib/Irr/IRRMeshLoader.h

@@ -4,7 +4,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,
@@ -47,12 +46,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_IRRMESHLOADER_H_INCLUDED
 #define AI_IRRMESHLOADER_H_INCLUDED
 
-#include <assimp/BaseImporter.h>
 #include "IRRShared.h"
+#include <assimp/BaseImporter.h>
 
 #ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
 
-namespace Assimp    {
+namespace Assimp {
 
 // ---------------------------------------------------------------------------
 /** IrrMesh importer class.
@@ -61,37 +60,31 @@ namespace Assimp    {
  * irrEdit. As IrrEdit itself is capable of importing quite many file formats,
  * it might be a good file format for data exchange.
  */
-class IRRMeshImporter : public BaseImporter, public IrrlichtBase
-{
+class IRRMeshImporter : public BaseImporter, public IrrlichtBase {
 public:
     IRRMeshImporter();
     ~IRRMeshImporter();
 
-
-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);
 };
 
 } // end of namespace Assimp

+ 215 - 329
code/AssetLib/Irr/IRRShared.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,
@@ -45,8 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Shared utilities for the IRR and IRRMESH loaders
  */
 
-
-
 //This section should be excluded only if both the Irrlicht AND the Irrlicht Mesh importers were omitted.
 #if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
 
@@ -56,10 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/material.h>
 
-
 using namespace Assimp;
-using namespace irr;
-using namespace irr::io;
 
 // Transformation matrix to convert from Assimp to IRR space
 const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
@@ -70,125 +63,94 @@ const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
 
 // ------------------------------------------------------------------------------------------------
 // read a property in hexadecimal format (i.e. ffffffff)
-void IrrlichtBase::ReadHexProperty    (HexProperty&    out)
-{
-    for (int i = 0; i < reader->getAttributeCount();++i)
-    {
-        if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
-        {
-            out.name = std::string( reader->getAttributeValue(i) );
-        }
-        else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
-        {
+void IrrlichtBase::ReadHexProperty(HexProperty &out ) {
+	for (pugi::xml_attribute attrib : mNode->attributes()) {
+        if (!ASSIMP_stricmp(attrib.name(), "name")) {
+            out.name = std::string( attrib.value() );
+        } else if (!ASSIMP_stricmp(attrib.name(),"value")) {
             // parse the hexadecimal value
-            out.value = strtoul16(reader->getAttributeValue(i));
+			out.value = strtoul16(attrib.name());
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // read a decimal property
-void IrrlichtBase::ReadIntProperty    (IntProperty&    out)
-{
-    for (int i = 0; i < reader->getAttributeCount();++i)
-    {
-        if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
-        {
-            out.name = std::string( reader->getAttributeValue(i) );
-        }
-        else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
-        {
-            // parse the ecimal value
-            out.value = strtol10(reader->getAttributeValue(i));
+void IrrlichtBase::ReadIntProperty(IntProperty & out) {
+	for (pugi::xml_attribute attrib : mNode->attributes()) {
+		if (!ASSIMP_stricmp(attrib.name(), "name")) {
+			out.name = std::string(attrib.value());
+        } else if (!ASSIMP_stricmp(attrib.value(),"value")) {
+            // parse the int value
+			out.value = strtol10(attrib.name());
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // read a string property
-void IrrlichtBase::ReadStringProperty (StringProperty& out)
-{
-    for (int i = 0; i < reader->getAttributeCount();++i)
-    {
-        if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
-        {
-            out.name = std::string( reader->getAttributeValue(i) );
-        }
-        else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
-        {
+void IrrlichtBase::ReadStringProperty( StringProperty& out) {
+	for (pugi::xml_attribute attrib : mNode->attributes()) {
+		if (!ASSIMP_stricmp(attrib.name(), "name")) {
+			out.name = std::string(attrib.value());
+		} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
             // simple copy the string
-            out.value = std::string (reader->getAttributeValue(i));
+			out.value = std::string(attrib.value());
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // read a boolean property
-void IrrlichtBase::ReadBoolProperty   (BoolProperty&   out)
-{
-    for (int i = 0; i < reader->getAttributeCount();++i)
-    {
-        if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
-        {
-            out.name = std::string( reader->getAttributeValue(i) );
-        }
-        else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
-        {
+void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
+	for (pugi::xml_attribute attrib : mNode->attributes()) {
+		if (!ASSIMP_stricmp(attrib.name(), "name")){
+			out.name = std::string(attrib.value());
+		} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
             // true or false, case insensitive
-            out.value = (ASSIMP_stricmp( reader->getAttributeValue(i),
-                "true") ? false : true);
+			out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true);
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // read a float property
-void IrrlichtBase::ReadFloatProperty  (FloatProperty&  out)
-{
-    for (int i = 0; i < reader->getAttributeCount();++i)
-    {
-        if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
-        {
-            out.name = std::string( reader->getAttributeValue(i) );
-        }
-        else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
-        {
+void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
+	for (pugi::xml_attribute attrib : mNode->attributes()) {
+		if (!ASSIMP_stricmp(attrib.name(), "name")) {
+			out.name = std::string(attrib.value());
+		} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
             // just parse the float
-            out.value = fast_atof( reader->getAttributeValue(i) );
+			out.value = fast_atof(attrib.value());
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // read a vector property
-void IrrlichtBase::ReadVectorProperty  (VectorProperty&  out)
-{
-    for (int i = 0; i < reader->getAttributeCount();++i)
-    {
-        if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
-        {
-            out.name = std::string( reader->getAttributeValue(i) );
-        }
-        else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
-        {
+void IrrlichtBase::ReadVectorProperty( VectorProperty &out ) {
+	for (pugi::xml_attribute attrib : mNode->attributes()) {
+		if (!ASSIMP_stricmp(attrib.name(), "name")) {
+			out.name = std::string(attrib.value());
+		} else if (!ASSIMP_stricmp(attrib.name(), "value")) {
             // three floats, separated with commas
-            const char* ptr = reader->getAttributeValue(i);
+            const char *ptr = attrib.value();
 
             SkipSpaces(&ptr);
             ptr = fast_atoreal_move<float>( ptr,(float&)out.value.x );
             SkipSpaces(&ptr);
-            if (',' != *ptr)
-            {
+            if (',' != *ptr) {
                 ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
-            }
-            else SkipSpaces(ptr+1,&ptr);
+			} else {
+				SkipSpaces(ptr + 1, &ptr);
+			}
             ptr = fast_atoreal_move<float>( ptr,(float&)out.value.y );
             SkipSpaces(&ptr);
-            if (',' != *ptr)
-            {
+            if (',' != *ptr) {
                 ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
-            }
-            else SkipSpaces(ptr+1,&ptr);
+			} else {
+				SkipSpaces(ptr + 1, &ptr);
+			}
             ptr = fast_atoreal_move<float>( ptr,(float&)out.value.z );
         }
     }
@@ -196,22 +158,19 @@ void IrrlichtBase::ReadVectorProperty  (VectorProperty&  out)
 
 // ------------------------------------------------------------------------------------------------
 // Convert a string to a proper aiMappingMode
-int ConvertMappingMode(const std::string& mode)
-{
-    if (mode == "texture_clamp_repeat")
-    {
+int ConvertMappingMode(const std::string& mode) {
+    if (mode == "texture_clamp_repeat") {
         return aiTextureMapMode_Wrap;
-    }
-    else if (mode == "texture_clamp_mirror")
-        return aiTextureMapMode_Mirror;
+	} else if (mode == "texture_clamp_mirror") {
+		return aiTextureMapMode_Mirror;
+	}
 
     return aiTextureMapMode_Clamp;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Parse a material from the XML file
-aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
-{
+aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
     aiMaterial* mat = new aiMaterial();
     aiColor4D clr;
     aiString s;
@@ -220,244 +179,170 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
     int cnt  = 0; // number of used texture channels
     unsigned int nd = 0;
 
-    // Continue reading from the file
-    while (reader->read())
-    {
-        switch (reader->getNodeType())
-        {
-        case EXN_ELEMENT:
-
-            // Hex properties
-            if (!ASSIMP_stricmp(reader->getNodeName(),"color"))
-            {
-                HexProperty prop;
-                ReadHexProperty(prop);
-                if (prop.name == "Diffuse")
-                {
-                    ColorFromARGBPacked(prop.value,clr);
-                    mat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
-                }
-                else if (prop.name == "Ambient")
-                {
-                    ColorFromARGBPacked(prop.value,clr);
-                    mat->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
-                }
-                else if (prop.name == "Specular")
-                {
-                    ColorFromARGBPacked(prop.value,clr);
-                    mat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
-                }
-
-                // NOTE: The 'emissive' property causes problems. It is
-                // often != 0, even if there is obviously no light
-                // emitted by the described surface. In fact I think
-                // IRRLICHT ignores this property, too.
+    for (pugi::xml_node child : mNode->children()) {
+		if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties
+			HexProperty prop;
+			ReadHexProperty(prop);
+			if (prop.name == "Diffuse") {
+				ColorFromARGBPacked(prop.value, clr);
+				mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
+			} else if (prop.name == "Ambient") {
+				ColorFromARGBPacked(prop.value, clr);
+				mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_AMBIENT);
+			} else if (prop.name == "Specular") {
+				ColorFromARGBPacked(prop.value, clr);
+				mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_SPECULAR);
+			}
+
+			// NOTE: The 'emissive' property causes problems. It is
+			// often != 0, even if there is obviously no light
+			// emitted by the described surface. In fact I think
+			// IRRLICHT ignores this property, too.
 #if 0
-                else if (prop.name == "Emissive")
-                {
-                    ColorFromARGBPacked(prop.value,clr);
-                    mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
-                }
-#endif
-            }
-            // Float properties
-            else if (!ASSIMP_stricmp(reader->getNodeName(),"float"))
-            {
-                FloatProperty prop;
-                ReadFloatProperty(prop);
-                if (prop.name == "Shininess")
-                {
-                    mat->AddProperty(&prop.value,1,AI_MATKEY_SHININESS);
-                }
-            }
-            // Bool properties
-            else if (!ASSIMP_stricmp(reader->getNodeName(),"bool"))
-            {
-                BoolProperty prop;
-                ReadBoolProperty(prop);
-                if (prop.name == "Wireframe")
-                {
-                    int val = (prop.value ? true : false);
-                    mat->AddProperty(&val,1,AI_MATKEY_ENABLE_WIREFRAME);
-                }
-                else if (prop.name == "GouraudShading")
-                {
-                    int val = (prop.value ? aiShadingMode_Gouraud
-                        : aiShadingMode_NoShading);
-                    mat->AddProperty(&val,1,AI_MATKEY_SHADING_MODEL);
-                }
-                else if (prop.name == "BackfaceCulling")
-                {
-                    int val = (!prop.value);
-                    mat->AddProperty(&val,1,AI_MATKEY_TWOSIDED);
-                }
-            }
-            // String properties - textures and texture related properties
-            else if (!ASSIMP_stricmp(reader->getNodeName(),"texture") ||
-                     !ASSIMP_stricmp(reader->getNodeName(),"enum"))
-            {
-                StringProperty prop;
-                ReadStringProperty(prop);
-                if (prop.value.length())
-                {
-                    // material type (shader)
-                    if (prop.name == "Type")
-                    {
-                        if (prop.value == "solid")
-                        {
-                            // default material ...
-                        }
-                        else if (prop.value == "trans_vertex_alpha")
-                        {
-                            matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
-                        }
-                        else if (prop.value == "lightmap")
-                        {
-                            matFlags = AI_IRRMESH_MAT_lightmap;
-                        }
-                        else if (prop.value == "solid_2layer")
-                        {
-                            matFlags = AI_IRRMESH_MAT_solid_2layer;
-                        }
-                        else if (prop.value == "lightmap_m2")
-                        {
-                            matFlags = AI_IRRMESH_MAT_lightmap_m2;
-                        }
-                        else if (prop.value == "lightmap_m4")
-                        {
-                            matFlags = AI_IRRMESH_MAT_lightmap_m4;
-                        }
-                        else if (prop.value == "lightmap_light")
-                        {
-                            matFlags = AI_IRRMESH_MAT_lightmap_light;
-                        }
-                        else if (prop.value == "lightmap_light_m2")
-                        {
-                            matFlags = AI_IRRMESH_MAT_lightmap_light_m2;
-                        }
-                        else if (prop.value == "lightmap_light_m4")
-                        {
-                            matFlags = AI_IRRMESH_MAT_lightmap_light_m4;
-                        }
-                        else if (prop.value == "lightmap_add")
-                        {
-                            matFlags = AI_IRRMESH_MAT_lightmap_add;
-                        }
-                        // Normal and parallax maps are treated equally
-                        else if (prop.value == "normalmap_solid" ||
-                            prop.value == "parallaxmap_solid")
-                        {
-                            matFlags = AI_IRRMESH_MAT_normalmap_solid;
-                        }
-                        else if (prop.value == "normalmap_trans_vertex_alpha" ||
-                            prop.value == "parallaxmap_trans_vertex_alpha")
-                        {
-                            matFlags = AI_IRRMESH_MAT_normalmap_tva;
-                        }
-                        else if (prop.value == "normalmap_trans_add" ||
-                            prop.value == "parallaxmap_trans_add")
-                        {
-                            matFlags = AI_IRRMESH_MAT_normalmap_ta;
-                        }
-                        else {
-                            ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: " + prop.value);
-                        }
-                    }
-
-                    // Up to 4 texture channels are supported
-                    if (prop.name == "Texture1")
-                    {
-                        // Always accept the primary texture channel
-                        ++cnt;
-                        s.Set(prop.value);
-                        mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
-                    }
-                    else if (prop.name == "Texture2" && cnt == 1)
-                    {
-                        // 2-layer material lightmapped?
-                        if (matFlags & AI_IRRMESH_MAT_lightmap) {
-                            ++cnt;
-                            s.Set(prop.value);
-                            mat->AddProperty(&s,AI_MATKEY_TEXTURE_LIGHTMAP(0));
-
-                            // set the corresponding material flag
-                            matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
-                        }
-                        // alternatively: normal or parallax mapping
-                        else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) {
-                            ++cnt;
-                            s.Set(prop.value);
-                            mat->AddProperty(&s,AI_MATKEY_TEXTURE_NORMALS(0));
-
-                            // set the corresponding material flag
-                            matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
-                        } else if (matFlags & AI_IRRMESH_MAT_solid_2layer)    {// or just as second diffuse texture
-                            ++cnt;
-                            s.Set(prop.value);
-                            mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(1));
-                            ++nd;
-
-                            // set the corresponding material flag
-                            matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
-                        } else {
-                            ASSIMP_LOG_WARN("IRRmat: Skipping second texture");
-                        }
-                    } else if (prop.name == "Texture3" && cnt == 2) {
-                        // Irrlicht does not seem to use these channels.
-                        ++cnt;
-                        s.Set(prop.value);
-                        mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+1));
-                    } else if (prop.name == "Texture4" && cnt == 3) {
-                        // Irrlicht does not seem to use these channels.
-                        ++cnt;
-                        s.Set(prop.value);
-                        mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+2));
-                    }
-
-                    // Texture mapping options
-                    if (prop.name == "TextureWrap1" && cnt >= 1)
-                    {
-                        int map = ConvertMappingMode(prop.value);
-                        mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
-                        mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
-                    }
-                    else if (prop.name == "TextureWrap2" && cnt >= 2)
-                    {
-                        int map = ConvertMappingMode(prop.value);
-                        if (matFlags & AI_IRRMESH_MAT_lightmap) {
-                            mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0));
-                            mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0));
-                        }
-                        else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) {
-                            mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_NORMALS(0));
-                            mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_NORMALS(0));
-                        }
-                        else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
-                            mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1));
-                            mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1));
-                        }
-                    }
-                    else if (prop.name == "TextureWrap3" && cnt >= 3)
-                    {
-                        int map = ConvertMappingMode(prop.value);
-                        mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+1));
-                        mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+1));
-                    }
-                    else if (prop.name == "TextureWrap4" && cnt >= 4)
-                    {
-                        int map = ConvertMappingMode(prop.value);
-                        mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+2));
-                        mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+2));
-                    }
-                }
+            else if (prop.name == "Emissive") {
+                ColorFromARGBPacked(prop.value,clr);
+                mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
             }
-            break;
-            case EXN_ELEMENT_END:
-
-                /* Assume there are no further nested nodes in <material> elements
-                 */
-                if (/* IRRMESH */ !ASSIMP_stricmp(reader->getNodeName(),"material") ||
-                    /* IRR     */ !ASSIMP_stricmp(reader->getNodeName(),"attributes"))
+#endif
+		} else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties
+			FloatProperty prop;
+			ReadFloatProperty(prop);
+			if (prop.name == "Shininess") {
+				mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS);
+			}
+		} else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties
+			BoolProperty prop;
+			ReadBoolProperty(prop);
+			if (prop.name == "Wireframe") {
+				int val = (prop.value ? true : false);
+				mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME);
+			} else if (prop.name == "GouraudShading") {
+				int val = (prop.value ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
+				mat->AddProperty(&val, 1, AI_MATKEY_SHADING_MODEL);
+			} else if (prop.name == "BackfaceCulling") {
+				int val = (!prop.value);
+				mat->AddProperty(&val, 1, AI_MATKEY_TWOSIDED);
+			}
+		} else if (!ASSIMP_stricmp(child.name(), "texture") ||
+				   !ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties
+			StringProperty prop;
+			ReadStringProperty(prop);
+			if (prop.value.length()) {
+				// material type (shader)
+				if (prop.name == "Type") {
+					if (prop.value == "solid") {
+						// default material ...
+					} else if (prop.value == "trans_vertex_alpha") {
+						matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
+					} else if (prop.value == "lightmap") {
+						matFlags = AI_IRRMESH_MAT_lightmap;
+					} else if (prop.value == "solid_2layer") {
+						matFlags = AI_IRRMESH_MAT_solid_2layer;
+					} else if (prop.value == "lightmap_m2") {
+						matFlags = AI_IRRMESH_MAT_lightmap_m2;
+					} else if (prop.value == "lightmap_m4") {
+						matFlags = AI_IRRMESH_MAT_lightmap_m4;
+					} else if (prop.value == "lightmap_light") {
+						matFlags = AI_IRRMESH_MAT_lightmap_light;
+					} else if (prop.value == "lightmap_light_m2") {
+						matFlags = AI_IRRMESH_MAT_lightmap_light_m2;
+					} else if (prop.value == "lightmap_light_m4") {
+						matFlags = AI_IRRMESH_MAT_lightmap_light_m4;
+					} else if (prop.value == "lightmap_add") {
+						matFlags = AI_IRRMESH_MAT_lightmap_add;
+					} else if (prop.value == "normalmap_solid" ||
+							   prop.value == "parallaxmap_solid") { // Normal and parallax maps are treated equally
+						matFlags = AI_IRRMESH_MAT_normalmap_solid;
+					} else if (prop.value == "normalmap_trans_vertex_alpha" ||
+							   prop.value == "parallaxmap_trans_vertex_alpha") {
+						matFlags = AI_IRRMESH_MAT_normalmap_tva;
+					} else if (prop.value == "normalmap_trans_add" ||
+							   prop.value == "parallaxmap_trans_add") {
+						matFlags = AI_IRRMESH_MAT_normalmap_ta;
+					} else {
+						ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: " + prop.value);
+					}
+				}
+
+				// Up to 4 texture channels are supported
+				if (prop.name == "Texture1") {
+					// Always accept the primary texture channel
+					++cnt;
+					s.Set(prop.value);
+					mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
+				} else if (prop.name == "Texture2" && cnt == 1) {
+					// 2-layer material lightmapped?
+					if (matFlags & AI_IRRMESH_MAT_lightmap) {
+						++cnt;
+						s.Set(prop.value);
+						mat->AddProperty(&s, AI_MATKEY_TEXTURE_LIGHTMAP(0));
+
+						// set the corresponding material flag
+						matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+					} else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) { // alternatively: normal or parallax mapping
+						++cnt;
+						s.Set(prop.value);
+						mat->AddProperty(&s, AI_MATKEY_TEXTURE_NORMALS(0));
+
+						// set the corresponding material flag
+						matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+					} else if (matFlags & AI_IRRMESH_MAT_solid_2layer) { // or just as second diffuse texture
+						++cnt;
+						s.Set(prop.value);
+						mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(1));
+						++nd;
+
+						// set the corresponding material flag
+						matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+					} else {
+						ASSIMP_LOG_WARN("IRRmat: Skipping second texture");
+					}
+				} else if (prop.name == "Texture3" && cnt == 2) {
+					// Irrlicht does not seem to use these channels.
+					++cnt;
+					s.Set(prop.value);
+					mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 1));
+				} else if (prop.name == "Texture4" && cnt == 3) {
+					// Irrlicht does not seem to use these channels.
+					++cnt;
+					s.Set(prop.value);
+					mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 2));
+				}
+
+				// Texture mapping options
+				if (prop.name == "TextureWrap1" && cnt >= 1) {
+					int map = ConvertMappingMode(prop.value);
+					mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+					mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+				} else if (prop.name == "TextureWrap2" && cnt >= 2) {
+					int map = ConvertMappingMode(prop.value);
+					if (matFlags & AI_IRRMESH_MAT_lightmap) {
+						mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0));
+						mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0));
+					} else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) {
+						mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_NORMALS(0));
+						mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_NORMALS(0));
+					} else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
+						mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1));
+						mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1));
+					}
+				} else if (prop.name == "TextureWrap3" && cnt >= 3) {
+					int map = ConvertMappingMode(prop.value);
+					mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 1));
+					mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 1));
+				} else if (prop.name == "TextureWrap4" && cnt >= 4) {
+					int map = ConvertMappingMode(prop.value);
+					mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 2));
+					mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 2));
+				}
+			}
+		}
+		//break;
+		/*case EXN_ELEMENT_END:
+
+                // Assume there are no further nested nodes in <material> elements
+                if ( !ASSIMP_stricmp(reader->getNodeName(),"material") ||
+                     !ASSIMP_stricmp(reader->getNodeName(),"attributes"))
                 {
                     // Now process lightmapping flags
                     // We should have at least one textur to do that ..
@@ -492,7 +377,8 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
                 // GCC complains here ...
                 break;
         }
-    }
+    }*/
+	}
     ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
 
     return mat;

+ 45 - 44
code/AssetLib/Irr/IRRShared.h

@@ -7,50 +7,48 @@
 #ifndef INCLUDED_AI_IRRSHARED_H
 #define INCLUDED_AI_IRRSHARED_H
 
-#include <assimp/irrXMLWrapper.h>
 #include <assimp/BaseImporter.h>
+#include <assimp/XmlParser.h>
 #include <stdint.h>
 
 struct aiMaterial;
 
-namespace Assimp    {
-
+namespace Assimp {
 
 /** @brief Matrix to convert from Assimp to IRR and backwards
  */
 extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
 
-
 // Default: 0 = solid, one texture
-#define AI_IRRMESH_MAT_solid_2layer         0x10000
+#define AI_IRRMESH_MAT_solid_2layer 0x10000
 
 // Transparency flags
-#define AI_IRRMESH_MAT_trans_vertex_alpha   0x1
-#define AI_IRRMESH_MAT_trans_add            0x2
+#define AI_IRRMESH_MAT_trans_vertex_alpha 0x1
+#define AI_IRRMESH_MAT_trans_add 0x2
 
 // Lightmapping flags
-#define AI_IRRMESH_MAT_lightmap             0x2
-#define AI_IRRMESH_MAT_lightmap_m2          (AI_IRRMESH_MAT_lightmap|0x4)
-#define AI_IRRMESH_MAT_lightmap_m4          (AI_IRRMESH_MAT_lightmap|0x8)
-#define AI_IRRMESH_MAT_lightmap_light       (AI_IRRMESH_MAT_lightmap|0x10)
-#define AI_IRRMESH_MAT_lightmap_light_m2    (AI_IRRMESH_MAT_lightmap|0x20)
-#define AI_IRRMESH_MAT_lightmap_light_m4    (AI_IRRMESH_MAT_lightmap|0x40)
-#define AI_IRRMESH_MAT_lightmap_add         (AI_IRRMESH_MAT_lightmap|0x80)
+#define AI_IRRMESH_MAT_lightmap 0x2
+#define AI_IRRMESH_MAT_lightmap_m2 (AI_IRRMESH_MAT_lightmap | 0x4)
+#define AI_IRRMESH_MAT_lightmap_m4 (AI_IRRMESH_MAT_lightmap | 0x8)
+#define AI_IRRMESH_MAT_lightmap_light (AI_IRRMESH_MAT_lightmap | 0x10)
+#define AI_IRRMESH_MAT_lightmap_light_m2 (AI_IRRMESH_MAT_lightmap | 0x20)
+#define AI_IRRMESH_MAT_lightmap_light_m4 (AI_IRRMESH_MAT_lightmap | 0x40)
+#define AI_IRRMESH_MAT_lightmap_add (AI_IRRMESH_MAT_lightmap | 0x80)
 
 // Standard NormalMap (or Parallax map, they're treated equally)
-#define AI_IRRMESH_MAT_normalmap_solid      (0x100)
+#define AI_IRRMESH_MAT_normalmap_solid (0x100)
 
 // Normal map combined with vertex alpha
-#define AI_IRRMESH_MAT_normalmap_tva    \
+#define AI_IRRMESH_MAT_normalmap_tva \
     (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_vertex_alpha)
 
 // Normal map combined with additive transparency
-#define AI_IRRMESH_MAT_normalmap_ta     \
+#define AI_IRRMESH_MAT_normalmap_ta \
     (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_add)
 
 // Special flag. It indicates a second texture has been found
 // Its type depends ... either a normal textue or a normal map
-#define AI_IRRMESH_EXTRA_2ND_TEXTURE        0x100000
+#define AI_IRRMESH_EXTRA_2ND_TEXTURE 0x100000
 
 // ---------------------------------------------------------------------------
 /** Base class for the Irr and IrrMesh importers.
@@ -58,61 +56,64 @@ extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
  *  Declares some irrlight-related xml parsing utilities and provides tools
  *  to load materials from IRR and IRRMESH files.
  */
-class IrrlichtBase
-{
+class IrrlichtBase {
 protected:
+    IrrlichtBase() :
+            mNode(nullptr) {
+        // empty
+    }
+
+    ~IrrlichtBase() {
+        // empty
+    }
 
     /** @brief Data structure for a simple name-value property
      */
     template <class T>
-    struct Property
-    {
+    struct Property {
         std::string name;
         T value;
     };
 
-    typedef Property<uint32_t>      HexProperty;
-    typedef Property<std::string>   StringProperty;
-    typedef Property<bool>          BoolProperty;
-    typedef Property<float>         FloatProperty;
-    typedef Property<aiVector3D>    VectorProperty;
-    typedef Property<int>           IntProperty;
+    typedef Property<uint32_t> HexProperty;
+    typedef Property<std::string> StringProperty;
+    typedef Property<bool> BoolProperty;
+    typedef Property<float> FloatProperty;
+    typedef Property<aiVector3D> VectorProperty;
+    typedef Property<int> IntProperty;
 
-    /** XML reader instance
-     */
-  irr::io::IrrXMLReader* reader;
+    /// XML reader instance
+    XmlParser mParser;
+    pugi::xml_node *mNode;
 
     // -------------------------------------------------------------------
     /** Parse a material description from the XML
      *  @return The created material
      *  @param matFlags Receives AI_IRRMESH_MAT_XX flags
      */
-    aiMaterial* ParseMaterial(unsigned int& matFlags);
+    aiMaterial *ParseMaterial(unsigned int &matFlags);
 
     // -------------------------------------------------------------------
     /** Read a property of the specified type from the current XML element.
      *  @param out Receives output data
      */
-    void ReadHexProperty    (HexProperty&    out);
-    void ReadStringProperty (StringProperty& out);
-    void ReadBoolProperty   (BoolProperty&   out);
-    void ReadFloatProperty  (FloatProperty&  out);
-    void ReadVectorProperty (VectorProperty&  out);
-    void ReadIntProperty    (IntProperty&    out);
+    void ReadHexProperty(HexProperty &out);
+    void ReadStringProperty(StringProperty &out);
+    void ReadBoolProperty(BoolProperty &out);
+    void ReadFloatProperty(FloatProperty &out);
+    void ReadVectorProperty(VectorProperty &out);
+    void ReadIntProperty(IntProperty &out);
 };
 
-
 // ------------------------------------------------------------------------------------------------
 // Unpack a hex color, e.g. 0xdcdedfff
-inline void ColorFromARGBPacked(uint32_t in, aiColor4D& clr)
-{
+inline void ColorFromARGBPacked(uint32_t in, aiColor4D &clr) {
     clr.a = ((in >> 24) & 0xff) / 255.f;
     clr.r = ((in >> 16) & 0xff) / 255.f;
-    clr.g = ((in >>  8) & 0xff) / 255.f;
-    clr.b = ((in      ) & 0xff) / 255.f;
+    clr.g = ((in >> 8) & 0xff) / 255.f;
+    clr.b = ((in)&0xff) / 255.f;
 }
 
-
 } // end namespace Assimp
 
 #endif // !! INCLUDED_AI_IRRSHARED_H

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

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

+ 11 - 6
code/AssetLib/LWS/LWSLoader.cpp

@@ -502,7 +502,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open LWS file " + pFile + ".");
+        throw DeadlyImportError("Failed to open LWS file ", pFile, ".");
     }
 
     // Allocate storage and copy the contents of the file to a memory buffer
@@ -750,12 +750,17 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
         }
         // 'LightIntensity': set intensity of currently active light
         else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity") {
-            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+            if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) {
                 ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'LightIntensity\'");
-
-            else
-                fast_atoreal_move<float>(c, nodes.back().lightIntensity);
-
+            } else {
+                const std::string env = "(envelope)";
+                if (0 == strncmp(c, env.c_str(), env.size())) {
+                    ASSIMP_LOG_ERROR("LWS: envelopes for  LightIntensity not supported, set to 1.0");
+                    nodes.back().lightIntensity = (ai_real)1.0;
+                } else {
+                    fast_atoreal_move<float>(c, nodes.back().lightIntensity);
+                }
+            }
         }
         // 'LightType': set type of currently active light
         else if ((*it).tokens[0] == "LightType") {

+ 5 - 2
code/AssetLib/M3D/M3DExporter.cpp

@@ -197,12 +197,15 @@ M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) {
                         break;
                     case m3dpf_float:
                         if (mat->Get(aiProps[k].pKey, aiProps[k].type,
-                                    aiProps[k].index, f) == AI_SUCCESS)
+                                    aiProps[k].index, f) == AI_SUCCESS) {
+                            uint32_t f_uint32;
+                            memcpy(&f_uint32, &f, sizeof(uint32_t));
                             addProp(&m3d->material[mi],
                                     m3d_propertytypes[k].id,
                                     /* not (uint32_t)f, because we don't want to convert
                                          * it, we want to see it as 32 bits of memory */
-                                    *((uint32_t *)&f));
+                                    f_uint32);
+                        }
                         break;
                     case m3dpf_uint8:
                         if (mat->Get(aiProps[k].pKey, aiProps[k].type,

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

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

+ 3 - 0
code/AssetLib/M3D/M3DWrapper.cpp

@@ -133,6 +133,9 @@ unsigned char *M3DWrapper::Save(int quality, int flags, unsigned int &size) {
     saved_output_ = m3d_save(m3d_, quality, flags, &size);
     return saved_output_;
 #else
+    (void)quality;
+    (void)flags;
+    (void)size;
     return nullptr;
 #endif
 }

+ 1 - 1
code/AssetLib/M3D/m3d.h

@@ -3590,7 +3590,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d
                         case 4: f = (float)(*((float *)(data + 0))); break;
                         case 8: f = (float)(*((double *)(data + 0))); break;
                         }
-                        h->cmd[i].arg[k] = *((uint32_t *)&f);
+                        memcpy(&(h->cmd[i].arg[k]), &f, sizeof(uint32_t));
                         data += model->vc_s;
                         break;
                     case m3dcp_hi_t: data = _m3d_getidx(data, model->hi_s, &h->cmd[i].arg[k]); break;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -524,7 +524,7 @@ namespace pmx
 		if (version != 2.0f && version != 2.1f)
 		{
 			std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
-            throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version));
+            throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but ", to_string(version));
     }
 		this->setting.Read(stream);
 

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

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

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

@@ -214,7 +214,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (!file.get())
-        throw DeadlyImportError("Failed to open NFF file " + pFile + ".");
+        throw DeadlyImportError("Failed to open NFF file ", pFile, ".");
 
     // allocate storage and copy the contents of the file to a memory buffer
     // (terminate it with zero)

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

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

+ 20 - 15
code/AssetLib/Obj/ObjFileImporter.cpp

@@ -75,7 +75,9 @@ using namespace std;
 // ------------------------------------------------------------------------------------------------
 //  Default constructor
 ObjFileImporter::ObjFileImporter() :
-        m_Buffer(), m_pRootObject(nullptr), m_strAbsPath(std::string(1, DefaultIOSystem().getOsSeparator())) {}
+        m_Buffer(),
+        m_pRootObject(nullptr),
+        m_strAbsPath(std::string(1, DefaultIOSystem().getOsSeparator())) {}
 
 // ------------------------------------------------------------------------------------------------
 //  Destructor.
@@ -107,9 +109,12 @@ const aiImporterDesc *ObjFileImporter::GetInfo() const {
 void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
     // Read file into memory
     static const std::string mode = "rb";
-    std::unique_ptr<IOStream> fileStream(pIOHandler->Open(file, mode));
+    auto streamCloser = [&](IOStream *pStream) {
+        pIOHandler->Close(pStream);
+    };
+    std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
     if (!fileStream.get()) {
-        throw DeadlyImportError("Failed to open file " + file + ".");
+        throw DeadlyImportError("Failed to open file ", file, ".");
     }
 
     // Get the file-size and validate it, throwing an exception when fails
@@ -589,18 +594,18 @@ void ObjFileImporter::createMaterials(const ObjFile::Model *pModel, aiScene *pSc
         // convert illumination model
         int sm = 0;
         switch (pCurrentMaterial->illumination_model) {
-            case 0:
-                sm = aiShadingMode_NoShading;
-                break;
-            case 1:
-                sm = aiShadingMode_Gouraud;
-                break;
-            case 2:
-                sm = aiShadingMode_Phong;
-                break;
-            default:
-                sm = aiShadingMode_Gouraud;
-                ASSIMP_LOG_ERROR("OBJ: unexpected illumination model (0-2 recognized)");
+        case 0:
+            sm = aiShadingMode_NoShading;
+            break;
+        case 1:
+            sm = aiShadingMode_Gouraud;
+            break;
+        case 2:
+            sm = aiShadingMode_Phong;
+            break;
+        default:
+            sm = aiShadingMode_Gouraud;
+            ASSIMP_LOG_ERROR("OBJ: unexpected illumination model (0-2 recognized)");
         }
 
         mat->AddProperty<int>(&sm, 1, AI_MATKEY_SHADING_MODEL);

+ 6 - 2
code/AssetLib/Obj/ObjFileMtlImporter.cpp

@@ -137,10 +137,14 @@ void ObjFileMtlImporter::load() {
             } break;
             case 'T': {
                 ++m_DataIt;
-                if (*m_DataIt == 'f') // Material transmission
-                {
+                // Material transmission color
+                if (*m_DataIt == 'f')  {
                     ++m_DataIt;
                     getColorRGBA(&m_pModel->m_pCurrentMaterial->transparent);
+                } else if (*m_DataIt == 'r')  {
+                    // Material transmission alpha value
+                    ++m_DataIt;
+                    getFloatValue(m_pModel->m_pCurrentMaterial->alpha);                    
                 }
                 m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
             } break;

+ 0 - 29
code/AssetLib/Obj/ObjTools.h

@@ -234,35 +234,6 @@ inline char_t getFloat(char_t it, char_t end, ai_real &value) {
     return it;
 }
 
-/** @brief  Will perform a simple tokenize.
- *  @param  str         String to tokenize.
- *  @param  tokens      Array with tokens, will be empty if no token was found.
- *  @param  delimiters  Delimiter for tokenize.
- *  @return Number of found token.
- */
-template <class string_type>
-unsigned int tokenize(const string_type &str, std::vector<string_type> &tokens,
-        const string_type &delimiters) {
-    // Skip delimiters at beginning.
-    typename string_type::size_type lastPos = str.find_first_not_of(delimiters, 0);
-
-    // Find first "non-delimiter".
-    typename string_type::size_type pos = str.find_first_of(delimiters, lastPos);
-    while (string_type::npos != pos || string_type::npos != lastPos) {
-        // Found a token, add it to the vector.
-        string_type tmp = str.substr(lastPos, pos - lastPos);
-        if (!tmp.empty() && ' ' != tmp[0])
-            tokens.push_back(tmp);
-
-        // Skip delimiters.  Note the "not_of"
-        lastPos = str.find_first_not_of(delimiters, pos);
-
-        // Find next "non-delimiter"
-        pos = str.find_first_of(delimiters, lastPos);
-    }
-
-    return static_cast<unsigned int>(tokens.size());
-}
 
 template <class string_type>
 string_type trim_whitespaces(string_type str) {

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

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

+ 192 - 200
code/AssetLib/Ogre/OgreBinarySerializer.h

@@ -4,7 +4,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,
@@ -48,16 +47,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "OgreStructs.h"
 #include <assimp/StreamReader.h>
 
-namespace Assimp
-{
-namespace Ogre
-{
+namespace Assimp {
+namespace Ogre {
 
 typedef Assimp::StreamReaderLE MemoryStreamReader;
 typedef std::shared_ptr<MemoryStreamReader> MemoryStreamReaderPtr;
 
-class OgreBinarySerializer
-{
+class OgreBinarySerializer {
 public:
     /// Imports mesh and returns the result.
     /** @note Fatal unrecoverable errors will throw a DeadlyImportError. */
@@ -71,17 +67,15 @@ public:
     static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh);
 
 private:
-    enum AssetMode
-    {
+    enum AssetMode {
         AM_Mesh,
         AM_Skeleton
     };
 
     OgreBinarySerializer(MemoryStreamReader *reader, AssetMode mode) :
-        m_currentLen(0),
-        m_reader(reader),
-        assetMode(mode)
-    {
+            m_currentLen(0),
+            m_reader(reader),
+            assetMode(mode) {
     }
 
     static MemoryStreamReaderPtr OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename);
@@ -136,7 +130,7 @@ private:
     // Reader utils
     bool AtEnd() const;
 
-    template<typename T>
+    template <typename T>
     inline T Read();
 
     void ReadBytes(char *dest, size_t numBytes);
@@ -158,155 +152,154 @@ private:
     AssetMode assetMode;
 };
 
-enum MeshChunkId
-{
+enum MeshChunkId {
     M_HEADER = 0x1000,
-        // char*          version          : Version number check
-    M_MESH   = 0x3000,
-        // bool skeletallyAnimated   // important flag which affects h/w buffer policies
-        // Optional M_GEOMETRY chunk
-        M_SUBMESH            = 0x4000,
-            // char* materialName
-            // bool useSharedVertices
-            // unsigned int indexCount
-            // bool indexes32Bit
-            // unsigned int* faceVertexIndices (indexCount)
-            // OR
-            // unsigned short* faceVertexIndices (indexCount)
-            // M_GEOMETRY chunk (Optional: present only if useSharedVertices = false)
-            M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing
-                // unsigned short operationType
-            M_SUBMESH_BONE_ASSIGNMENT = 0x4100,
-                // Optional bone weights (repeating section)
-                // unsigned int vertexIndex;
-                // unsigned short boneIndex;
-                // float weight;
-            // Optional chunk that matches a texture name to an alias
-            // a texture alias is sent to the submesh material to use this texture name
-            // instead of the one in the texture unit with a matching alias name
-            M_SUBMESH_TEXTURE_ALIAS = 0x4200, // Repeating section
-                // char* aliasName;
-                // char* textureName;
-
-        M_GEOMETRY        = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH
-            // unsigned int vertexCount
-            M_GEOMETRY_VERTEX_DECLARATION = 0x5100,
-                M_GEOMETRY_VERTEX_ELEMENT = 0x5110, // Repeating section
-                    // unsigned short source;   // buffer bind source
-                    // unsigned short type;     // VertexElementType
-                    // unsigned short semantic; // VertexElementSemantic
-                    // unsigned short offset;   // start offset in buffer in bytes
-                    // unsigned short index;    // index of the semantic (for colours and texture coords)
-            M_GEOMETRY_VERTEX_BUFFER = 0x5200, // Repeating section
-                // unsigned short bindIndex;    // Index to bind this buffer to
-                // unsigned short vertexSize;   // Per-vertex size, must agree with declaration at this index
-                M_GEOMETRY_VERTEX_BUFFER_DATA = 0x5210,
-                    // raw buffer data
-        M_MESH_SKELETON_LINK = 0x6000,
-            // Optional link to skeleton
-            // char* skeletonName          : name of .skeleton to use
-        M_MESH_BONE_ASSIGNMENT = 0x7000,
-            // Optional bone weights (repeating section)
-            // unsigned int vertexIndex;
-            // unsigned short boneIndex;
-            // float weight;
-        M_MESH_LOD = 0x8000,
-            // Optional LOD information
-            // string strategyName;
-            // unsigned short numLevels;
-            // bool manual;  (true for manual alternate meshes, false for generated)
-            M_MESH_LOD_USAGE = 0x8100,
-            // Repeating section, ordered in increasing depth
-            // NB LOD 0 (full detail from 0 depth) is omitted
-            // LOD value - this is a distance, a pixel count etc, based on strategy
-            // float lodValue;
-                M_MESH_LOD_MANUAL = 0x8110,
-                // Required if M_MESH_LOD section manual = true
-                // String manualMeshName;
-                M_MESH_LOD_GENERATED = 0x8120,
-                // Required if M_MESH_LOD section manual = false
-                // Repeating section (1 per submesh)
-                // unsigned int indexCount;
-                // bool indexes32Bit
-                // unsigned short* faceIndexes;  (indexCount)
-                // OR
-                // unsigned int* faceIndexes;  (indexCount)
-        M_MESH_BOUNDS = 0x9000,
-            // float minx, miny, minz
-            // float maxx, maxy, maxz
-            // float radius
-
-        // Added By DrEvil
-        // optional chunk that contains a table of submesh indexes and the names of
-        // the sub-meshes.
-        M_SUBMESH_NAME_TABLE = 0xA000,
-            // Subchunks of the name table. Each chunk contains an index & string
-            M_SUBMESH_NAME_TABLE_ELEMENT = 0xA100,
-                // short index
-                // char* name
-        // Optional chunk which stores precomputed edge data
-        M_EDGE_LISTS = 0xB000,
-            // Each LOD has a separate edge list
-            M_EDGE_LIST_LOD = 0xB100,
-                // unsigned short lodIndex
-                // bool isManual            // If manual, no edge data here, loaded from manual mesh
-                    // bool isClosed
-                    // unsigned long numTriangles
-                    // unsigned long numEdgeGroups
-                    // Triangle* triangleList
-                        // unsigned long indexSet
-                        // unsigned long vertexSet
-                        // unsigned long vertIndex[3]
-                        // unsigned long sharedVertIndex[3]
-                        // float normal[4]
-
-                    M_EDGE_GROUP = 0xB110,
-                        // unsigned long vertexSet
-                        // unsigned long triStart
-                        // unsigned long triCount
-                        // unsigned long numEdges
-                        // Edge* edgeList
-                            // unsigned long  triIndex[2]
-                            // unsigned long  vertIndex[2]
-                            // unsigned long  sharedVertIndex[2]
-                            // bool degenerate
-        // Optional poses section, referred to by pose keyframes
-        M_POSES = 0xC000,
-            M_POSE = 0xC100,
-                // char* name (may be blank)
-                // unsigned short target    // 0 for shared geometry,
-                                            // 1+ for submesh index + 1
-                // bool includesNormals [1.8+]
-                M_POSE_VERTEX = 0xC111,
-                    // unsigned long vertexIndex
-                    // float xoffset, yoffset, zoffset
-                    // float xnormal, ynormal, znormal (optional, 1.8+)
-        // Optional vertex animation chunk
-        M_ANIMATIONS = 0xD000,
-            M_ANIMATION = 0xD100,
-            // char* name
-            // float length
-            M_ANIMATION_BASEINFO = 0xD105,
-            // [Optional] base keyframe information (pose animation only)
-            // char* baseAnimationName (blank for self)
-            // float baseKeyFrameTime
-            M_ANIMATION_TRACK = 0xD110,
-                // unsigned short type          // 1 == morph, 2 == pose
-                // unsigned short target        // 0 for shared geometry,
-                                                // 1+ for submesh index + 1
-                M_ANIMATION_MORPH_KEYFRAME = 0xD111,
-                    // float time
-                    // bool includesNormals [1.8+]
-                    // float x,y,z          // repeat by number of vertices in original geometry
-                M_ANIMATION_POSE_KEYFRAME = 0xD112,
-                    // float time
-                    M_ANIMATION_POSE_REF = 0xD113, // repeat for number of referenced poses
-                        // unsigned short poseIndex
-                        // float influence
-        // Optional submesh extreme vertex list chink
-        M_TABLE_EXTREMES = 0xE000
-        // unsigned short submesh_index;
-        // float extremes [n_extremes][3];
+    // char*          version          : Version number check
+    M_MESH = 0x3000,
+    // bool skeletallyAnimated   // important flag which affects h/w buffer policies
+    // Optional M_GEOMETRY chunk
+    M_SUBMESH = 0x4000,
+    // char* materialName
+    // bool useSharedVertices
+    // unsigned int indexCount
+    // bool indexes32Bit
+    // unsigned int* faceVertexIndices (indexCount)
+    // OR
+    // unsigned short* faceVertexIndices (indexCount)
+    // M_GEOMETRY chunk (Optional: present only if useSharedVertices = false)
+    M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing
+    // unsigned short operationType
+    M_SUBMESH_BONE_ASSIGNMENT = 0x4100,
+    // Optional bone weights (repeating section)
+    // unsigned int vertexIndex;
+    // unsigned short boneIndex;
+    // float weight;
+    // Optional chunk that matches a texture name to an alias
+    // a texture alias is sent to the submesh material to use this texture name
+    // instead of the one in the texture unit with a matching alias name
+    M_SUBMESH_TEXTURE_ALIAS = 0x4200, // Repeating section
+    // char* aliasName;
+    // char* textureName;
+
+    M_GEOMETRY = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH
+    // unsigned int vertexCount
+    M_GEOMETRY_VERTEX_DECLARATION = 0x5100,
+    M_GEOMETRY_VERTEX_ELEMENT = 0x5110, // Repeating section
+    // unsigned short source;   // buffer bind source
+    // unsigned short type;     // VertexElementType
+    // unsigned short semantic; // VertexElementSemantic
+    // unsigned short offset;   // start offset in buffer in bytes
+    // unsigned short index;    // index of the semantic (for colours and texture coords)
+    M_GEOMETRY_VERTEX_BUFFER = 0x5200, // Repeating section
+    // unsigned short bindIndex;    // Index to bind this buffer to
+    // unsigned short vertexSize;   // Per-vertex size, must agree with declaration at this index
+    M_GEOMETRY_VERTEX_BUFFER_DATA = 0x5210,
+    // raw buffer data
+    M_MESH_SKELETON_LINK = 0x6000,
+    // Optional link to skeleton
+    // char* skeletonName          : name of .skeleton to use
+    M_MESH_BONE_ASSIGNMENT = 0x7000,
+    // Optional bone weights (repeating section)
+    // unsigned int vertexIndex;
+    // unsigned short boneIndex;
+    // float weight;
+    M_MESH_LOD = 0x8000,
+    // Optional LOD information
+    // string strategyName;
+    // unsigned short numLevels;
+    // bool manual;  (true for manual alternate meshes, false for generated)
+    M_MESH_LOD_USAGE = 0x8100,
+    // Repeating section, ordered in increasing depth
+    // NB LOD 0 (full detail from 0 depth) is omitted
+    // LOD value - this is a distance, a pixel count etc, based on strategy
+    // float lodValue;
+    M_MESH_LOD_MANUAL = 0x8110,
+    // Required if M_MESH_LOD section manual = true
+    // String manualMeshName;
+    M_MESH_LOD_GENERATED = 0x8120,
+    // Required if M_MESH_LOD section manual = false
+    // Repeating section (1 per submesh)
+    // unsigned int indexCount;
+    // bool indexes32Bit
+    // unsigned short* faceIndexes;  (indexCount)
+    // OR
+    // unsigned int* faceIndexes;  (indexCount)
+    M_MESH_BOUNDS = 0x9000,
+    // float minx, miny, minz
+    // float maxx, maxy, maxz
+    // float radius
+
+    // Added By DrEvil
+    // optional chunk that contains a table of submesh indexes and the names of
+    // the sub-meshes.
+    M_SUBMESH_NAME_TABLE = 0xA000,
+    // Subchunks of the name table. Each chunk contains an index & string
+    M_SUBMESH_NAME_TABLE_ELEMENT = 0xA100,
+    // short index
+    // char* name
+    // Optional chunk which stores precomputed edge data
+    M_EDGE_LISTS = 0xB000,
+    // Each LOD has a separate edge list
+    M_EDGE_LIST_LOD = 0xB100,
+    // unsigned short lodIndex
+    // bool isManual            // If manual, no edge data here, loaded from manual mesh
+    // bool isClosed
+    // unsigned long numTriangles
+    // unsigned long numEdgeGroups
+    // Triangle* triangleList
+    // unsigned long indexSet
+    // unsigned long vertexSet
+    // unsigned long vertIndex[3]
+    // unsigned long sharedVertIndex[3]
+    // float normal[4]
+
+    M_EDGE_GROUP = 0xB110,
+    // unsigned long vertexSet
+    // unsigned long triStart
+    // unsigned long triCount
+    // unsigned long numEdges
+    // Edge* edgeList
+    // unsigned long  triIndex[2]
+    // unsigned long  vertIndex[2]
+    // unsigned long  sharedVertIndex[2]
+    // bool degenerate
+    // Optional poses section, referred to by pose keyframes
+    M_POSES = 0xC000,
+    M_POSE = 0xC100,
+    // char* name (may be blank)
+    // unsigned short target    // 0 for shared geometry,
+    // 1+ for submesh index + 1
+    // bool includesNormals [1.8+]
+    M_POSE_VERTEX = 0xC111,
+    // unsigned long vertexIndex
+    // float xoffset, yoffset, zoffset
+    // float xnormal, ynormal, znormal (optional, 1.8+)
+    // Optional vertex animation chunk
+    M_ANIMATIONS = 0xD000,
+    M_ANIMATION = 0xD100,
+    // char* name
+    // float length
+    M_ANIMATION_BASEINFO = 0xD105,
+    // [Optional] base keyframe information (pose animation only)
+    // char* baseAnimationName (blank for self)
+    // float baseKeyFrameTime
+    M_ANIMATION_TRACK = 0xD110,
+    // unsigned short type          // 1 == morph, 2 == pose
+    // unsigned short target        // 0 for shared geometry,
+    // 1+ for submesh index + 1
+    M_ANIMATION_MORPH_KEYFRAME = 0xD111,
+    // float time
+    // bool includesNormals [1.8+]
+    // float x,y,z          // repeat by number of vertices in original geometry
+    M_ANIMATION_POSE_KEYFRAME = 0xD112,
+    // float time
+    M_ANIMATION_POSE_REF = 0xD113, // repeat for number of referenced poses
+    // unsigned short poseIndex
+    // float influence
+    // Optional submesh extreme vertex list chink
+    M_TABLE_EXTREMES = 0xE000
+    // unsigned short submesh_index;
+    // float extremes [n_extremes][3];
 };
 
 /*
@@ -353,49 +346,48 @@ static std::string MeshHeaderToString(MeshChunkId id)
 }
 */
 
-enum SkeletonChunkId
-{
-    SKELETON_HEADER             = 0x1000,
-        // char* version           : Version number check
-        SKELETON_BLENDMODE      = 0x1010, // optional
-            // unsigned short blendmode     : SkeletonAnimationBlendMode
-    SKELETON_BONE               = 0x2000,
+enum SkeletonChunkId {
+    SKELETON_HEADER = 0x1000,
+    // char* version           : Version number check
+    SKELETON_BLENDMODE = 0x1010, // optional
+    // unsigned short blendmode     : SkeletonAnimationBlendMode
+    SKELETON_BONE = 0x2000,
     // Repeating section defining each bone in the system.
     // Bones are assigned indexes automatically based on their order of declaration
     // starting with 0.
-        // char* name                      : name of the bone
-        // unsigned short handle            : handle of the bone, should be contiguous & start at 0
-        // Vector3 position              : position of this bone relative to parent
-        // Quaternion orientation          : orientation of this bone relative to parent
-        // Vector3 scale                    : scale of this bone relative to parent
-    SKELETON_BONE_PARENT        = 0x3000,
+    // char* name                      : name of the bone
+    // unsigned short handle            : handle of the bone, should be contiguous & start at 0
+    // Vector3 position              : position of this bone relative to parent
+    // Quaternion orientation          : orientation of this bone relative to parent
+    // Vector3 scale                    : scale of this bone relative to parent
+    SKELETON_BONE_PARENT = 0x3000,
     // Record of the parent of a single bone, used to build the node tree
     // Repeating section, listed in Bone Index order, one per Bone
-        // unsigned short handle             : child bone
-        // unsigned short parentHandle   : parent bone
-    SKELETON_ANIMATION          = 0x4000,
+    // unsigned short handle             : child bone
+    // unsigned short parentHandle   : parent bone
+    SKELETON_ANIMATION = 0x4000,
     // A single animation for this skeleton
-        // char* name                      : Name of the animation
-        // float length                   : Length of the animation in seconds
-        SKELETON_ANIMATION_BASEINFO = 0x4010,
-        // [Optional] base keyframe information
-        // char* baseAnimationName (blank for self)
-        // float baseKeyFrameTime
-        SKELETON_ANIMATION_TRACK    = 0x4100,
-        // A single animation track (relates to a single bone)
-        // Repeating section (within SKELETON_ANIMATION)
-            // unsigned short boneIndex  : Index of bone to apply to
-            SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110,
-            // A single keyframe within the track
-            // Repeating section
-                // float time                   : The time position (seconds)
-                // Quaternion rotate            : Rotation to apply at this keyframe
-                // Vector3 translate            : Translation to apply at this keyframe
-                // Vector3 scale                : Scale to apply at this keyframe
-    SKELETON_ANIMATION_LINK     = 0x5000
+    // char* name                      : Name of the animation
+    // float length                   : Length of the animation in seconds
+    SKELETON_ANIMATION_BASEINFO = 0x4010,
+    // [Optional] base keyframe information
+    // char* baseAnimationName (blank for self)
+    // float baseKeyFrameTime
+    SKELETON_ANIMATION_TRACK = 0x4100,
+    // A single animation track (relates to a single bone)
+    // Repeating section (within SKELETON_ANIMATION)
+    // unsigned short boneIndex  : Index of bone to apply to
+    SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110,
+    // A single keyframe within the track
+    // Repeating section
+    // float time                   : The time position (seconds)
+    // Quaternion rotate            : Rotation to apply at this keyframe
+    // Vector3 translate            : Translation to apply at this keyframe
+    // Vector3 scale                : Scale to apply at this keyframe
+    SKELETON_ANIMATION_LINK = 0x5000
     // Link to another skeleton, to re-use its animations
-        // char* skeletonName                   : name of skeleton to get animations from
-        // float scale                          : scale to apply to trans/scale keys
+    // char* skeletonName                   : name of skeleton to get animations from
+    // float scale                          : scale to apply to trans/scale keys
 };
 
 /*
@@ -416,8 +408,8 @@ static std::string SkeletonHeaderToString(SkeletonChunkId id)
     return "Unknown_SkeletonChunkId";
 }
 */
-} // Ogre
-} // Assimp
+} // namespace Ogre
+} // namespace Assimp
 
 #endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
 #endif // AI_OGREBINARYSERIALIZER_H_INC

+ 20 - 29
code/AssetLib/Ogre/OgreImporter.cpp

@@ -44,8 +44,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "OgreImporter.h"
 #include "OgreBinarySerializer.h"
 #include "OgreXmlSerializer.h"
-#include <assimp/Importer.hpp>
 #include <assimp/importerdesc.h>
+#include <assimp/Importer.hpp>
 #include <memory>
 
 static const aiImporterDesc desc = {
@@ -61,51 +61,41 @@ static const aiImporterDesc desc = {
     "mesh mesh.xml"
 };
 
-namespace Assimp
-{
-namespace Ogre
-{
+namespace Assimp {
+namespace Ogre {
 
-const aiImporterDesc* OgreImporter::GetInfo() const
-{
+const aiImporterDesc *OgreImporter::GetInfo() const {
     return &desc;
 }
 
-void OgreImporter::SetupProperties(const Importer* pImp)
-{
+void OgreImporter::SetupProperties(const Importer *pImp) {
     m_userDefinedMaterialLibFile = pImp->GetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "Scene.material");
     m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false);
 }
 
-bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
-{
+bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const {
     if (!checkSig) {
         return EndsWith(pFile, ".mesh.xml", false) || EndsWith(pFile, ".mesh", false);
     }
 
-    if (EndsWith(pFile, ".mesh.xml", false))
-    {
-        const char* tokens[] = { "<mesh>" };
+    if (EndsWith(pFile, ".mesh.xml", false)) {
+        const char *tokens[] = { "<mesh>" };
         return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
-    }
-    else
-    {
+    } else {
         /// @todo Read and validate first header chunk?
         return EndsWith(pFile, ".mesh", false);
     }
 }
 
-void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
-{
+void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler) {
     // Open source file
     IOStream *f = pIOHandler->Open(pFile, "rb");
     if (!f) {
-        throw DeadlyImportError("Failed to open file " + pFile);
+        throw DeadlyImportError("Failed to open file ", pFile);
     }
 
     // Binary .mesh import
-    if (EndsWith(pFile, ".mesh", false))
-    {
+    if (EndsWith(pFile, ".mesh", false)) {
         /// @note MemoryStreamReader takes ownership of f.
         MemoryStreamReader reader(f);
 
@@ -122,15 +112,16 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
         mesh->ConvertToAssimpScene(pScene);
     }
     // XML .mesh.xml import
-    else
-    {
+    else {
         /// @note XmlReader does not take ownership of f, hence the scoped ptr.
         std::unique_ptr<IOStream> scopedFile(f);
-        std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(scopedFile.get()));
-        std::unique_ptr<XmlReader> reader(irr::io::createIrrXMLReader(xmlStream.get()));
+        XmlParser xmlParser;
 
+        //std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(scopedFile.get()));
+        //std::unique_ptr<XmlReader> reader(irr::io::createIrrXMLReader(xmlStream.get()));
+        xmlParser.parse(scopedFile.get());
         // Import mesh
-        std::unique_ptr<MeshXml> mesh(OgreXmlSerializer::ImportMesh(reader.get()));
+        std::unique_ptr<MeshXml> mesh(OgreXmlSerializer::ImportMesh(&xmlParser));
 
         // Import skeleton
         OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh.get());
@@ -143,7 +134,7 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
     }
 }
 
-} // Ogre
-} // Assimp
+} // namespace Ogre
+} // namespace Assimp
 
 #endif // ASSIMP_BUILD_NO_OGRE_IMPORTER

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

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

File diff suppressed because it is too large
+ 318 - 428
code/AssetLib/Ogre/OgreXmlSerializer.cpp


+ 32 - 48
code/AssetLib/Ogre/OgreXmlSerializer.h

@@ -46,73 +46,57 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
 
 #include "OgreStructs.h"
-#include <assimp/irrXMLWrapper.h>
+#include <assimp/XmlParser.h>
 
-namespace Assimp
-{
-namespace Ogre
-{
+namespace Assimp {
 
-typedef irr::io::IrrXMLReader XmlReader;
-typedef std::shared_ptr<XmlReader> XmlReaderPtr;
+namespace Ogre {
 
-class OgreXmlSerializer
-{
+using XmlParserPtr = std::shared_ptr<::Assimp::XmlParser> ;
+
+class OgreXmlSerializer {
 public:
     /// Imports mesh and returns the result.
-    /** @note Fatal unrecoverable errors will throw a DeadlyImportError. */
-    static MeshXml *ImportMesh(XmlReader *reader);
+    /// @note Fatal unrecoverable errors will throw a DeadlyImportError.
+    static MeshXml *ImportMesh(XmlParser *parser);
 
     /// Imports skeleton to @c mesh.
-    /** If mesh does not have a skeleton reference or the skeleton file
-        cannot be found it is not a fatal DeadlyImportError.
-        @return If skeleton import was successful. */
-    static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh);
-    static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh);
+    /// If mesh does not have a skeleton reference or the skeleton file
+    /// cannot be found it is not a fatal DeadlyImportError.
+    /// @return If skeleton import was successful.
+    static bool ImportSkeleton(IOSystem *pIOHandler, MeshXml *mesh);
+    static bool ImportSkeleton(IOSystem *pIOHandler, Mesh *mesh);
 
 private:
-    explicit OgreXmlSerializer(XmlReader *reader) :
-        m_reader(reader)
-    {
-    }
+    explicit OgreXmlSerializer(XmlParser *xmlParser);
 
-    static XmlReaderPtr OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename);
+    static XmlParserPtr OpenXmlParser(Assimp::IOSystem *pIOHandler, const std::string &filename);
 
     // Mesh
     void ReadMesh(MeshXml *mesh);
-    void ReadSubMesh(MeshXml *mesh);
-
-    void ReadGeometry(VertexDataXml *dest);
-    void ReadGeometryVertexBuffer(VertexDataXml *dest);
-
-    void ReadBoneAssignments(VertexDataXml *dest);
+    void ReadSubMesh(XmlNode &node, MeshXml *mesh);
+    void ReadGeometry(XmlNode &node, VertexDataXml *dest);
+    void ReadGeometryVertexBuffer(XmlNode &node, VertexDataXml *dest);
+    void ReadBoneAssignments(XmlNode &node, VertexDataXml *dest);
 
     // Skeleton
-    void ReadSkeleton(Skeleton *skeleton);
-
-    void ReadBones(Skeleton *skeleton);
-    void ReadBoneHierarchy(Skeleton *skeleton);
-
-    void ReadAnimations(Skeleton *skeleton);
-    void ReadAnimationTracks(Animation *dest);
-    void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *dest);
+    void ReadSkeleton(XmlNode &node, Skeleton *skeleton);
+    void ReadBones(XmlNode &node, Skeleton *skeleton);
+    void ReadBoneHierarchy(XmlNode &node, Skeleton *skeleton);
+    void ReadAnimations(XmlNode &node, Skeleton *skeleton);
+    void ReadAnimationTracks(XmlNode &node, Animation *dest);
+    void ReadAnimationKeyFrames(XmlNode &node, Animation *anim, VertexAnimationTrack *dest);
 
-    template<typename T>
-    T ReadAttribute(const char *name) const;
-    bool HasAttribute(const char *name) const;
+    template <typename T>
+    T ReadAttribute(XmlNode &xmlNode, const char *name) const;
 
-    std::string &NextNode();
-    std::string &SkipCurrentNode();
-
-    bool CurrentNodeNameEquals(const std::string &name) const;
-    std::string CurrentNodeName(bool forceRead = false);
-
-    XmlReader *m_reader;
-    std::string m_currentNodeName;
+private:
+    XmlParser *mParser;
 };
 
-} // Ogre
-} // Assimp
+
+} // namespace Ogre
+} // namespace Assimp
 
 #endif // ASSIMP_BUILD_NO_OGRE_IMPORTER
 #endif // AI_OGREXMLSERIALIZER_H_INC

File diff suppressed because it is too large
+ 408 - 413
code/AssetLib/OpenGEX/OpenGEXImporter.cpp


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

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

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

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

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

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

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

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

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

@@ -808,7 +808,7 @@ void SIBImporter::InternReadFile(const std::string &pFile,
 
     // We should have at least one chunk
     if (stream.GetRemainingSize() < 16)
-        throw DeadlyImportError("SIB file is either empty or corrupt: " + pFile);
+        throw DeadlyImportError("SIB file is either empty or corrupt: ", pFile);
 
     SIB sib;
 

Some files were not shown because too many files changed in this diff