Bläddra i källkod

Merge pull request #1 from assimp/master

sync all modify
yunqiangshanbill 6 år sedan
förälder
incheckning
ab4166a279
100 ändrade filer med 10515 tillägg och 6482 borttagningar
  1. 127 0
      .clang-format
  2. 2 0
      .github/FUNDING.yml
  3. 23 0
      .gitignore
  4. 2 1
      .travis.sh
  5. 4 10
      .travis.yml
  6. 0 21
      AssimpConfig.cmake.in
  7. 26 0
      BUILDBINARIES_EXAMPLE.bat
  8. 88 0
      Build.md
  9. 258 0
      CHANGES
  10. 261 109
      CMakeLists.txt
  11. 10 0
      CONTRIBUTING.md
  12. 10 4
      INSTALL
  13. 43 26
      Readme.md
  14. 18 2
      appveyor.yml
  15. 1 62
      assimp-config.cmake.in
  16. 1 1
      assimp.pc.in
  17. 123 0
      assimpTargets-debug.cmake.in
  18. 122 0
      assimpTargets-release.cmake.in
  19. 104 0
      assimpTargets.cmake.in
  20. 7 3
      cmake-modules/Findassimp.cmake
  21. 540 0
      cmake/HunterGate.cmake
  22. 14 0
      cmake/assimp-hunter-config.cmake.in
  23. 17 15
      code/3DS/3DSConverter.cpp
  24. 8 6
      code/3DS/3DSExporter.cpp
  25. 1 1
      code/3DS/3DSExporter.h
  26. 17 19
      code/3DS/3DSHelper.h
  27. 40 41
      code/3DS/3DSLoader.cpp
  28. 1 1
      code/3DS/3DSLoader.h
  29. 1 1
      code/3MF/3MFXmlTags.h
  30. 7 3
      code/3MF/D3MFExporter.cpp
  31. 1 1
      code/3MF/D3MFExporter.h
  32. 16 15
      code/3MF/D3MFImporter.cpp
  33. 1 1
      code/3MF/D3MFImporter.h
  34. 207 0
      code/3MF/D3MFOpcPackage.cpp
  35. 4 5
      code/3MF/D3MFOpcPackage.h
  36. 21 21
      code/AC/ACLoader.cpp
  37. 1 1
      code/AC/ACLoader.h
  38. 3 3
      code/AMF/AMFImporter.cpp
  39. 65 197
      code/AMF/AMFImporter.hpp
  40. 1 1
      code/AMF/AMFImporter_Geometry.cpp
  41. 5 5
      code/AMF/AMFImporter_Macro.hpp
  42. 60 42
      code/AMF/AMFImporter_Material.cpp
  43. 63 124
      code/AMF/AMFImporter_Node.hpp
  44. 15 15
      code/AMF/AMFImporter_Postprocess.cpp
  45. 25 25
      code/ASE/ASELoader.cpp
  46. 1 2
      code/ASE/ASELoader.h
  47. 17 16
      code/ASE/ASEParser.cpp
  48. 54 75
      code/ASE/ASEParser.h
  49. 846 0
      code/Assbin/AssbinExporter.cpp
  50. 9 2
      code/Assbin/AssbinExporter.h
  51. 182 190
      code/Assbin/AssbinLoader.cpp
  52. 23 22
      code/Assbin/AssbinLoader.h
  53. 0 823
      code/AssbinExporter.cpp
  54. 109 0
      code/Assjson/cencode.c
  55. 31 0
      code/Assjson/cencode.h
  56. 809 0
      code/Assjson/json_exporter.cpp
  57. 320 0
      code/Assjson/mesh_splitter.cpp
  58. 61 0
      code/Assjson/mesh_splitter.h
  59. 12 7
      code/Assxml/AssxmlExporter.cpp
  60. 1 1
      code/Assxml/AssxmlExporter.h
  61. 102 99
      code/B3D/B3DImporter.cpp
  62. 1 1
      code/B3D/B3DImporter.h
  63. 61 30
      code/BVH/BVHLoader.cpp
  64. 1 1
      code/BVH/BVHLoader.h
  65. 0 0
      code/Blender/BlenderBMesh.cpp
  66. 0 0
      code/Blender/BlenderBMesh.h
  67. 189 0
      code/Blender/BlenderCustomData.cpp
  68. 89 0
      code/Blender/BlenderCustomData.h
  69. 6 7
      code/Blender/BlenderDNA.cpp
  70. 37 4
      code/Blender/BlenderDNA.h
  71. 104 4
      code/Blender/BlenderDNA.inl
  72. 1 1
      code/Blender/BlenderIntermediate.h
  73. 98 12
      code/Blender/BlenderLoader.cpp
  74. 1 1
      code/Blender/BlenderLoader.h
  75. 1 29
      code/Blender/BlenderModifier.cpp
  76. 29 32
      code/Blender/BlenderModifier.h
  77. 49 2
      code/Blender/BlenderScene.cpp
  78. 84 2
      code/Blender/BlenderScene.h
  79. 11 0
      code/Blender/BlenderSceneGen.h
  80. 1 1
      code/Blender/BlenderTessellator.cpp
  81. 7 3
      code/Blender/BlenderTessellator.h
  82. 63 92
      code/C4D/C4DImporter.cpp
  83. 6 12
      code/C4D/C4DImporter.h
  84. 2 3
      code/CApi/AssimpCExport.cpp
  85. 1 1
      code/CApi/CInterfaceIOWrapper.cpp
  86. 1 1
      code/CApi/CInterfaceIOWrapper.h
  87. 677 467
      code/CMakeLists.txt
  88. 45 93
      code/COB/COBLoader.cpp
  89. 2 22
      code/COB/COBLoader.h
  90. 1 1
      code/COB/COBScene.h
  91. 2 2
      code/CSM/CSMLoader.cpp
  92. 1 1
      code/CSM/CSMLoader.h
  93. 190 115
      code/Collada/ColladaExporter.cpp
  94. 3 4
      code/Collada/ColladaExporter.h
  95. 7 10
      code/Collada/ColladaHelper.h
  96. 286 282
      code/Collada/ColladaLoader.cpp
  97. 6 9
      code/Collada/ColladaLoader.h
  98. 3484 0
      code/Collada/ColladaParser.cpp
  99. 27 2
      code/Collada/ColladaParser.h
  100. 0 3216
      code/ColladaParser.cpp

+ 127 - 0
.clang-format

@@ -0,0 +1,127 @@
+# Commented out parameters are those with the same value as base LLVM style
+# We can uncomment them if we want to change their value, or enforce the
+# chosen value in case the base style changes (last sync: Clang 6.0.1).
+---
+### General config, applies to all languages ###
+BasedOnStyle:  LLVM
+AccessModifierOffset: -4
+AlignAfterOpenBracket: DontAlign
+# AlignConsecutiveAssignments: false
+# AlignConsecutiveDeclarations: false
+# AlignEscapedNewlines: Right
+# AlignOperands:   true
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: false
+# AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+# AllowShortLoopsOnASingleLine: false
+# AlwaysBreakAfterDefinitionReturnType: None
+# AlwaysBreakAfterReturnType: None
+# AlwaysBreakBeforeMultilineStrings: false
+# AlwaysBreakTemplateDeclarations: false
+# BinPackArguments: true
+# BinPackParameters: true
+# BraceWrapping:
+#   AfterClass:      false
+#   AfterControlStatement: false
+#   AfterEnum:       false
+#   AfterFunction:   false
+#   AfterNamespace:  false
+#   AfterObjCDeclaration: false
+#   AfterStruct:     false
+#   AfterUnion:      false
+#   AfterExternBlock: false
+#   BeforeCatch:     false
+#   BeforeElse:      false
+#   IndentBraces:    false
+#   SplitEmptyFunction: true
+#   SplitEmptyRecord: true
+#   SplitEmptyNamespace: true
+# BreakBeforeBinaryOperators: None
+# BreakBeforeBraces: Attach
+# BreakBeforeInheritanceComma: false
+BreakBeforeTernaryOperators: false
+# BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: AfterColon
+# BreakStringLiterals: true
+ColumnLimit:     0
+# CommentPragmas:  '^ IWYU pragma:'
+# CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 8
+ContinuationIndentWidth: 8
+Cpp11BracedListStyle: false
+# DerivePointerAlignment: false
+# DisableFormat:   false
+# ExperimentalAutoDetectBinPacking: false
+# FixNamespaceComments: true
+# ForEachMacros:
+#   - foreach
+#   - Q_FOREACH
+#   - BOOST_FOREACH
+# IncludeBlocks:   Preserve
+IncludeCategories:
+  - Regex:           '".*"'
+    Priority:        1
+  - Regex:           '^<.*\.h>'
+    Priority:        2
+  - Regex:           '^<.*'
+    Priority:        3
+# IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: true
+# IndentPPDirectives: None
+IndentWidth:     4
+# IndentWrappedFunctionNames: false
+# JavaScriptQuotes: Leave
+# JavaScriptWrapImports: true
+# KeepEmptyLinesAtTheStartOfBlocks: true
+# MacroBlockBegin: ''
+# MacroBlockEnd:   ''
+# MaxEmptyLinesToKeep: 1
+# NamespaceIndentation: None
+# PenaltyBreakAssignment: 2
+# PenaltyBreakBeforeFirstCallParameter: 19
+# PenaltyBreakComment: 300
+# PenaltyBreakFirstLessLess: 120
+# PenaltyBreakString: 1000
+# PenaltyExcessCharacter: 1000000
+# PenaltyReturnTypeOnItsOwnLine: 60
+# PointerAlignment: Right
+# RawStringFormats:
+#   - Delimiter:       pb
+#     Language:        TextProto
+#     BasedOnStyle:    google
+# ReflowComments:  true
+# SortIncludes:    true
+# SortUsingDeclarations: true
+# SpaceAfterCStyleCast: false
+# SpaceAfterTemplateKeyword: true
+# SpaceBeforeAssignmentOperators: true
+# SpaceBeforeParens: ControlStatements
+# SpaceInEmptyParentheses: false
+# SpacesBeforeTrailingComments: 1
+# SpacesInAngles:  false
+# SpacesInContainerLiterals: true
+# SpacesInCStyleCastParentheses: false
+# SpacesInParentheses: false
+# SpacesInSquareBrackets: false
+TabWidth:        4
+UseTab:          Always
+---
+### C++ specific config ###
+Language:        Cpp
+Standard:        Cpp11
+---
+### ObjC specific config ###
+Language:        ObjC
+Standard:        Cpp11
+ObjCBlockIndentWidth: 4
+# ObjCSpaceAfterProperty: false
+# ObjCSpaceBeforeProtocolList: true
+---
+### Java specific config ###
+Language:        Java
+# BreakAfterJavaFieldAnnotations: false
+...

+ 2 - 0
.github/FUNDING.yml

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

+ 23 - 0
.gitignore

@@ -2,23 +2,38 @@
 build
 .project
 *.kdev4*
+.DS_Store
+
+# build artefacts
+*.o
+*.a
 
 # Visual Studio
 *.sln
 *.ncb
 *.vcproj
+*.vcxproj.user
+*.VC.db
+*.VC.db-shm
+*.VC.db-wal
+*.VC.opendb
+*.ipch
 
 # Output
 bin/
 lib/
 
+# QtCreator
+CMakeLists.txt.user
 
 # Generated
 assimp.pc
+assimp.aps
 revision.h
 contrib/zlib/zconf.h
 contrib/zlib/zlib.pc
 include/assimp/config.h
+unit.vcxproj.user
 
 # CMake
 CMakeCache.txt
@@ -28,6 +43,7 @@ cmake_uninstall.cmake
 *.dir/
 assimp-config.cmake
 assimp-config-version.cmake
+assimpTargets*.cmake
 
 # MakeFile
 Makefile
@@ -38,6 +54,7 @@ tools/assimp_cmd/Makefile
 
 # Tests
 test/results
+test/readlinetest*
 
 # Python
 __pycache__
@@ -84,3 +101,9 @@ lib64/assimp-vc120-mt.exp
 xcuserdata
 
 cmake-build-debug
+install_manifest.txt
+tools/assimp_qt_viewer/moc_glview.cpp
+tools/assimp_qt_viewer/moc_glview.cpp_parameters
+tools/assimp_qt_viewer/moc_mainwindow.cpp
+tools/assimp_qt_viewer/moc_mainwindow.cpp_parameters
+tools/assimp_qt_viewer/ui_mainwindow.h

+ 2 - 1
.travis.sh

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

+ 4 - 10
.travis.yml

@@ -27,16 +27,11 @@ compiler:
 
 env:
   global:
-  # COVERITY_SCAN_TOKEN
     - secure: "lZ7pHQvl5dpZWzBQAaIMf0wqrvtcZ4wiZKeIZjf83TEsflW8+z0uTpIuN30ZV6Glth/Sq1OhLnTP5+N57fZU/1ebA5twHdvP4bS5CIUUg71/CXQZNl36xeaqvxsG/xRrdpKOsPdjAOsQ9KPTQulsX43XDLS7CasMiLvYOpqKcPc="
     - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME}
 
 matrix:
   include:
-    # disabled until clang 5.0 analyzer issues are fixed
-    # - os: linux
-    #   compiler: clang
-    #   env: ANALYZE=ON
     - os: linux
       compiler: clang
       env: ASAN=ON
@@ -51,7 +46,7 @@ matrix:
       env: ANALYZE=ON
     - os: linux
       compiler: gcc
-      env: DISABLE_EXPORTERS=YES ENABLE_COVERALLS=ON
+      env: ENABLE_COVERALLS=ON
     - os: linux
       compiler: gcc
       env: SHARED_BUILD=ON
@@ -60,8 +55,7 @@ install:
   - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi
 
 before_script:
-  # init coverage to 0 (optional)
-  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && lcov --directory . --zerocounters ; fi
+  cmake . -DASSIMP_ENABLE_BOOST_WORKAROUND=YES
 
 script:
   - export COVERALLS_SERVICE_NAME=travis-ci
@@ -76,6 +70,6 @@ addons:
     project:
       name: "assimp/assimp"
     notification_email: [email protected]
-    build_command_prepend: "cmake"
-    build_command: "make"
+    build_command_prepend: "cmake ./"
+    build_command: "make -j4"
     branch_pattern: coverity_scan

+ 0 - 21
AssimpConfig.cmake.in

@@ -1,21 +0,0 @@
-# - Config file for the FooBar package
-# It defines the following variables
-#  FOOBAR_INCLUDE_DIRS - include directories for FooBar
-#  FOOBAR_LIBRARIES    - libraries to link against
-#  FOOBAR_EXECUTABLE   - the bar executable
-
-# Compute paths
-get_filename_component(FOOBAR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
-if(EXISTS "${FOOBAR_CMAKE_DIR}/CMakeCache.txt")
-  # In build tree
-  include("${FOOBAR_CMAKE_DIR}/FooBarBuildTreeSettings.cmake")
-else()
-  set(FOOBAR_INCLUDE_DIRS "${FOOBAR_CMAKE_DIR}/@CONF_REL_INCLUDE_DIR@")
-endif()
-
-# Our library dependencies (contains definitions for IMPORTED targets)
-include("${FOOBAR_CMAKE_DIR}/FooBarLibraryDepends.cmake")
-
-# These are IMPORTED targets created by FooBarLibraryDepends.cmake
-set(FOOBAR_LIBRARIES foo)
-set(FOOBAR_EXECUTABLE bar)

+ 26 - 0
BUILDBINARIES_EXAMPLE.bat

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

+ 88 - 0
Build.md

@@ -0,0 +1,88 @@
+# Build Instructions
+
+## Build on all platforms using vcpkg
+You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
+```bash
+    git clone https://github.com/Microsoft/vcpkg.git
+    cd vcpkg
+    ./bootstrap-vcpkg.sh
+    ./vcpkg integrate install
+    vcpkg install assimp
+```
+The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
+
+## Manual build instructions
+
+### Install CMake
+Asset-Importer-Lib can be build for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via
+```bash
+sudo apt-get install cmake
+```
+
+### Get the source
+Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
+```bash
+git clone https://github.com/assimp/assimp.git
+```
+
+### Build instructions for Windows with Visual-Studio
+
+First you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/
+To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
+```bash
+cmake CMakeLists.txt
+```
+This will generate the project files for the visual studio. All dependencies used to build Asset-IMporter-Lib shall be part of the repo. If you want to use you own zlib.installation this is possible as well. Check the options for it.
+
+### Build instructions for Windows with UWP
+See <https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app>
+
+### Build instructions for Linux / Unix
+Open a terminal and got to your repository. You can generate the makefiles and build the library via:
+
+```bash
+cmake CMakeLists.txt
+make -j4
+```
+The option -j descripes the number of parallel processes for the build. In this case make will try to use 4 cores for the build.
+
+If you want to use a IDE for linux you can try QTCreator for instance. 
+
+### Build instructions for MinGW
+ Older versions of MinGW's compiler (e.g. 5.1.0) do not support the -mbig_obj flag 
+required to compile some of assimp's files, especially for debug builds.
+Version 7.3.0 of g++-mingw-w64 & gcc-mingw-w64 appears to work.
+
+Please see [CMake Cross Compiling](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling) for general information on CMake Toolchains.
+
+Some users have had success building assimp using MinGW on Linux using [polly](https://github.com/ruslo/polly/).
+
+The following toolchain, which is not maintained by assimp, seems to work on Linux: [linux-mingw-w64-gnuxx11.cmake](https://github.com/ruslo/polly/blob/master/linux-mingw-w64-gnuxx11.cmake)
+
+The following toolchain may or may not be helpful for building assimp using MinGW on Windows (untested):
+ [mingw-cxx17.cmake](https://github.com/ruslo/polly/blob/master/mingw-cxx17.cmake)
+
+Besides the toolchain, compilation should be the same as for Linux / Unix.
+
+### CMake build options
+The cmake-build-environment provides options to configure the build. The following options can be used:
+- **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib.
+- **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle
+- **ASSIMP_DOUBLE_PRECISION( default OFF )**: All data will be stored as double values.
+- **ASSIMP_OPT_BUILD_PACKAGES ( default OFF)**: Set to ON to generate CPack configuration files and packaging targets
+- **ASSIMP_ANDROID_JNIIOSYSTEM ( default OFF )**: Android JNI IOSystem support is active
+- **ASSIMP_NO_EXPORT ( default OFF )**: Disable Assimp's export functionality
+- **ASSIMP_BUILD_ZLIB ( default OFF )**: Build your own zlib
+- **ASSIMP_BUILD_ASSIMP_TOOLS ( default ON )**: If the supplementary tools for Assimp are built in addition to the library.
+- **ASSIMP_BUILD_SAMPLES ( default OFF )**: If the official samples are built as well (needs Glut).
+- **ASSIMP_BUILD_TESTS ( default ON )**: If the test suite for Assimp is built in addition to the library.
+- **ASSIMP_COVERALLS ( default OFF )**: Enable this to measure test coverage.
+- **ASSIMP_ERROR_MAX( default OFF)**: Enable all warnings.
+- **ASSIMP_WERROR( default OFF )**: Treat warnings as errors.
+- **ASSIMP_ASAN ( default OFF )**: Enable AddressSanitizer.
+- **ASSIMP_UBSAN ( default OFF )**: Enable Undefined Behavior sanitizer.
+- **SYSTEM_IRRXML ( default OFF )**: Use system installed Irrlicht/IrrXML library.
+- **BUILD_DOCS ( default OFF )**: Build documentation using Doxygen.
+- **INJECT_DEBUG_POSTFIX( default ON )**: Inject debug postfix in .a/.so lib names
+- **IGNORE_GIT_HASH ( default OFF )**: Don't call git to get the hash.
+- **ASSIMP_INSTALL_PDB ( default ON )**: Install MSVC debug files.

+ 258 - 0
CHANGES

@@ -1,6 +1,264 @@
 ----------------------------------------------------------------------
 CHANGELOG
 ----------------------------------------------------------------------
+4.1.0 (2017-12):
+- FEATURES:
+ - Export 3MF ( experimental )
+ - Import / Export glTF 2
+ - Introduce new zib-lib to eb able to export zip-archives
+- FIXES/HOUSEKEEPING:
+ - Added missing include to stdlib.h and remove load library call
+ - Fix install for builds with MSVC compiler and NMake.
+ - Update list of supported file formats.
+ - Add TriLib to the official list of supported ports.
+ - Re-enabling PACK_STRUCT for MDL files.
+ - Use std.::unique_ptr
+ - Update D3MFExporter.h
+ - Update MD3Loader.cpp, using index
+ - Fix all warnings on MSVC14
+ - Copy assimp dll to unit folder on windows
+ - Update jvm port supported formats
+ - Add support for building Mac OS X Framework bundles
+ - Check for nullptr dereferencing before copying scene data
+ - Update ValidateDataStructure.h, typo
+ - Enable data structure validation in cases where it doesn't cause failures
+ - Remove some dead assignments
+ - fast_atof: Silence some uninitialized variable warnings
+ - Check for area test if the face is a triangle.
+ - Set mNumUVComponents to 0 when deleting texture coordinate sets
+ - Only scale the root node because this will rescale all children nodes as well.
+ - Issue 1514: Fix frame pointer arithmetic
+ - Prevent failing stringstream to crash the export process
+ - powf -> pow
+ - add Defines.h to include folder for install.
+ - Android:
+  - Fix android build
+  - Fix assimp for cross compile for android
+  - Use define for D_FILE_OFFSET_BITS only for not-android systems.
+ - FBX:
+  - Fix handling with embedded textures
+  - FBX 7500 Binary reading
+  - Remove dead assignment
+  - Fix export of deleted meshes; Add LazyDict::Remove method
+  - Log an error instead of letting the fbx-importer crash. ( issue 213 )
+  - Replace bad pointer casting with memcpy
+  - Remove useless const qualifier from return value
+  - Add explicit instantiation of log_prefix so other FBX source files can see it
+  - add missing inversion of postrotation matrix for fbx.
+  - FIReader: Silence uninitialized variable warning
+  - Update version check in FBX reader to check for version >= 7500
+  - Use actual min/max of anim keys when start/stop time is missing
+- GLTF1:
+ - Fix output of glTF 1 version string
+ - Fix delete / delete[] mismatch in glTFAsset
+ - Don’t ignore rgba(1,1,1,1) color properties
+ - glTF2 primitives fixes
+ - Don’t ignore rgba(1,1,1,1) color properties
+ - Fix delete / delete[] mismatch in glTFAsset
+ - Remove KHR_binary_glTF code
+ - glTF nodes can only hold one mesh. this simply assigns to and check’s a Node’s Mesh
+ - version in glb header is stored as uint32_t
+- GLTF2:
+ - node name conflict fix
+ - Fix transform matrices multiplication order
+ - Preserve node names when importing
+ - Add support for tangents in import
+ - Fix typo on gltf2 camera parameters
+ - Moved byteStride from accessor to bufferView
+ - Implemented reading binary glTF2 (glb) files
+ - Fix signed/unsigned warning
+ - Add postprocess step for scaling
+ - Fix shininess to roughness conversion
+ - Prefer “BLEND” over “MASK” as an alphaMode default
+ - Approximate specularity / glossiness in metallicRoughness materials
+ - Diffuse color and diffuse texture import and export improvements
+ - Addressed some mismatched news/deletes caused by the new glTF2 sources.
+ - Fix delete / delete[] mismatches in glTF2 importer
+ - use correct name of exporter to gltf2
+ - Fix possible infinite loop when exporting to gltf2
+ - Fix glTF2::Asset::FindUniqueID() when the input string is >= 256 chars
+ - Fix glTF2 alphaMode storage and reading
+ - Fix glTF 2.0 multi-primitive support
+ - Load gltf .bin files from correct directory
+ - Add support for importing both glTF and glTF2 files
+ - ampler improvements; Add new LazyDict method
+ - Changes to GLTF2 materials
+ - Remove Light, Technique references
+ - Start removing materials common, and adding pbrSpecularGlossiness
+ - Use !ObjectEmpty() vs. MemberCount() > 0
+ - Working read, import, export, and write of gltf2 (pbr) material
+ - Check in gltf2 models to test directory
+ - Remove un-needed test models
+ - Start managing and importing gltf2 pbr materials
+ - Update glTF2 Asset to use indexes
+ - Duplicate gltfImporter as gltf2Importer; Include glTF2 importer in CMake List
+ - glTF2: Fix animation export
+ - use opacity for diffuse alpha + alphaMode
+- STL:
+ - Restore import of multi mesh binary STLs
+- Blender:
+ - Silence warning about uninitialized member
+- MDLImporter:
+ - Don't take address of packed struct member
+- assimp_cmd:
+ - Fix strict-aliasing warnings
+- Open3DGC:
+ - Fix strict-aliasing warnings
+ - Add assertions to silence static analyzer warnings
+ - Remove redundant const qualifiers from return types
+ - Fix some uninitialized variable warnings
+ - Remove OPEN3DGC and compression references
+- unzip:
+ - Remove dead assignment
+ - Bail on bad compression method
+ - Fix possibly uninitialized variables
+- clipper:
+ - Add assertion to silence a static analyzer warning
+- OpenDDLExport:
+ - Reduce scope of a variable
+ - Remove dead variable
+ - Remove dead assignment
+ - Fix another potential memory leak
+- X3DImporter:
+ - Add assertions to silence static analyzer warnings
+ - Add missing unittest
+ - Workaround for buggy Android NDK (issue #1361)
+- TerragenLoader:
+ - Remove unused variable
+- SIBImporter:
+ - Add assertions to silence static analyzer warnings
+- IFC:
+ - Remove dead code
+ - Add explicit instantiation of log_prefix so IFCMaterial.cpp can see it
+- PLY:
+ - Remove dead assignment and reduce scope of a variable
+ - fix vertex attribute lookup.
+- OpenGEX:
+ - Add assertion to silence a static analyzer warning
+ - Fix for TextureFile with number in file name
+ - Return early when element is TextureFile
+- NFF:
+ - Add assertions to silence static analyzer warnings
+ - Split up some complicated assignments
+- Raw: Fix misleading indentation warning
+ - Reduce scope of a variable
+- LWO
+ - Reduce scope of a variable
+- IRRLoader:
+ - Fix confusing boolean casting
+- AssbinExporter:
+ - Add assertion to silence a static analyzer warning
+- ASE:
+ - Add assertion to silence a static analyzer warning
+- AMFImporter:
+ - Add assertion to silence a static analyzer warning
+ - Add a block
+- OptimizeGraph:
+ - Fix possible null pointer dereference
+ - RemoveRedundantMaterials:
+ - Add assertion to silence a static analyzer warning
+- ImproveCacheLocality:
+ - Add assertion to silence a static analyzer warning
+- RemoveRedundantMaterials:
+ - Set pointer to nullptr after deleting it
+- Travis:
+ - Disable unit tests in scan-build config
+ - Move slower builds earlier to improve parallelization
+ - Add static analysis to build
+ - Remove unused branch rule for travis.
+ - Add Clang UBSan build configuration
+ - Treat warnings as errors, without typos this time
+- Unittests:
+ - Add VS-based source groups for the unittests.
+- Collada:
+ - export <library_animations> tag
+ - Update ColladaExporter.cpp
+ - Silence uninitialized variable warning
+ - Add support for line strip primitives
+- Obj Wavefront:
+ - check in exporting against out-of-bounds-access .
+ - Issue 1351: use correct name for obj-meshname export for groups.
+ - fix mem-lead: face will be not released in case of an error.
+ - Anatoscope obj exporter nomtl
+ - Raise exception when obj file contains invalid face indices
+ - Added alternative displacement texture token in OBJ MTL material.
+ - Obj: rename attribute from exporter.
+ - Fix OBJ discarding all material names if the material library is missing
+- Step:
+ - use correct lookup for utf32
+- MD2:
+ - Fix MD2 frames containing garbage
+- STL
+ - add missing const.
+ - Fix memory-alignment bug.
+ - Fix issue 104: deal with more solids in one STL file.
+- CMake
+ - Fix issue 213: use correct include folder for assimp
+- Doxygen
+ - Fix issue 1513: put irrXML onto exclucde list for doxygen run
+- PyAssimp:
+ - Search for libassimp.so in LD_LIBRARY_PATH if available.
+ - Fix operator precedence issue in header check
+ - Split setup.py into multiple lines
+ - Detect if Anaconda and fixed 3d_viewer for Python 3
+ - created a python3 version of the 3dviewer and fixed the / = float in py3
+- Blender:
+ - Fix invalid access to mesh array when the array is empty.
+ - Fix short overflow.
+ - Silence warning about inline function which is declared but not defined
+- JAssimp
+ - Changed license header for IHMC contributions from Apache 2.0 to BSD
+ - Add Node metadata to the Jassmip Java API
+ - Added supported for custom IO Systems in Java. Implemented ClassLoader IO System
+ - Added a link to pure jvm assimp port
+- Clang sanitizer:
+ - Undefined Behavior sanitizer
+ - Fixed a divide by zero error in IFCBoolean that was latent, but nevertheless a bug
+- B3DImporter:
+ - Replace bad pointer casting with memcpy
+- AppVeyor:
+ - Cleanup and Addition of VS 2017 and running Tests
+ - Fixed File Size reported as 0 in tests that use temporary files
+ - x86 isn't a valid VS platform. Win32 it is, then.
+ - Replaced the worker image name, which doesn't work as generator name, with a manually created generator name.
+ - Cleaned up appveyor setup, added VS 2017 to the build matrix and attempted to add running of tests.
+ - Treat warnings as errors on Appveyor
+ - Disable warning 4351 on MSVC 2013
+- OpenGEXImporter:
+ - Copy materials to scene
+ - Store RefInfo in unique_ptr so they get automatically cleaned up
+ - Fix IOStream leak
+ - Store ChildInfo in unique_ptr so they get automatically cleaned up
+ - improve logging to be able to detect error-prone situations.
+-  AMFImporter:
+ - Fix memory leak
+- UnrealLoader:
+ - Fix IOStream leak
+- Upgrade RapidJSON to get rid of a clang warning
+- zlib:
+ - Update zlib contribution
+ - Removed unnecessary files from zlib contribution
+ - Replaced unsigned long for the crc table to z_crc_t, to match what is returned by get-crc_table
+- MakeVerboseFormat:
+  - Fix delete / delete[] mismatches in MakeVerboseFormat
+- MaterialSystem:
+ - Fix out-of-bounds read in MaterialSystem unit test
+- SIB:
+ - Added support for SIB models from Silo 2.5
+- AssbinExporter:
+ - Fix strict aliasing violation
+ - Add Write specialization for aiColor3D
+- DefaultLogger:
+ - Whitespace cleanup to fix GCC misleading indentation warning
+- MDP:
+ - Fix encoding issues.
+ - PreTransformVertices:
+ - fix name lost in mesh and nodes when load with flag
+- C4D:
+ - Fixes for C4D importer
+- Unzip:
+ - Latest greatest.
+
 4.0.1 (2017-07-28)
     - FIXES/HOUSEKEEPING:
     - fix version test.

+ 261 - 109
CMakeLists.txt

@@ -1,7 +1,7 @@
 # Open Asset Import Library (assimp)
 # ----------------------------------------------------------------------
-# Copyright (c) 2006-2018, assimp team
-
+# Copyright (c) 2006-2019, assimp team
+#
 # All rights reserved.
 #
 # Redistribution and use of this software in source and binary forms,
@@ -34,15 +34,32 @@
 # (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(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required
-CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
-PROJECT( Assimp )
+SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
+
+CMAKE_MINIMUM_REQUIRED( VERSION 3.0 )
+
+# Toggles the use of the hunter package manager
+option(HUNTER_ENABLED "Enable Hunter package manager support" OFF)
+
+include("cmake/HunterGate.cmake")
+HunterGate(
+    URL "https://github.com/ruslo/hunter/archive/v0.23.176.tar.gz"
+    SHA1 "2e9ae973d028660b735ac4c6142725ca36a0048a"
+)
+
+IF(HUNTER_ENABLED)
+  add_definitions(-DASSIMP_USE_HUNTER)
+ENDIF(HUNTER_ENABLED)
+
+PROJECT( Assimp VERSION 5.0.0 )
 
 # All supported options ###############################################
+
 OPTION( BUILD_SHARED_LIBS
   "Build package with shared libraries."
   ON
 )
+
 OPTION( BUILD_FRAMEWORK
   "Build package as Mac OS X Framework bundle."
   OFF
@@ -83,6 +100,10 @@ OPTION ( ASSIMP_COVERALLS
   "Enable this to measure test coverage."
   OFF
 )
+OPTION ( ASSIMP_ERROR_MAX
+  "Enable all warnings."
+  OFF
+)
 OPTION ( ASSIMP_WERROR
   "Treat warnings as errors."
   OFF
@@ -103,6 +124,22 @@ OPTION ( BUILD_DOCS
   "Build documentation using Doxygen."
   OFF
 )
+OPTION( INJECT_DEBUG_POSTFIX
+  "Inject debug postfix in .a/.so/.dll lib names"
+  ON
+)
+
+OPTION ( IGNORE_GIT_HASH
+   "Don't call git to get the hash."
+   OFF
+)
+
+IF (IOS AND NOT HUNTER_ENABLED)
+  IF (NOT CMAKE_BUILD_TYPE)
+    SET(CMAKE_BUILD_TYPE "Release")
+  ENDIF (NOT CMAKE_BUILD_TYPE)
+  ADD_DEFINITIONS(-DENABLE_BITCODE)
+ENDIF (IOS AND NOT HUNTER_ENABLED)
 
 # Use subset of Windows.h
 if (WIN32)
@@ -114,6 +151,10 @@ IF(MSVC)
     "Install MSVC debug files."
     ON
   )
+  IF(NOT (MSVC_VERSION LESS 1900))
+    # Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
+    ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
+  ENDIF()
 ENDIF(MSVC)
 
 IF (BUILD_FRAMEWORK)
@@ -129,35 +170,37 @@ ELSE()
 ENDIF(NOT BUILD_SHARED_LIBS)
 
 # Define here the needed parameters
-SET (ASSIMP_VERSION_MAJOR 4)
-SET (ASSIMP_VERSION_MINOR 1)
-SET (ASSIMP_VERSION_PATCH 0)
+SET (ASSIMP_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
+SET (ASSIMP_VERSION_MINOR ${PROJECT_VERSION_MINOR})
+SET (ASSIMP_VERSION_PATCH ${PROJECT_VERSION_PATCH})
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
-SET (ASSIMP_SOVERSION 4)
-SET (PROJECT_VERSION "${ASSIMP_VERSION}")
+SET (ASSIMP_SOVERSION 5)
 
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
+if(NOT HUNTER_ENABLED)
+  # Enable C++11 support globally
+  set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
+endif()
 
-# Enable C++1 globally
-set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
-
-# Get the current working branch
-EXECUTE_PROCESS(
-  COMMAND git rev-parse --abbrev-ref HEAD
-  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-  OUTPUT_VARIABLE GIT_BRANCH
-  OUTPUT_STRIP_TRAILING_WHITESPACE
-  ERROR_QUIET
-)
+IF(NOT IGNORE_GIT_HASH)
+  # Get the current working branch
+  EXECUTE_PROCESS(
+    COMMAND git rev-parse --abbrev-ref HEAD
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+    OUTPUT_VARIABLE GIT_BRANCH
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_QUIET
+  )
 
-# Get the latest abbreviated commit hash of the working branch
-EXECUTE_PROCESS(
-  COMMAND git log -1 --format=%h
-  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-  OUTPUT_VARIABLE GIT_COMMIT_HASH
-  OUTPUT_STRIP_TRAILING_WHITESPACE
-  ERROR_QUIET
-)
+  # Get the latest abbreviated commit hash of the working branch
+  EXECUTE_PROCESS(
+    COMMAND git rev-parse --short=8 HEAD
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+    OUTPUT_VARIABLE GIT_COMMIT_HASH
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_QUIET
+  )
+ENDIF()
 
 IF(NOT GIT_COMMIT_HASH)
   SET(GIT_COMMIT_HASH 0)
@@ -177,8 +220,9 @@ CONFIGURE_FILE(
   ${CMAKE_CURRENT_BINARY_DIR}/include/assimp/config.h
 )
 
-INCLUDE_DIRECTORIES(
+INCLUDE_DIRECTORIES( BEFORE
   ./
+  code/
   include
   ${CMAKE_CURRENT_BINARY_DIR}
   ${CMAKE_CURRENT_BINARY_DIR}/include
@@ -197,33 +241,55 @@ ENDIF( UNIX )
 
 # Grouped compiler settings
 IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
+  IF(NOT HUNTER_ENABLED)
+    SET(CMAKE_CXX_FLAGS "-fPIC -std=c++0x ${CMAKE_CXX_FLAGS}")
+    SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+  ENDIF()
   # hide all not-exported symbols
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -std=c++0x")
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+  SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
+  SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
   SET(LIBSTDC++_LIBRARIES -lstdc++)
 ELSEIF(MSVC)
   # enable multi-core compilation with MSVC
   ADD_COMPILE_OPTIONS(/MP)
-  IF("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
-    ADD_COMPILE_OPTIONS( /bigobj )
-  ENDIF()
+  ADD_COMPILE_OPTIONS( /bigobj )
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   IF(MSVC12)
     ADD_COMPILE_OPTIONS(/wd4351)
   ENDIF()
+  SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
 ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -Wno-long-long -std=c++11" )
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+  IF(NOT HUNTER_ENABLED)
+    SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")
+    SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+  ENDIF()
+  SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
+  SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
 ELSEIF( CMAKE_COMPILER_IS_MINGW )
-  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wno-long-long -std=c++11" )
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+  IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
+    message(FATAL_ERROR "MinGW is too old to be supported. Please update MinGW and try again.")
+  ELSEIF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.3)
+    message(WARNING "MinGW is old, if you experience errors, update MinGW.")
+  ENDIF()
+  IF(NOT HUNTER_ENABLED)
+    SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+    SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+  ENDIF()
+  SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -Wa,-mbig-obj -O3 ${CMAKE_CXX_FLAGS}")
+  SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
 ENDIF()
 
-IF (IOS)
-  SET(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -fembed-bitcode -O3")
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
-ENDIF()
+IF ( IOS AND NOT HUNTER_ENABLED)
+  IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -Og")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -Og")
+  ELSE()
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
+    # Experimental for pdb generation
+  ENDIF()
+ENDIF( IOS AND NOT HUNTER_ENABLED)
 
 IF (ASSIMP_COVERALLS)
   MESSAGE(STATUS "Coveralls enabled")
@@ -232,6 +298,16 @@ IF (ASSIMP_COVERALLS)
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
 ENDIF()
 
+IF (ASSIMP_ERROR_MAX)
+  MESSAGE(STATUS "Turning on all warnings")
+  IF (MSVC)
+    ADD_COMPILE_OPTIONS(/W4) # NB: there is a /Wall option, pedantic mode
+  ELSE()
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+  ENDIF()
+ENDIF()
+
 IF (ASSIMP_WERROR)
   MESSAGE(STATUS "Treating warnings as errors")
   IF (MSVC)
@@ -275,7 +351,9 @@ SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE STRING
 SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
   "Path the tool executables are installed to." )
 
-IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+
+IF (INJECT_DEBUG_POSTFIX AND (is_multi_config OR CMAKE_BUILD_TYPE STREQUAL "Debug"))
   SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfix for lib, samples and tools")
 ELSE()
   SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Debug Postfix for lib, samples and tools")
@@ -288,10 +366,72 @@ IF (NOT TARGET uninstall)
   ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
 ENDIF()
 
-# cmake configuration files
-CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
-CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" @ONLY IMMEDIATE)
-INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake"             "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT})
+IF(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(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}::")
+
+  # Include module with fuction 'write_basic_package_version_file'
+  include(CMakePackageConfigHelpers)
+
+  # Note: PROJECT_VERSION is used as a VERSION
+  write_basic_package_version_file("${VERSION_CONFIG}" COMPATIBILITY SameMajorVersion)
+
+  # Use variables:
+  #   * TARGETS_EXPORT_NAME
+  #   * PROJECT_NAME
+  configure_package_config_file(
+      "cmake/assimp-hunter-config.cmake.in"
+      "${PROJECT_CONFIG}"
+      INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
+  )
+
+  install(
+      FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
+      DESTINATION "${CONFIG_INSTALL_DIR}"
+  )
+
+  install(
+      EXPORT "${TARGETS_EXPORT_NAME}"
+      NAMESPACE "${NAMESPACE}"
+      DESTINATION "${CONFIG_INSTALL_DIR}"
+  )
+ELSE(HUNTER_ENABLED)
+  # cmake configuration files
+  if(${BUILD_SHARED_LIBS})
+    set(BUILD_LIB_TYPE SHARED)
+  else()
+    set(BUILD_LIB_TYPE STATIC)
+  endif()
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
+  IF (is_multi_config)
+    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(HUNTER_ENABLED)
 
 FIND_PACKAGE( DirectX )
 
@@ -306,28 +446,58 @@ ENDIF( SYSTEM_IRRXML )
 
 # Search for external dependencies, and build them from source if not found
 # Search for zlib
-IF ( NOT ASSIMP_BUILD_ZLIB )
-  FIND_PACKAGE(ZLIB)
-ENDIF( NOT ASSIMP_BUILD_ZLIB )
-
-IF( NOT ZLIB_FOUND )
-  MESSAGE(STATUS "compiling zlib from souces")
-  INCLUDE(CheckIncludeFile)
-  INCLUDE(CheckTypeSize)
-  INCLUDE(CheckFunctionExists)
-  # compile from sources
-  ADD_SUBDIRECTORY(contrib/zlib)
-  SET(ZLIB_FOUND 1)
-  SET(ZLIB_LIBRARIES zlibstatic)
-  SET(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/contrib/zlib ${CMAKE_CURRENT_BINARY_DIR}/contrib/zlib)
-ELSE(NOT ZLIB_FOUND)
-  ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB)
-  SET(ZLIB_LIBRARIES_LINKED -lz)
-ENDIF(NOT ZLIB_FOUND)
-INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
-
-# Search for unzip
-use_pkgconfig(UNZIP minizip)
+IF(HUNTER_ENABLED)
+  hunter_add_package(ZLIB)
+  find_package(ZLIB CONFIG REQUIRED)
+
+  add_definitions(-DASSIMP_BUILD_NO_OWN_ZLIB)
+  set(ZLIB_FOUND TRUE)
+  set(ZLIB_LIBRARIES ZLIB::zlib)
+  set(ASSIMP_BUILD_MINIZIP TRUE)
+ELSE(HUNTER_ENABLED)
+  IF ( NOT ASSIMP_BUILD_ZLIB )
+    FIND_PACKAGE(ZLIB)
+  ENDIF( NOT ASSIMP_BUILD_ZLIB )
+
+  IF( NOT ZLIB_FOUND )
+    MESSAGE(STATUS "compiling zlib from sources")
+    INCLUDE(CheckIncludeFile)
+    INCLUDE(CheckTypeSize)
+    INCLUDE(CheckFunctionExists)
+
+    # Explicitly turn off ASM686 and AMD64 cmake options.
+    # The AMD64 option causes a build failure on MSVC and the ASM builds seem to have problems:
+    #		https://github.com/madler/zlib/issues/41#issuecomment-125848075
+    # Also prevents these options from "polluting" the cmake options if assimp is being
+    # included as a submodule.
+    set( ASM686 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
+    set( AMD64 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
+
+    # compile from sources
+    ADD_SUBDIRECTORY(contrib/zlib)
+    SET(ZLIB_FOUND 1)
+    SET(ZLIB_LIBRARIES zlibstatic)
+    SET(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/contrib/zlib ${CMAKE_CURRENT_BINARY_DIR}/contrib/zlib)
+    # need to ensure we don't link with system zlib or minizip as well.
+    SET(ASSIMP_BUILD_MINIZIP 1)
+  ELSE(NOT ZLIB_FOUND)
+    ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB)
+    SET(ZLIB_LIBRARIES_LINKED -lz)
+  ENDIF(NOT ZLIB_FOUND)
+  INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
+ENDIF(HUNTER_ENABLED)
+
+IF( NOT IOS )
+  IF( NOT ASSIMP_BUILD_MINIZIP )
+    use_pkgconfig(UNZIP minizip)
+  ENDIF( NOT ASSIMP_BUILD_MINIZIP )
+ELSE ( NOT IOS )
+  IF( NOT BUILD_SHARED_LIBS )
+    IF( NOT ASSIMP_BUILD_MINIZIP )
+      use_pkgconfig(UNZIP minizip)
+    ENDIF( NOT ASSIMP_BUILD_MINIZIP )
+  ENDIF ( NOT BUILD_SHARED_LIBS )
+ENDIF ( NOT IOS )
 
 IF ( ASSIMP_NO_EXPORT )
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_EXPORT)
@@ -400,49 +570,27 @@ ELSE (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER )
 ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
 
-ADD_SUBDIRECTORY(contrib)
+IF(NOT HUNTER_ENABLED)
+  ADD_SUBDIRECTORY(contrib)
+ENDIF(NOT HUNTER_ENABLED)
 
 ADD_SUBDIRECTORY( code/ )
 IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
+  # The viewer for windows only
   IF ( WIN32 AND DirectX_D3DX9_LIBRARY )
     OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} )
     IF ( ASSIMP_BUILD_ASSIMP_VIEW )
       ADD_SUBDIRECTORY( tools/assimp_view/ )
     ENDIF ( ASSIMP_BUILD_ASSIMP_VIEW )
   ENDIF ( WIN32 AND DirectX_D3DX9_LIBRARY )
-
+  # Te command line tool
   ADD_SUBDIRECTORY( tools/assimp_cmd/ )
-
-  # Check dependencies for assimp_qt_viewer.
-  # Why here? Maybe user do not want Qt viewer and have no Qt.
-  # Why assimp_qt_viewer/CMakeLists.txt still contain similar check?
-  # Because viewer can be build independently of Assimp.
-  FIND_PACKAGE(Qt5Widgets QUIET)
-  FIND_PACKAGE(DevIL QUIET)
-  FIND_PACKAGE(OpenGL QUIET)
-  IF ( Qt5Widgets_FOUND AND IL_FOUND AND OPENGL_FOUND)
-    ADD_SUBDIRECTORY( tools/assimp_qt_viewer/ )
-  ELSE()
-    SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "")
-    IF (NOT Qt5_FOUND)
-      SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "${ASSIMP_QT_VIEWER_DEPENDENCIES} Qt5")
-    ENDIF (NOT Qt5_FOUND)
-
-    IF (NOT IL_FOUND)
-      SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "${ASSIMP_QT_VIEWER_DEPENDENCIES} DevIL")
-    ENDIF (NOT IL_FOUND)
-
-    IF (NOT OPENGL_FOUND)
-      SET ( ASSIMP_QT_VIEWER_DEPENDENCIES "${ASSIMP_QT_VIEWER_DEPENDENCIES} OpengGL")
-    ENDIF (NOT OPENGL_FOUND)
-
-    MESSAGE (WARNING "Build of assimp_qt_viewer is disabled. Unsatisfied dendencies: ${ASSIMP_QT_VIEWER_DEPENDENCIES}")
-  ENDIF ( Qt5Widgets_FOUND AND IL_FOUND AND OPENGL_FOUND)
 ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
 
 IF ( ASSIMP_BUILD_SAMPLES)
   IF ( WIN32 )
     ADD_SUBDIRECTORY( samples/SimpleTexturedOpenGL/ )
+    ADD_SUBDIRECTORY( samples/SimpleTexturedDirectx11 )
   ENDIF ( WIN32 )
   ADD_SUBDIRECTORY( samples/SimpleOpenGL/ )
 ENDIF ( ASSIMP_BUILD_SAMPLES )
@@ -457,7 +605,7 @@ INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTAL
 
 IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES)
   # Packing information
-  SET(CPACK_PACKAGE_NAME                    "assimp{ASSIMP_VERSION_MAJOR}")
+  SET(CPACK_PACKAGE_NAME                    "assimp{ASSIMP_VERSION_MAJOR}.{ASSIMP_VERSION_MINOR}")
   SET(CPACK_PACKAGE_CONTACT "" CACHE STRING "Package maintainer and PGP signer.")
   SET(CPACK_PACKAGE_VENDOR                  "https://github.com/assimp")
   SET(CPACK_PACKAGE_DISPLAY_NAME            "Assimp ${ASSIMP_VERSION}")
@@ -487,8 +635,8 @@ IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES)
   SET(CPACK_DEBIAN_PACKAGE_SECTION  "libs" )
   SET(CPACK_DEBIAN_PACKAGE_DEPENDS  "${CPACK_COMPONENTS_ALL}")
   SET(CPACK_DEBIAN_PACKAGE_SUGGESTS)
-  SET(CPACK_DEBIAN_PACKAGE_NAME     "assimp")
-  SET(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES contrib/cppunit-1.12.1 contrib/cppunit_note.txt contrib/zlib workspaces test doc obj samples packaging)
+  set(cPACK_DEBIAN_PACKAGE_NAME     "assimp")
+  SET(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES contrib/gtest contrib/zlib workspaces test doc obj samples packaging)
   SET(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force)
   SET(CPACK_DEBIAN_CHANGELOG)
   execute_process(COMMAND lsb_release -is
@@ -508,18 +656,22 @@ if(WIN32)
   if (CMAKE_SIZEOF_VOID_P EQUAL 8)
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin64/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib64/")
-  elseif()
+  else()
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin32/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib32/")
   ENDIF()
 
-  IF(MSVC12)
-    SET(ASSIMP_MSVC_VERSION "vc120")
-  ELSEIF(MSVC14)
-    SET(ASSIMP_MSVC_VERSION "vc140")
-  ELSEIF(MSVC15)
-    SET(ASSIMP_MSVC_VERSION "vc141")
-  ENDIF(MSVC12)
+  IF(MSVC_TOOLSET_VERSION)
+    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
+  ELSE()
+    IF(MSVC12)
+      SET(ASSIMP_MSVC_VERSION "vc120")
+    ELSEIF(MSVC14)
+      SET(ASSIMP_MSVC_VERSION "vc140")
+    ELSEIF(MSVC15)
+      SET(ASSIMP_MSVC_VERSION "vc141")
+    ENDIF(MSVC12)
+  ENDIF()
 
   IF(MSVC12 OR MSVC14 OR MSVC15 )
     ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)

+ 10 - 0
CONTRIBUTING.md

@@ -0,0 +1,10 @@
+# How to contribute
+
+If you want to contribute, follow these steps:
+
+- First, create your own clone of assimp.
+- When you want to fix a bug or add a new feature, create a branch on your own fork following [these instructions](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
+- Push it to your fork of the repository and open a pull request.
+- A pull request will start our continuous integration service, which checks if the build works for Linux and Windows.
+  It will check for memory leaks, compiler warnings and memory alignment issues. If any of these tests fail, fix it and the tests will be restarted automatically.
+  - At the end, we will perform a code review and merge your branch to the master branch.

+ 10 - 4
INSTALL

@@ -35,10 +35,16 @@ http://www.cmake.org/.
 
 For Unix:
 
-1. cmake CMakeLists.txt -G 'Unix Makefiles'
-2. make
+1. mkdir build && cd build
+2. cmake .. -G 'Unix Makefiles'
+3. make -j4
 
 For Windows:
 1. Open a command prompt
-2. cmake CMakeLists.txt
-2. Open your default IDE and build it
+2. mkdir build
+3. cd build
+4. cmake ..
+5. cmake --build .
+
+For iOS:
+Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS

+ 43 - 26
Readme.md

@@ -1,7 +1,7 @@
 Open Asset Import Library (assimp)
 ==================================
 A library to import and export various 3d-model-formats including scene-post-processing to generate missing render data.
-### Current build status ###
+### Current project status ###
 [![Linux Build Status](https://travis-ci.org/assimp/assimp.svg)](https://travis-ci.org/assimp/assimp)
 [![Windows Build Status](https://ci.appveyor.com/api/projects/status/tmo433wax6u6cjp4?svg=true)](https://ci.appveyor.com/project/kimkulling/assimp)
 <a href="https://scan.coverity.com/projects/5607">
@@ -10,10 +10,15 @@ A library to import and export various 3d-model-formats including scene-post-pro
 </a>
 [![Coverage Status](https://coveralls.io/repos/github/assimp/assimp/badge.svg?branch=master)](https://coveralls.io/github/assimp/assimp?branch=master)
 [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue")
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5be56faac64f46fc941ac890fb4febef)](https://www.codacy.com/app/kimkulling/assimp?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=assimp/assimp&amp;utm_campaign=Badge_Grade)
+[![Total alerts](https://img.shields.io/lgtm/alerts/g/assimp/assimp.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/assimp/assimp/alerts/)
 <br>
 
 APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
 
+[Check the latest doc](https://assimp-docs.readthedocs.io/en/latest/).
+
 Additionally, assimp features various __mesh post processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
 
 This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
@@ -30,36 +35,44 @@ One-off donations via PayPal:
 
 Please check our Wiki as well: https://github.com/assimp/assimp/wiki
 
+If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
+
 #### Supported file formats ####
 
 __Importers__:
 
 - 3D
-- 3DS
-- 3MF
+- [3DS](https://en.wikipedia.org/wiki/.3ds)
+- [3MF](https://en.wikipedia.org/wiki/3D_Manufacturing_Format)
 - AC
-- AC3D
+- [AC3D](https://en.wikipedia.org/wiki/AC3D)
 - ACC
 - AMJ
 - ASE
 - ASK
 - B3D
-- BLEND (Blender)
-- BVH
-- COB
+- [BLEND](https://en.wikipedia.org/wiki/.blend_(file_format))
+- [BVH](https://en.wikipedia.org/wiki/Biovision_Hierarchy)
 - CMS
-- DAE/Collada
-- DXF
+- COB
+- [DAE/Collada](https://en.wikipedia.org/wiki/COLLADA)
+- [DXF](https://en.wikipedia.org/wiki/AutoCAD_DXF)
 - ENFF
-- FBX
-- glTF 1.0 + GLB
-- glTF 2.0
+- [FBX](https://en.wikipedia.org/wiki/FBX)
+- [glTF 1.0](https://en.wikipedia.org/wiki/GlTF#glTF_1.0) + GLB
+- [glTF 2.0](https://en.wikipedia.org/wiki/GlTF#glTF_2.0):
+  At the moment for glTF2.0 the following extensions are supported:
+  + KHR_lights_punctual ( 5.0 )
+  + KHR_materials_pbrSpecularGlossiness ( 5.0 )
+  + KHR_materials_unlit ( 5.0 )
+  + KHR_texture_transform ( 5.1 under test )
 - HMB
 - IFC-STEP
 - IRR / IRRMESH
-- LWO
+- [LWO](https://en.wikipedia.org/wiki/LightWave_3D)
 - LWS
 - LXO
+- [M3D](https://bztsrc.gitlab.io/model3d)
 - MD2
 - MD3
 - MD5
@@ -70,10 +83,10 @@ __Importers__:
 - MS3D
 - NDO
 - NFF
-- OBJ
-- OFF
-- OGEX
-- PLY
+- [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file)
+- [OFF](https://en.wikipedia.org/wiki/OFF_(file_format))
+- [OGEX](https://en.wikipedia.org/wiki/Open_Game_Engine_Exchange)
+- [PLY](https://en.wikipedia.org/wiki/PLY_(file_format))
 - PMX
 - PRJ
 - Q3O
@@ -82,19 +95,19 @@ __Importers__:
 - SCN
 - SIB
 - SMD
-- STL
-- STP
+- [STP](https://en.wikipedia.org/wiki/ISO_10303-21)
+- [STL](https://en.wikipedia.org/wiki/STL_(file_format))
 - TER
 - UC
 - VTA
 - X
-- X3D
+- [X3D](https://en.wikipedia.org/wiki/X3D)
 - XGL
 - ZGL
 
 Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default):
 
-- C4D (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange)
+- [C4D](https://en.wikipedia.org/wiki/Cinema_4D) (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) IMporting geometry + node hierarchy are currently supported
 
 __Exporters__:
 
@@ -113,22 +126,23 @@ __Exporters__:
 - FBX ( experimental )
 
 ### Building ###
-Take a look into the `INSTALL` file. Our build system is CMake, if you used CMake before there is a good chance you know what to do.
+Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
 
 ### Ports ###
 * [Android](port/AndroidJNI/README.md)
 * [Python](port/PyAssimp/README.md)
-* [.NET](port/AssimpNET/Readme.md)
+* [.NET](https://github.com/kebby/assimp-net)
 * [Pascal](port/AssimpPascal/Readme.md)
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
 * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777)
 * [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status))
+* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
 
 ### Other tools ###
 [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.
 
 #### Repository structure ####
-Open Asset Import Library is implemented in C++. The directory structure is:
+Open Asset Import Library is implemented in C++. The directory structure looks like:
 
 	/code		Source code
 	/contrib	Third-party libraries
@@ -140,9 +154,12 @@ Open Asset Import Library is implemented in C++. The directory structure is:
 	/tools		Tools (old assimp viewer, command line `assimp`)
 	/samples	A small number of samples to illustrate possible
                         use cases for Assimp
-	/workspaces	Build environments for vc,xcode,... (deprecated,
-			CMake has superseeded all legacy build options!)
 
+The source code is organized in the following way:
+
+	code/Common		The base implementation for importers and the infrastructure
+	code/PostProcessing	The post-processing steps
+	code/<FormatName>	Implementation for import and export for the format
 
 ### Where to get help ###
 For more information, visit [our website](http://assimp.org/). Or check out the `./doc`- folder, which contains the official documentation in HTML format.

+ 18 - 2
appveyor.yml

@@ -17,6 +17,8 @@ image:
   - Visual Studio 2013
   - Visual Studio 2015
   - Visual Studio 2017
+  - Visual Studio 2019
+  - MinGW  
     
 platform:
   - Win32
@@ -27,11 +29,18 @@ configuration: Release
 install:
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set CMAKE_DEFINES -DASSIMP_WERROR=ON
+  - if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH%
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
-  - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
-  - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%"
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" set CMAKE_GENERATOR_NAME=Visual Studio 16 2019
+  - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" -A %platform% .
+  # Rename sh.exe as sh.exe in PATH interferes with MinGW  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
+
+  - rename "C:\Program Files\Git\usr\bin\sh.exe" "sh2.exe"
+  - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
+  - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
+  - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
   
 cache:
   - code\assimp.dir\%CONFIGURATION%
@@ -50,6 +59,13 @@ build:
   project: Assimp.sln
   
 after_build:
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (
+        if "%platform%"=="x64" (
+            iscc packaging\windows-innosetup\script_x64.iss
+        ) else (
+            iscc packaging\windows-innosetup\script_x86.iss
+        )
+    )
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
 
 test_script:

+ 1 - 62
assimp-config.cmake.in

@@ -1,62 +1 @@
-# - Find Assimp Installation
-#
-# Users can set the following variables before calling the module:
-#  ASSIMP_DIR - The preferred installation prefix for searching for ASSIMP. Set by the user.
-#
-# ASSIMP_ROOT_DIR - the root directory where the installation can be found
-# ASSIMP_CXX_FLAGS - extra flags for compilation
-# ASSIMP_LINK_FLAGS - extra flags for linking
-# ASSIMP_INCLUDE_DIRS - include directories
-# ASSIMP_LIBRARY_DIRS - link directories
-# ASSIMP_LIBRARIES - libraries to link plugins with
-# ASSIMP_Boost_VERSION - the boost version assimp was compiled with
-get_filename_component(_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
-get_filename_component(_PREFIX "${_PREFIX}" PATH)
-get_filename_component(_PREFIX "${_PREFIX}" PATH)
-get_filename_component(ASSIMP_ROOT_DIR "${_PREFIX}" PATH)
-
-if( MSVC )
-  # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
-  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( MSVC14 )
-    set(MSVC_PREFIX "vc140")
-  else()
-    set(MSVC_PREFIX "vc150")
-  endif()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
-else()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" )
-endif()
-
-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@")
-set( ASSIMP_LIBRARIES assimp${ASSIMP_LIBRARY_SUFFIX})
-set( ASSIMP_LIBRARIES ${ASSIMP_LIBRARIES}@CMAKE_DEBUG_POSTFIX@)
-
-# 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
-)
+include(${CMAKE_CURRENT_LIST_DIR}/assimpTargets.cmake)

+ 1 - 1
assimp.pc.in

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

+ 123 - 0
assimpTargets-debug.cmake.in

@@ -0,0 +1,123 @@
+#----------------------------------------------------------------
+# 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@)
+
+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" )
+
+  file(TO_NATIVE_PATH ${_IMPORT_PREFIX} _IMPORT_PREFIX)
+
+  if(ASSIMP_BUILD_SHARED_LIBS)
+    set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@")
+    set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_IMPORT_LIBRARY_SUFFIX@")
+
+    # Import target "assimp::assimp" for configuration "Debug"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_IMPLIB_DEBUG "${_IMPORT_PREFIX}/lib/${importLibraryName}"
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
+
+    # Import target "assimp::assimp" for configuration "Debug"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${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()
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_SONAME_DEBUG "${sharedLibraryName}"
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${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
+)

+ 122 - 0
assimpTargets-release.cmake.in

@@ -0,0 +1,122 @@
+#----------------------------------------------------------------
+# 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@)
+
+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" )
+  	
+  file(TO_NATIVE_PATH ${_IMPORT_PREFIX} _IMPORT_PREFIX)
+
+  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 "${_IMPORT_PREFIX}/lib/${importLibraryName}"
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_STATIC_LIBRARY_SUFFIX@")
+
+    # Import target "assimp::assimp" for configuration "Release"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${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()
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_SONAME_RELEASE "${sharedLibraryName}"
+
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_STATIC_LIBRARY_SUFFIX@")
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${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
+)

+ 104 - 0
assimpTargets.cmake.in

@@ -0,0 +1,104 @@
+# 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)
+
+
+# Compute the installation prefix relative to this file.
+get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
+if(_IMPORT_PREFIX STREQUAL "/")
+  set(_IMPORT_PREFIX "")
+endif()
+
+# Create imported target assimp::assimp
+add_library(assimp::assimp @BUILD_LIB_TYPE@ IMPORTED)
+
+set_target_properties(assimp::assimp PROPERTIES
+  COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION"
+  INTERFACE_assimp_MAJOR_VERSION "1"
+  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include;${_IMPORT_PREFIX}/include"
+  #INTERFACE_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()
+
+# Cleanup temporary variables.
+set(_IMPORT_PREFIX)
+
+# Loop over all imported files and verify that they actually exist
+foreach(target ${_IMPORT_CHECK_TARGETS} )
+  foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )
+    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)

+ 7 - 3
cmake-modules/Findassimp.cmake

@@ -54,14 +54,18 @@ else(WIN32)
 
 	find_path(
 	  assimp_INCLUDE_DIRS
-	  NAMES postprocess.h scene.h version.h config.h cimport.h
-	  PATHS /usr/local/include/
+	  NAMES assimp/postprocess.h assimp/scene.h assimp/version.h assimp/config.h assimp/cimport.h
+	  PATHS /usr/local/include
+	  PATHS /usr/include/
+
 	)
 
 	find_library(
 	  assimp_LIBRARIES
 	  NAMES assimp
 	  PATHS /usr/local/lib/
+	  PATHS /usr/lib64/
+	  PATHS /usr/lib/
 	)
 
 	if (assimp_INCLUDE_DIRS AND assimp_LIBRARIES)
@@ -78,4 +82,4 @@ else(WIN32)
 	  endif (assimp_FIND_REQUIRED)
 	endif (assimp_FOUND)
 	
-endif(WIN32)
+endif(WIN32)

+ 540 - 0
cmake/HunterGate.cmake

@@ -0,0 +1,540 @@
+# Copyright (c) 2013-2018, Ruslan Baratov
+# All rights reserved.
+#
+# Redistribution and use 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.
+#
+# 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 HOLDER 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.
+
+# This is a gate file to Hunter package manager.
+# Include this file using `include` command and add package you need, example:
+#
+#     cmake_minimum_required(VERSION 3.2)
+#
+#     include("cmake/HunterGate.cmake")
+#     HunterGate(
+#         URL "https://github.com/path/to/hunter/archive.tar.gz"
+#         SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d"
+#     )
+#
+#     project(MyProject)
+#
+#     hunter_add_package(Foo)
+#     hunter_add_package(Boo COMPONENTS Bar Baz)
+#
+# Projects:
+#     * https://github.com/hunter-packages/gate/
+#     * https://github.com/ruslo/hunter
+
+option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
+
+if(HUNTER_ENABLED)
+  if(CMAKE_VERSION VERSION_LESS "3.2")
+    message(
+        FATAL_ERROR
+        "At least CMake version 3.2 required for Hunter dependency management."
+        " Update CMake or set HUNTER_ENABLED to OFF."
+    )
+  endif()
+endif()
+
+include(CMakeParseArguments) # cmake_parse_arguments
+
+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")
+
+function(hunter_gate_status_print)
+  if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
+    foreach(print_message ${ARGV})
+      message(STATUS "[hunter] ${print_message}")
+    endforeach()
+  endif()
+endfunction()
+
+function(hunter_gate_status_debug)
+  if(HUNTER_STATUS_DEBUG)
+    foreach(print_message ${ARGV})
+      string(TIMESTAMP timestamp)
+      message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}")
+    endforeach()
+  endif()
+endfunction()
+
+function(hunter_gate_wiki wiki_page)
+  message("------------------------------ WIKI -------------------------------")
+  message("    ${HUNTER_WIKI}/${wiki_page}")
+  message("-------------------------------------------------------------------")
+  message("")
+  message(FATAL_ERROR "")
+endfunction()
+
+function(hunter_gate_internal_error)
+  message("")
+  foreach(print_message ${ARGV})
+    message("[hunter ** INTERNAL **] ${print_message}")
+  endforeach()
+  message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
+  message("")
+  hunter_gate_wiki("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")
+  endif()
+  message("")
+  foreach(x ${hunter_UNPARSED_ARGUMENTS})
+    message("[hunter ** FATAL ERROR **] ${x}")
+  endforeach()
+  message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
+  message("")
+  hunter_gate_wiki("${hunter_WIKI}")
+endfunction()
+
+function(hunter_gate_user_error)
+  hunter_gate_fatal_error(${ARGV} WIKI "error.incorrect.input.data")
+endfunction()
+
+function(hunter_gate_self root version sha1 result)
+  string(COMPARE EQUAL "${root}" "" is_bad)
+  if(is_bad)
+    hunter_gate_internal_error("root is empty")
+  endif()
+
+  string(COMPARE EQUAL "${version}" "" is_bad)
+  if(is_bad)
+    hunter_gate_internal_error("version is empty")
+  endif()
+
+  string(COMPARE EQUAL "${sha1}" "" is_bad)
+  if(is_bad)
+    hunter_gate_internal_error("sha1 is empty")
+  endif()
+
+  string(SUBSTRING "${sha1}" 0 7 archive_id)
+
+  if(EXISTS "${root}/cmake/Hunter")
+    set(hunter_self "${root}")
+  else()
+    set(
+        hunter_self
+        "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked"
+    )
+  endif()
+
+  set("${result}" "${hunter_self}" PARENT_SCOPE)
+endfunction()
+
+# Set HUNTER_GATE_ROOT cmake variable to suitable value.
+function(hunter_gate_detect_root)
+  # Check CMake variable
+  string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty)
+  if(not_empty)
+    set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE)
+    hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable")
+    return()
+  endif()
+
+  # Check environment variable
+  string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty)
+  if(not_empty)
+    set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE)
+    hunter_gate_status_debug("HUNTER_ROOT detected by environment variable")
+    return()
+  endif()
+
+  # Check HOME environment variable
+  string(COMPARE NOTEQUAL "$ENV{HOME}" "" result)
+  if(result)
+    set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE)
+    hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable")
+    return()
+  endif()
+
+  # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only)
+  if(WIN32)
+    string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result)
+    if(result)
+      set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE)
+      hunter_gate_status_debug(
+          "HUNTER_ROOT set using SYSTEMDRIVE environment variable"
+      )
+      return()
+    endif()
+
+    string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result)
+    if(result)
+      set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE)
+      hunter_gate_status_debug(
+          "HUNTER_ROOT set using USERPROFILE environment variable"
+      )
+      return()
+    endif()
+  endif()
+
+  hunter_gate_fatal_error(
+      "Can't detect HUNTER_ROOT"
+      WIKI "error.detect.hunter.root"
+  )
+endfunction()
+
+function(hunter_gate_download dir)
+  string(
+      COMPARE
+      NOTEQUAL
+      "$ENV{HUNTER_DISABLE_AUTOINSTALL}"
+      ""
+      disable_autoinstall
+  )
+  if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL)
+    hunter_gate_fatal_error(
+        "Hunter not found in '${dir}'"
+        "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'"
+        "Settings:"
+        "  HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
+        "  HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
+        WIKI "error.run.install"
+    )
+  endif()
+  string(COMPARE EQUAL "${dir}" "" is_bad)
+  if(is_bad)
+    hunter_gate_internal_error("Empty 'dir' argument")
+  endif()
+
+  string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad)
+  if(is_bad)
+    hunter_gate_internal_error("HUNTER_GATE_SHA1 empty")
+  endif()
+
+  string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad)
+  if(is_bad)
+    hunter_gate_internal_error("HUNTER_GATE_URL empty")
+  endif()
+
+  set(done_location "${dir}/DONE")
+  set(sha1_location "${dir}/SHA1")
+
+  set(build_dir "${dir}/Build")
+  set(cmakelists "${dir}/CMakeLists.txt")
+
+  hunter_gate_status_debug("Locking directory: ${dir}")
+  file(LOCK "${dir}" DIRECTORY GUARD FUNCTION)
+  hunter_gate_status_debug("Lock done")
+
+  if(EXISTS "${done_location}")
+    # while waiting for lock other instance can do all the job
+    hunter_gate_status_debug("File '${done_location}' found, skip install")
+    return()
+  endif()
+
+  file(REMOVE_RECURSE "${build_dir}")
+  file(REMOVE_RECURSE "${cmakelists}")
+
+  file(MAKE_DIRECTORY "${build_dir}") # check directory permissions
+
+  # Disabling languages speeds up a little bit, reduces noise in the output
+  # and avoids path too long windows error
+  file(
+      WRITE
+      "${cmakelists}"
+      "cmake_minimum_required(VERSION 3.2)\n"
+      "project(HunterDownload LANGUAGES NONE)\n"
+      "include(ExternalProject)\n"
+      "ExternalProject_Add(\n"
+      "    Hunter\n"
+      "    URL\n"
+      "    \"${HUNTER_GATE_URL}\"\n"
+      "    URL_HASH\n"
+      "    SHA1=${HUNTER_GATE_SHA1}\n"
+      "    DOWNLOAD_DIR\n"
+      "    \"${dir}\"\n"
+      "    TLS_VERIFY\n"
+      "    ${HUNTER_TLS_VERIFY}\n"
+      "    SOURCE_DIR\n"
+      "    \"${dir}/Unpacked\"\n"
+      "    CONFIGURE_COMMAND\n"
+      "    \"\"\n"
+      "    BUILD_COMMAND\n"
+      "    \"\"\n"
+      "    INSTALL_COMMAND\n"
+      "    \"\"\n"
+      ")\n"
+  )
+
+  if(HUNTER_STATUS_DEBUG)
+    set(logging_params "")
+  else()
+    set(logging_params OUTPUT_QUIET)
+  endif()
+
+  hunter_gate_status_debug("Run generate")
+
+  # Need to add toolchain file too.
+  # Otherwise on Visual Studio + MDD this will fail with error:
+  # "Could not find an appropriate version of the Windows 10 SDK installed on this machine"
+  if(EXISTS "${CMAKE_TOOLCHAIN_FILE}")
+    get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE)
+    set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}")
+  else()
+    # 'toolchain_arg' can't be empty
+    set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=")
+  endif()
+
+  string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make)
+  if(no_make)
+    set(make_arg "")
+  else()
+    # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM
+    set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
+  endif()
+
+  execute_process(
+      COMMAND
+      "${CMAKE_COMMAND}"
+      "-H${dir}"
+      "-B${build_dir}"
+      "-G${CMAKE_GENERATOR}"
+      "${toolchain_arg}"
+      ${make_arg}
+      WORKING_DIRECTORY "${dir}"
+      RESULT_VARIABLE download_result
+      ${logging_params}
+  )
+
+  if(NOT download_result EQUAL 0)
+    hunter_gate_internal_error(
+        "Configure project failed."
+        "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}"
+        "In directory ${dir}"
+    )
+  endif()
+
+  hunter_gate_status_print(
+      "Initializing Hunter workspace (${HUNTER_GATE_SHA1})"
+      "  ${HUNTER_GATE_URL}"
+      "  -> ${dir}"
+  )
+  execute_process(
+      COMMAND "${CMAKE_COMMAND}" --build "${build_dir}"
+      WORKING_DIRECTORY "${dir}"
+      RESULT_VARIABLE download_result
+      ${logging_params}
+  )
+
+  if(NOT download_result EQUAL 0)
+    hunter_gate_internal_error("Build project failed")
+  endif()
+
+  file(REMOVE_RECURSE "${build_dir}")
+  file(REMOVE_RECURSE "${cmakelists}")
+
+  file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}")
+  file(WRITE "${done_location}" "DONE")
+
+  hunter_gate_status_debug("Finished")
+endfunction()
+
+# Must be a macro so master file 'cmake/Hunter' can
+# apply all variables easily just by 'include' command
+# (otherwise PARENT_SCOPE magic needed)
+macro(HunterGate)
+  if(HUNTER_GATE_DONE)
+    # variable HUNTER_GATE_DONE set explicitly for external project
+    # (see `hunter_download`)
+    set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
+  endif()
+
+  # First HunterGate command will init Hunter, others will be ignored
+  get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET)
+
+  if(NOT HUNTER_ENABLED)
+    # Empty function to avoid error "unknown function"
+    function(hunter_add_package)
+    endfunction()
+
+    set(
+        _hunter_gate_disabled_mode_dir
+        "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode"
+    )
+    if(EXISTS "${_hunter_gate_disabled_mode_dir}")
+      hunter_gate_status_debug(
+          "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}"
+      )
+      list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}")
+    endif()
+  elseif(_hunter_gate_done)
+    hunter_gate_status_debug("Secondary HunterGate (use old settings)")
+    hunter_gate_self(
+        "${HUNTER_CACHED_ROOT}"
+        "${HUNTER_VERSION}"
+        "${HUNTER_SHA1}"
+        _hunter_self
+    )
+    include("${_hunter_self}/cmake/Hunter")
+  else()
+    set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}")
+
+    string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name)
+    if(_have_project_name)
+      hunter_gate_fatal_error(
+          "Please set HunterGate *before* 'project' command. "
+          "Detected project: ${PROJECT_NAME}"
+          WIKI "error.huntergate.before.project"
+      )
+    endif()
+
+    cmake_parse_arguments(
+        HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV}
+    )
+
+    string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1)
+    string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url)
+    string(
+        COMPARE
+        NOTEQUAL
+        "${HUNTER_GATE_UNPARSED_ARGUMENTS}"
+        ""
+        _have_unparsed
+    )
+    string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global)
+    string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath)
+
+    if(_have_unparsed)
+      hunter_gate_user_error(
+          "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}"
+      )
+    endif()
+    if(_empty_sha1)
+      hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory")
+    endif()
+    if(_empty_url)
+      hunter_gate_user_error("URL suboption of HunterGate is mandatory")
+    endif()
+    if(_have_global)
+      if(HUNTER_GATE_LOCAL)
+        hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)")
+      endif()
+      if(_have_filepath)
+        hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)")
+      endif()
+    endif()
+    if(HUNTER_GATE_LOCAL)
+      if(_have_global)
+        hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)")
+      endif()
+      if(_have_filepath)
+        hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)")
+      endif()
+    endif()
+    if(_have_filepath)
+      if(_have_global)
+        hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)")
+      endif()
+      if(HUNTER_GATE_LOCAL)
+        hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)")
+      endif()
+    endif()
+
+    hunter_gate_detect_root() # set HUNTER_GATE_ROOT
+
+    # Beautify path, fix probable problems with windows path slashes
+    get_filename_component(
+        HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE
+    )
+    hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}")
+    if(NOT HUNTER_ALLOW_SPACES_IN_PATH)
+      string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces)
+      if(NOT _contain_spaces EQUAL -1)
+        hunter_gate_fatal_error(
+            "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"
+        )
+      endif()
+    endif()
+
+    string(
+        REGEX
+        MATCH
+        "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*"
+        HUNTER_GATE_VERSION
+        "${HUNTER_GATE_URL}"
+    )
+    string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty)
+    if(_is_empty)
+      set(HUNTER_GATE_VERSION "unknown")
+    endif()
+
+    hunter_gate_self(
+        "${HUNTER_GATE_ROOT}"
+        "${HUNTER_GATE_VERSION}"
+        "${HUNTER_GATE_SHA1}"
+        _hunter_self
+    )
+
+    set(_master_location "${_hunter_self}/cmake/Hunter")
+    if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter")
+      # Hunter downloaded manually (e.g. by 'git clone')
+      set(_unused "xxxxxxxxxx")
+      set(HUNTER_GATE_SHA1 "${_unused}")
+      set(HUNTER_GATE_VERSION "${_unused}")
+    else()
+      get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE)
+      set(_done_location "${_archive_id_location}/DONE")
+      set(_sha1_location "${_archive_id_location}/SHA1")
+
+      # Check Hunter already downloaded by HunterGate
+      if(NOT EXISTS "${_done_location}")
+        hunter_gate_download("${_archive_id_location}")
+      endif()
+
+      if(NOT EXISTS "${_done_location}")
+        hunter_gate_internal_error("hunter_gate_download failed")
+      endif()
+
+      if(NOT EXISTS "${_sha1_location}")
+        hunter_gate_internal_error("${_sha1_location} not found")
+      endif()
+      file(READ "${_sha1_location}" _sha1_value)
+      string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal)
+      if(NOT _is_equal)
+        hunter_gate_internal_error(
+            "Short SHA1 collision:"
+            "  ${_sha1_value} (from ${_sha1_location})"
+            "  ${HUNTER_GATE_SHA1} (HunterGate)"
+        )
+      endif()
+      if(NOT EXISTS "${_master_location}")
+        hunter_gate_user_error(
+            "Master file not found:"
+            "  ${_master_location}"
+            "try to update Hunter/HunterGate"
+        )
+      endif()
+    endif()
+    include("${_master_location}")
+    set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
+  endif()
+endmacro()

+ 14 - 0
cmake/assimp-hunter-config.cmake.in

@@ -0,0 +1,14 @@
+@PACKAGE_INIT@
+
+find_package(RapidJSON CONFIG REQUIRED)
+find_package(ZLIB CONFIG REQUIRED)
+find_package(utf8 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)
+
+include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
+check_required_components("@PROJECT_NAME@")

+ 17 - 15
code/3DSConverter.cpp → code/3DS/3DSConverter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // internal headers
 #include "3DSLoader.h"
-#include "TargetAnimation.h"
+#include "Common/TargetAnimation.h"
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/StringComparison.h>
@@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
     unsigned int idx( NotSet );
     for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
     {
-        std::string &s = mScene->mMaterials[i].mName;
+        std::string s = mScene->mMaterials[i].mName;
         for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) {
             *it = static_cast< char >( ::tolower( *it ) );
         }
@@ -120,7 +120,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
             else if ( (*a) >= mScene->mMaterials.size())
             {
                 (*a) = idx;
-                DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material");
+                ASSIMP_LOG_WARN("Material index overflow in 3DS file. Using default material");
                 ++cnt;
             }
         }
@@ -132,7 +132,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
         sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
         mScene->mMaterials.push_back(sMat);
 
-        DefaultLogger::get()->info("3DS: Generating default material");
+        ASSIMP_LOG_INFO("3DS: Generating default material");
     }
 }
 
@@ -147,12 +147,12 @@ void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
         {
             if ((*i).mIndices[a] >= sMesh.mPositions.size())
             {
-                DefaultLogger::get()->warn("3DS: Vertex index overflow)");
+                ASSIMP_LOG_WARN("3DS: Vertex index overflow)");
                 (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
             }
             if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size())
             {
-                DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)");
+                ASSIMP_LOG_WARN("3DS: Texture coordinate index overflow)");
                 (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1;
             }
         }
@@ -204,8 +204,9 @@ void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
         mat.AddProperty<ai_real>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
 
     // Setup the texture mapping mode
-    mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
-    mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
+    int mapMode = static_cast<int>(texture.mMapMode);
+    mat.AddProperty<int>(&mapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
+    mat.AddProperty<int>(&mapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
 
     // Mirroring - double the scaling values
     // FIXME: this is not really correct ...
@@ -313,7 +314,8 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
         case D3DS::Discreet3DS::Blinn :
             eShading = aiShadingMode_Blinn; break;
     }
-    mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
+    int eShading_ = static_cast<int>(eShading);
+    mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
 
     // DIFFUSE texture
     if( oldMat.sTexDiffuse.mMapName.length() > 0)
@@ -497,7 +499,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
                         pvCurrent->x *= -1.f;
                         t2->x *= -1.f;
                     }
-                    DefaultLogger::get()->info("3DS: Flipping mesh X-Axis");
+                    ASSIMP_LOG_INFO("3DS: Flipping mesh X-Axis");
                 }
 
                 // Handle pivot point
@@ -573,11 +575,11 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
         pcIn->aTargetPositionKeys.size() > 1)
     {
         aiAnimation* anim = pcSOut->mAnimations[0];
-        ai_assert(NULL != anim);
+        ai_assert(nullptr != anim);
 
         if (pcIn->aCameraRollKeys.size() > 1)
         {
-            DefaultLogger::get()->debug("3DS: Converting camera roll track ...");
+            ASSIMP_LOG_DEBUG("3DS: Converting camera roll track ...");
 
             // Camera roll keys - in fact they're just rotations
             // around the camera's z axis. The angles are given
@@ -597,7 +599,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
 #if 0
         if (pcIn->aTargetPositionKeys.size() > 1)
         {
-            DefaultLogger::get()->debug("3DS: Converting target track ...");
+            ASSIMP_LOG_DEBUG("3DS: Converting target track ...");
 
             // Camera or spot light - need to convert the separate
             // target position channel to our representation
@@ -743,7 +745,7 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
         //   |       |       |            |         |
         // MESH_0  MESH_1  MESH_2  ...  MESH_N    CAMERA_0 ....
         //
-        DefaultLogger::get()->warn("No hierarchy information has been found in the file. ");
+        ASSIMP_LOG_WARN("No hierarchy information has been found in the file. ");
 
         pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
             static_cast<unsigned int>(mScene->mCameras.size() + mScene->mLights.size());

+ 8 - 6
code/3DSExporter.cpp → code/3DS/3DSExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -43,15 +43,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
 
-#include "3DSExporter.h"
-#include "3DSLoader.h"
-#include "3DSHelper.h"
+#include "3DS/3DSExporter.h"
+#include "3DS/3DSLoader.h"
+#include "3DS/3DSHelper.h"
+#include "PostProcessing/SplitLargeMeshes.h"
+
 #include <assimp/SceneCombiner.h>
-#include "SplitLargeMeshes.h"
 #include <assimp/StringComparison.h>
 #include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Exporter.hpp>
+
 #include <memory>
 
 using namespace Assimp;
@@ -381,7 +383,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
 
     // TODO: handle embedded textures properly
     if (path.data[0] == '*') {
-        DefaultLogger::get()->error("Ignoring embedded texture for export: " + std::string(path.C_Str()));
+        ASSIMP_LOG_ERROR("Ignoring embedded texture for export: " + std::string(path.C_Str()));
         return;
     }
 

+ 1 - 1
code/3DSExporter.h → code/3DS/3DSExporter.h

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

+ 17 - 19
code/3DSHelper.h → code/3DS/3DSHelper.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -58,7 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp    {
 namespace D3DS  {
 
-#include "./../include/assimp/Compiler/pushpack1.h"
+#include <assimp/Compiler/pushpack1.h>
 
 // ---------------------------------------------------------------------------
 /** Discreet3DS class: Helper class for loading 3ds files. Defines chunks
@@ -66,7 +66,7 @@ namespace D3DS  {
 */
 class Discreet3DS {
 private:
-    Discreet3DS() {
+    Discreet3DS() AI_NO_EXCEPT {
         // empty
     }
 
@@ -328,19 +328,17 @@ struct Face : public FaceWithSmoothingGroup
 
 // ---------------------------------------------------------------------------
 /** Helper structure representing a texture */
-struct Texture
-{
+struct Texture {
     //! Default constructor
-    Texture()
-        : mOffsetU  (0.0)
-        , mOffsetV  (0.0)
-        , mScaleU   (1.0)
-        , mScaleV   (1.0)
-        , mRotation (0.0)
-        , mMapMode  (aiTextureMapMode_Wrap)
-        , bPrivate()
-        , iUVSrc    (0)
-    {
+    Texture() AI_NO_EXCEPT
+    : mOffsetU  (0.0)
+    , mOffsetV  (0.0)
+    , mScaleU   (1.0)
+    , mScaleV   (1.0)
+    , mRotation (0.0)
+    , mMapMode  (aiTextureMapMode_Wrap)
+    , bPrivate()
+    , iUVSrc    (0) {
         mTextureBlend = get_qnan();
     }
 
@@ -365,7 +363,7 @@ struct Texture
     int iUVSrc;
 };
 
-#include "./../include/assimp/Compiler/poppack1.h"
+#include <assimp/Compiler/poppack1.h>
 
 // ---------------------------------------------------------------------------
 /** Helper structure representing a 3ds material */
@@ -394,7 +392,7 @@ struct Material
 
 
     //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
-    Material(Material &&other)
+    Material(Material &&other) AI_NO_EXCEPT
     : mName(std::move(other.mName))
     , mDiffuse(std::move(other.mDiffuse))
     , mSpecularExponent(std::move(other.mSpecularExponent))
@@ -418,7 +416,7 @@ struct Material
     }
 
 
-    Material &operator=(Material &&other) {
+    Material &operator=(Material &&other) AI_NO_EXCEPT {
         if (this == &other) {
             return *this;
         }
@@ -447,7 +445,7 @@ struct Material
     }
 
 
-    ~Material() {}
+    virtual ~Material() {}
 
 
     //! Name of the material

+ 40 - 41
code/3DSLoader.cpp → code/3DS/3DSLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -50,9 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
-// internal headers
 #include "3DSLoader.h"
-#include <assimp/Macros.h>
 #include <assimp/IOSystem.hpp>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
@@ -71,7 +69,7 @@ static const aiImporterDesc desc = {
     0,
     0,
     0,
-    "3ds prj"
+	"3ds prj"
 };
 
 
@@ -127,7 +125,7 @@ Discreet3DSImporter::~Discreet3DSImporter() {
 // Returns whether the class can handle the format of the given file.
 bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     std::string extension = GetExtension(pFile);
-    if(extension == "3ds" || extension == "prj" ) {
+	if(extension == "3ds" || extension == "prj") {
         return true;
     }
 
@@ -161,19 +159,21 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
     aiScene* pScene, IOSystem* pIOHandler)
 {
     StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
-    this->stream = &stream;
 
     // We should have at least one chunk
     if (stream.GetRemainingSize() < 16) {
         throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile);
     }
+    this->stream = &stream;
 
     // Allocate our temporary 3DS representation
-    mScene = new D3DS::Scene();
+    D3DS::Scene _scene;
+    mScene = &_scene;
 
     // Initialize members
+    D3DS::Node _rootNode("UNNAMED");
     mLastNodeIndex             = -1;
-    mCurrentNode               = new D3DS::Node("UNNAMED");
+    mCurrentNode               = &_rootNode;
     mRootNode                  = mCurrentNode;
     mRootNode->mHierarchyPos   = -1;
     mRootNode->mHierarchyIndex = -1;
@@ -193,7 +193,6 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
     // file.
     for (auto &mesh : mScene->mMeshes) {
         if (mesh.mFaces.size() > 0 && mesh.mPositions.size() == 0)  {
-            delete mScene;
             throw DeadlyImportError("3DS file contains faces but no vertices: " + pFile);
         }
         CheckIndices(mesh);
@@ -201,7 +200,7 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
         ComputeNormalsWithSmoothingsGroups<D3DS::Face>(mesh);
     }
 
-    // Replace all occurrences of the default material with a
+    // Replace all occurences of the default material with a
     // valid material. Generate it if no material containing
     // DEFAULT in its name has been found in the file
     ReplaceDefaultMaterial();
@@ -218,10 +217,8 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile,
     // Now apply the master scaling factor to the scene
     ApplyMasterScale(pScene);
 
-    // Delete our internal scene representation and the root
-    // node, so the whole hierarchy will follow
-    delete mRootNode;
-    delete mScene;
+    // Our internal scene representation and the root
+    // node will be automatically deleted, so the whole hierarchy will follow
 
     AI_DEBUG_INVALIDATE_PTR(mRootNode);
     AI_DEBUG_INVALIDATE_PTR(mScene);
@@ -250,16 +247,18 @@ void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene)
 // Reads a new chunk from the file
 void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut)
 {
-    ai_assert(pcOut != NULL);
+    ai_assert(pcOut != nullptr);
 
     pcOut->Flag = stream->GetI2();
     pcOut->Size = stream->GetI4();
 
-    if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize())
+    if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) {
         throw DeadlyImportError("Chunk is too large");
+    }
 
-    if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit())
-        DefaultLogger::get()->error("3DS: Chunk overflow");
+    if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) {
+        ASSIMP_LOG_ERROR("3DS: Chunk overflow");
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -320,7 +319,7 @@ void Discreet3DSImporter::ParseEditorChunk()
         // print the version number
         char buff[10];
         ASSIMP_itoa10(buff,stream->GetI2());
-        DefaultLogger::get()->info(std::string("3DS file format version: ") + buff);
+        ASSIMP_LOG_INFO_F(std::string("3DS file format version: "), buff);
         }
         break;
     };
@@ -361,7 +360,7 @@ void Discreet3DSImporter::ParseObjectChunk()
         if (is_qnan(mClrAmbient.r))
         {
             // We failed to read the ambient base color.
-            DefaultLogger::get()->error("3DS: Failed to read ambient base color");
+            ASSIMP_LOG_ERROR("3DS: Failed to read ambient base color");
             mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f;
         }
         break;
@@ -463,7 +462,7 @@ void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
         if (len < 1e-5) {
 
             // There are some files with lookat == position. Don't know why or whether it's ok or not.
-            DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector");
+            ASSIMP_LOG_ERROR("3DS: Unable to read proper camera look-at vector");
             camera->mLookAt = aiVector3D(0.0,1.0,0.0);
 
         }
@@ -629,9 +628,9 @@ void Discreet3DSImporter::SkipTCBInfo()
     if (!flags) {
         // Currently we can't do anything with these values. They occur
         // quite rare, so it wouldn't be worth the effort implementing
-        // them. 3DS ist not really suitable for complex animations,
+        // them. 3DS is not really suitable for complex animations,
         // so full support is not required.
-        DefaultLogger::get()->warn("3DS: Skipping TCB animation info");
+        ASSIMP_LOG_WARN("3DS: Skipping TCB animation info");
     }
 
     if (flags & Discreet3DS::KEY_USE_TENS) {
@@ -732,7 +731,6 @@ void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent)
 
             // If object name is DUMMY, take this one instead
             if (mCurrentNode->mName == "$$$DUMMY")  {
-                //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object");
                 mCurrentNode->mName = std::string(sz);
                 break;
             }
@@ -743,7 +741,7 @@ void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent)
 
         if ( Discreet3DS::CHUNK_TRACKINFO != parent)
         {
-            DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object");
+            ASSIMP_LOG_WARN("3DS: Skipping pivot subchunk for non usual object");
             break;
         }
 
@@ -805,7 +803,7 @@ void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent)
         {
         // roll keys are accepted for cameras only
         if (parent != Discreet3DS::CHUNK_TRACKCAMERA)   {
-            DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object");
+            ASSIMP_LOG_WARN("3DS: Ignoring roll track for non-camera object");
             break;
         }
         bool sortKeys = false;
@@ -845,7 +843,7 @@ void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent)
         // CAMERA FOV KEYFRAME
     case Discreet3DS::CHUNK_TRACKFOV:
         {
-            DefaultLogger::get()->error("3DS: Skipping FOV animation track. "
+            ASSIMP_LOG_ERROR("3DS: Skipping FOV animation track. "
                 "This is not supported");
         }
         break;
@@ -985,7 +983,7 @@ void Discreet3DSImporter::ParseFaceChunk()
             }
         }
         if (0xcdcdcdcd == idx)  {
-            DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz);
+            ASSIMP_LOG_ERROR_F( "3DS: Unknown material: ", sz);
         }
 
         // Now continue and read all material indices
@@ -995,7 +993,7 @@ void Discreet3DSImporter::ParseFaceChunk()
 
             // check range
             if (fidx >= mMesh.mFaceMaterials.size())    {
-                DefaultLogger::get()->error("3DS: Invalid face index in face material list");
+                ASSIMP_LOG_ERROR("3DS: Invalid face index in face material list");
             }
             else mMesh.mFaceMaterials[fidx] = idx;
         }}
@@ -1110,7 +1108,7 @@ void Discreet3DSImporter::ParseMaterialChunk()
 
         if (!cnt)   {
             // This may not be, we use the default name instead
-            DefaultLogger::get()->error("3DS: Empty material name");
+            ASSIMP_LOG_ERROR("3DS: Empty material name");
         }
         else mScene->mMaterials.back().mName = std::string(sz,cnt);
         }
@@ -1123,7 +1121,7 @@ void Discreet3DSImporter::ParseMaterialChunk()
         ParseColorChunk(pc);
         if (is_qnan(pc->r)) {
             // color chunk is invalid. Simply ignore it
-            DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk");
+            ASSIMP_LOG_ERROR("3DS: Unable to read DIFFUSE chunk");
             pc->r = pc->g = pc->b = 1.0f;
         }}
         break;
@@ -1135,7 +1133,7 @@ void Discreet3DSImporter::ParseMaterialChunk()
         ParseColorChunk(pc);
         if (is_qnan(pc->r)) {
             // color chunk is invalid. Simply ignore it
-            DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk");
+            ASSIMP_LOG_ERROR("3DS: Unable to read SPECULAR chunk");
             pc->r = pc->g = pc->b = 1.0f;
         }}
         break;
@@ -1147,7 +1145,7 @@ void Discreet3DSImporter::ParseMaterialChunk()
         ParseColorChunk(pc);
         if (is_qnan(pc->r)) {
             // color chunk is invalid. Simply ignore it
-            DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk");
+            ASSIMP_LOG_ERROR("3DS: Unable to read AMBIENT chunk");
             pc->r = pc->g = pc->b = 0.0f;
         }}
         break;
@@ -1159,7 +1157,7 @@ void Discreet3DSImporter::ParseMaterialChunk()
         ParseColorChunk(pc);
         if (is_qnan(pc->r)) {
             // color chunk is invalid. Simply ignore it
-            DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk");
+            ASSIMP_LOG_ERROR("3DS: Unable to read EMISSIVE chunk");
             pc->r = pc->g = pc->b = 0.0f;
         }}
         break;
@@ -1293,7 +1291,7 @@ void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
         pcOut->mScaleU = stream->GetF4();
         if (0.0f == pcOut->mScaleU)
         {
-            DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1.");
+            ASSIMP_LOG_WARN("Texture coordinate scaling in the x direction is zero. Assuming 1.");
             pcOut->mScaleU = 1.0f;
         }
         break;
@@ -1302,7 +1300,7 @@ void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
         pcOut->mScaleV = stream->GetF4();
         if (0.0f == pcOut->mScaleV)
         {
-            DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1.");
+            ASSIMP_LOG_WARN("Texture coordinate scaling in the y direction is zero. Assuming 1.");
             pcOut->mScaleV = 1.0f;
         }
         break;
@@ -1344,15 +1342,16 @@ void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
 
 // ------------------------------------------------------------------------------------------------
 // Read a percentage chunk
-ai_real Discreet3DSImporter::ParsePercentageChunk()
-{
+ai_real Discreet3DSImporter::ParsePercentageChunk() {
     Discreet3DS::Chunk chunk;
     ReadChunk(&chunk);
 
-    if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag)
-        return stream->GetF4();
-    else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
+    if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) {
+        return stream->GetF4() * ai_real(100) / ai_real(0xFFFF);
+    } else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) {
         return (ai_real)((uint16_t)stream->GetI2()) / (ai_real)0xFFFF;
+    }
+
     return get_qnan();
 }
 

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

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

+ 1 - 1
code/3MFXmlTags.h → code/3MF/3MFXmlTags.h

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

+ 7 - 3
code/D3MFExporter.cpp → code/3MF/D3MFExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -55,7 +55,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "3MFXmlTags.h"
 #include "D3MFOpcPackage.h"
 
-#include <contrib/zip/src/zip.h>
+#ifdef ASSIMP_USE_HUNTER
+#  include <zip/zip.h>
+#else
+#  include <contrib/zip/src/zip.h>
+#endif
 
 namespace Assimp {
 
@@ -222,7 +226,7 @@ void D3MFExporter::writeMetaData() {
         return;
     }
 
-    const aiString *key;
+	const aiString *key = nullptr;
     const aiMetadataEntry *entry(nullptr);
     for ( size_t i = 0; i < numMetaEntries; ++i ) {
         mScene->mMetaData->Get( i, key, entry );

+ 1 - 1
code/D3MFExporter.h → code/3MF/D3MFExporter.h

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

+ 16 - 15
code/D3MFImporter.cpp → code/3MF/D3MFImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/importerdesc.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
+#include <assimp/ZipArchiveIOSystem.h>
 
 #include <string>
 #include <vector>
@@ -58,7 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory>
 
 #include "D3MFOpcPackage.h"
-#include <unzip.h>
 #include <assimp/irrXMLWrapper.h>
 #include "3MFXmlTags.h"
 #include <assimp/fast_atof.h>
@@ -115,10 +115,10 @@ public:
         // import the metadata
         if ( !mMetaData.empty() ) {
             const size_t numMeta( mMetaData.size() );
-            scene->mMetaData = aiMetadata::Alloc( numMeta );
+            scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>( numMeta ) );
             for ( size_t i = 0; i < numMeta; ++i ) {
                 aiString val( mMetaData[ i ].value );
-                scene->mMetaData->Set( i, mMetaData[ i ].name, val );
+                scene->mMetaData->Set(static_cast<unsigned int>( i ), mMetaData[ i ].name, val );
             }
         }
 
@@ -297,8 +297,9 @@ private:
             return false;
         }
 
+        //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
         const size_t len( strlen( color ) );
-        if ( 9 != len ) {
+        if ( 9 != len && 7 != len) {
             return false;
         }
 
@@ -313,26 +314,28 @@ private:
         ++buf;
         comp[ 1 ] = *buf;
         ++buf;
-        diffuse.r = static_cast<ai_real>( strtol( comp, NULL, 16 ) );
+        diffuse.r = static_cast<ai_real>( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
 
 
         comp[ 0 ] = *buf;
         ++buf;
         comp[ 1 ] = *buf;
         ++buf;
-        diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) );
+        diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
 
         comp[ 0 ] = *buf;
         ++buf;
         comp[ 1 ] = *buf;
         ++buf;
-        diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) );
+        diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
 
+        if(7 == len)
+            return true;
         comp[ 0 ] = *buf;
         ++buf;
         comp[ 1 ] = *buf;
         ++buf;
-        diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) );
+        diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
 
         return true;
     }
@@ -396,7 +399,7 @@ private:
                 return false;
             }
         }
-        DefaultLogger::get()->error("unexpected EOF, expected closing <" + closeTag + "> tag");
+        ASSIMP_LOG_ERROR("unexpected EOF, expected closing <" + closeTag + "> tag");
 
         return false;
     }
@@ -416,8 +419,6 @@ private:
 
 } //namespace D3MF
 
-static const std::string Extension = "3mf";
-
 static const aiImporterDesc desc = {
     "3mf Importer",
     "",
@@ -428,7 +429,7 @@ static const aiImporterDesc desc = {
     0,
     0,
     0,
-    Extension.c_str()
+    "3mf"
 };
 
 D3MFImporter::D3MFImporter()
@@ -442,13 +443,13 @@ D3MFImporter::~D3MFImporter() {
 
 bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
     const std::string extension( GetExtension( filename ) );
-    if(extension == Extension ) {
+    if(extension == desc.mFileExtensions ) {
         return true;
     } else if ( !extension.length() || checkSig ) {
         if ( nullptr == pIOHandler ) {
             return false;
         }
-        if ( !D3MF::D3MFOpcPackage::isZipArchive( pIOHandler, filename ) ) {
+        if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
             return false;
         }
         D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );

+ 1 - 1
code/D3MFImporter.h → code/3MF/D3MFImporter.h

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

+ 207 - 0
code/3MF/D3MFOpcPackage.cpp

@@ -0,0 +1,207 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
+
+#include "D3MFOpcPackage.h"
+#include <assimp/Exceptional.h>
+
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/ai_assert.h>
+#include <assimp/ZipArchiveIOSystem.h>
+
+#include <cstdlib>
+#include <memory>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <cassert>
+#include "3MFXmlTags.h"
+
+namespace Assimp {
+
+namespace D3MF {
+// ------------------------------------------------------------------------------------------------
+
+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);
+            }
+        }
+    }
+
+    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 ParseAttributes(XmlReader*) {
+        // empty
+    }
+
+    bool validateRels( OpcPackageRelationshipPtr &relPtr ) {
+        if ( relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty() ) {
+            return false;
+        }
+        return true;
+    }
+
+    void ParseChildNode(XmlReader* xmlReader) {        
+        OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
+
+        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 );
+        }
+    }
+
+    std::vector<OpcPackageRelationshipPtr> m_relationShips;
+};
+
+// ------------------------------------------------------------------------------------------------
+D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
+: mRootStream(nullptr)
+, mZipArchive() {    
+    mZipArchive.reset( new ZipArchiveIOSystem( pIOHandler, rFile ) );
+    if(!mZipArchive->isOpen()) {
+        throw DeadlyImportError("Failed to open file " + rFile+ ".");
+    }
+
+    std::vector<std::string> fileList;
+    mZipArchive->getFileList(fileList);
+
+    for (auto& file: fileList) {
+        if(file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) {
+            //PkgRelationshipReader pkgRelReader(file, archive);
+            ai_assert(mZipArchive->Exists(file.c_str()));
+
+            IOStream *fileStream = mZipArchive->Open(file.c_str());
+
+            ai_assert(fileStream != nullptr);
+
+            std::string rootFile = ReadPackageRootRelationship(fileStream);
+            if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) {
+                rootFile = rootFile.substr( 1 );
+                if ( rootFile[ 0 ] == '/' ) {
+                    // deal with zip-bug
+                    rootFile = rootFile.substr( 1 );
+                }
+            }
+
+            ASSIMP_LOG_DEBUG(rootFile);
+
+            mZipArchive->Close(fileStream);
+
+            mRootStream = mZipArchive->Open(rootFile.c_str());
+            ai_assert( mRootStream != nullptr );
+            if ( nullptr == mRootStream ) {
+                throw DeadlyExportError( "Cannot open root-file in archive : " + rootFile );
+            }
+
+        } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
+            ASSIMP_LOG_WARN_F("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES",file);
+        } else {
+            ASSIMP_LOG_WARN_F("Ignored file of unknown type: ",file);
+        }
+
+    }
+}
+
+D3MFOpcPackage::~D3MFOpcPackage() {
+    mZipArchive->Close(mRootStream);
+}
+
+IOStream* D3MFOpcPackage::RootStream() const {
+    return mRootStream;
+}
+
+static const std::string ModelRef = "3D/3dmodel.model";
+
+bool D3MFOpcPackage::validate() {
+    if ( nullptr == mRootStream || nullptr == mZipArchive ) {
+        return false;
+    }
+
+    return mZipArchive->Exists( ModelRef.c_str() );
+}
+
+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()));
+
+    OpcPackageRelationshipReader reader(xml.get());
+
+    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 );
+    }
+
+    return (*itr)->target;
+}
+
+} // Namespace D3MF
+} // Namespace Assimp
+
+#endif //ASSIMP_BUILD_NO_3MF_IMPORTER

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

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

+ 21 - 21
code/ACLoader.cpp → code/AC/ACLoader.cpp

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ParsingUtils.h>
 #include <assimp/fast_atof.h>
 #include <assimp/Subdivision.h>
-#include "Importer.h"
+#include "Common/Importer.h"
 #include <assimp/BaseImporter.h>
 #include <assimp/Importer.hpp>
 #include <assimp/light.h>
@@ -85,7 +85,7 @@ static const aiImporterDesc desc = {
 #define AI_AC_SKIP_TO_NEXT_TOKEN() \
     if (!SkipSpaces(&buffer)) \
     { \
-        DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \
+        ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL"); \
         continue; \
     }
 
@@ -101,7 +101,7 @@ static const aiImporterDesc desc = {
     { \
         if (IsLineEnd( *buffer )) \
         { \
-            DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \
+            ASSIMP_LOG_ERROR("AC3D: Unexpected EOF/EOL in string"); \
             out = "ERROR"; \
             break; \
         } \
@@ -120,7 +120,7 @@ static const aiImporterDesc desc = {
     { \
         if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \
         { \
-            DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \
+            ASSIMP_LOG_ERROR("AC3D: Unexpexted token. " name " was expected."); \
             continue; \
         } \
         buffer += name_length+1; \
@@ -217,7 +217,7 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
         light->mName.length = ::ai_snprintf(light->mName.data, MAXLEN, "ACLight_%i",static_cast<unsigned int>(mLights->size())-1);
         obj.name = std::string( light->mName.data );
 
-        DefaultLogger::get()->debug("AC3D: Light source encountered");
+        ASSIMP_LOG_DEBUG("AC3D: Light source encountered");
         obj.type = Object::Light;
     }
     else if (!ASSIMP_strincmp(buffer,"group",5))
@@ -307,12 +307,12 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
             {
                 if (!GetNextLine())
                 {
-                    DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet");
+                    ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: not all vertices have been parsed yet");
                     break;
                 }
                 else if (!IsNumeric(*buffer))
                 {
-                    DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet");
+                    ASSIMP_LOG_ERROR("AC3D: Unexpected token: not all vertices have been parsed yet");
                     --buffer; // make sure the line is processed a second time
                     break;
                 }
@@ -338,8 +338,8 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
                     // example writes no surf chunks
                     if (!Q3DWorkAround)
                     {
-                        DefaultLogger::get()->warn("AC3D: SURF token was expected");
-                        DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled");
+                        ASSIMP_LOG_WARN("AC3D: SURF token was expected");
+                        ASSIMP_LOG_DEBUG("Continuing with Quick3D Workaround enabled");
                     }
                     --buffer; // make sure the line is processed a second time
                     // break; --- see fix notes above
@@ -384,7 +384,7 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
                         {
                             if(!GetNextLine())
                             {
-                                DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete");
+                                ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: surface references are incomplete");
                                 break;
                             }
                             surf.entries.push_back(Surface::SurfaceEntry());
@@ -405,7 +405,7 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
             }
         }
     }
-    DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected");
+    ASSIMP_LOG_ERROR("AC3D: Unexpected EOF: \'kids\' line was expected");
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -478,7 +478,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                  therefore: if no surfaces are defined return point data only
              */
 
-            DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, "
+            ASSIMP_LOG_INFO("AC3D: No surfaces defined in object definition, "
                 "a point list is returned");
 
             meshes.push_back(new aiMesh());
@@ -519,12 +519,12 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                 unsigned int idx = (*it).mat;
                 if (idx >= needMat.size())
                 {
-                    DefaultLogger::get()->error("AC3D: material index is out of range");
+                    ASSIMP_LOG_ERROR("AC3D: material index is out of range");
                     idx = 0;
                 }
                 if ((*it).entries.empty())
                 {
-                    DefaultLogger::get()->warn("AC3D: surface her zero vertex references");
+                    ASSIMP_LOG_WARN("AC3D: surface her zero vertex references");
                 }
 
                 // validate all vertex indices to make sure we won't crash here
@@ -533,7 +533,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                 {
                     if ((*it2).first >= object.vertices.size())
                     {
-                        DefaultLogger::get()->warn("AC3D: Invalid vertex reference");
+                        ASSIMP_LOG_WARN("AC3D: Invalid vertex reference");
                         (*it2).first = 0;
                     }
                 }
@@ -561,7 +561,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
 
                     if ((*it).flags & 0xf)
                     {
-                        DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown");
+                        ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown");
                         (*it).flags &= ~(0xf);
                     }
 
@@ -712,7 +712,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
             if (object.subDiv)  {
                 if (configEvalSubdivision) {
                     std::unique_ptr<Subdivider> div(Subdivider::Create(Subdivider::CATMULL_CLARKE));
-                    DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name);
+                    ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: "+object.name);
 
                     std::vector<aiMesh*> cpy(meshes.size()-oldm,NULL);
                     div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true);
@@ -721,7 +721,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
                     // previous meshes are deleted vy Subdivide().
                 }
                 else {
-                    DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: "
+                    ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: "
                         +object.name);
                 }
             }
@@ -813,7 +813,7 @@ void AC3DImporter::InternReadFile( const std::string& pFile,
     unsigned int version = HexDigitToDecimal( buffer[4] );
     char msg[3];
     ASSIMP_itoa10(msg,3,version);
-    DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg);
+    ASSIMP_LOG_INFO_F("AC3D file format version: ", msg);
 
     std::vector<Material> materials;
     materials.reserve(5);
@@ -857,7 +857,7 @@ void AC3DImporter::InternReadFile( const std::string& pFile,
     }
     if (materials.empty())
     {
-        DefaultLogger::get()->warn("AC3D: No material has been found");
+        ASSIMP_LOG_WARN("AC3D: No material has been found");
         materials.push_back(Material());
     }
 

+ 1 - 1
code/ACLoader.h → code/AC/ACLoader.h

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

+ 3 - 3
code/AMFImporter.cpp → code/AMF/AMFImporter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -83,7 +83,7 @@ void AMFImporter::Clear()
 	mMaterial_Converted.clear();
 	mTexture_Converted.clear();
 	// Delete all elements
-	if(mNodeElement_List.size())
+	if(!mNodeElement_List.empty())
 	{
 		for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
 
@@ -230,7 +230,7 @@ casu_cres:
 	if(!skipped_before[sk_idx])
 	{
 		skipped_before[sk_idx] = true;
-		LogWarning("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
+        ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
 	}
 }
 

+ 65 - 197
code/AMFImporter.hpp → code/AMF/AMFImporter.hpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -63,8 +63,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // Header files, stdlib.
 #include <set>
 
-namespace Assimp
-{
+namespace Assimp {
+
 /// \class AMFImporter
 /// Class that holding scene graph which include: geometry, metadata, materials etc.
 ///
@@ -99,100 +99,49 @@ namespace Assimp
 ///            new - <texmap> and children <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>
 ///            old - <map> and children <u1>, <u2>, <u3>, <v1>, <v2>, <v3>
 ///
-class AMFImporter : public BaseImporter
-{
-	/***********************************************/
-	/******************** Types ********************/
-	/***********************************************/
-
-private:
-
-	struct SPP_Material;// forward declaration
-
-	/// \struct SPP_Composite
-	/// Data type for postprocessing 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.
-	};
-
-	/// \struct SPP_Material
-	/// Data type for postprocessing 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.
-
-		/// \fn aiColor4D GetColor(const float pX, const float pY, const float pZ) const
-		/// Return color calculated for specified coordinate.
-		/// \param [in] pX - "x" coordinate.
-		/// \param [in] pY - "y" coordinate.
-		/// \param [in] pZ - "z" coordinate.
-		/// \return calculated color.
-		aiColor4D GetColor(const float pX, const float pY, const float pZ) const;
-	};
-
-	/// \struct SPP_Texture
-	/// 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;
-	};
-
-	///	\struct SComplexFace
-	/// 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.
-	};
-
-
-
-	/***********************************************/
-	/****************** Constants ******************/
-	/***********************************************/
-
-private:
-
-	static const aiImporterDesc Description;
-
-	/***********************************************/
-	/****************** Variables ******************/
-	/***********************************************/
-
+class AMFImporter : public BaseImporter {
 private:
+    struct SPP_Material;// forward declaration
+
+                        /// \struct SPP_Composite
+                        /// 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.
+    };
+
+    /// \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.
+
+        /// Return color calculated for specified coordinate.
+        /// \param [in] pX - "x" coordinate.
+        /// \param [in] pY - "y" coordinate.
+        /// \param [in] pZ - "z" coordinate.
+        /// \return calculated color.
+        aiColor4D GetColor(const float pX, const float pY, const float pZ) const;
+    };
+
+    /// 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;
+    };
+
+    /// 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.
+    };
 
-    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
-	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.
-
-	/***********************************************/
-	/****************** Functions ******************/
-	/***********************************************/
-
-private:
-
-	/// \fn AMFImporter(const AMFImporter& pScene)
-	/// Disabled copy constructor.
-	AMFImporter(const AMFImporter& pScene);
-
-	/// \fn AMFImporter& operator=(const AMFImporter& pScene)
-	/// Disabled assign operator.
-	AMFImporter& operator=(const AMFImporter& pScene);
-
-	/// \fn void Clear()
 	/// Clear all temporary data.
 	void Clear();
 
@@ -200,7 +149,6 @@ private:
 	/************* Functions: find set *************/
 	/***********************************************/
 
-	/// \fn bool Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, aiNode** pNode) const
 	/// 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.
@@ -208,7 +156,6 @@ private:
 	/// \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;
 
-	/// \fn bool Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) 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.
@@ -216,15 +163,13 @@ private:
 	/// \return true - if the node is found, else - false.
 	bool Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const;
 
-	/// \fn bool Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) 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;
 
-	/// \fn 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
-	/// Find texture in list of converted textures. Use at postprocessing step,
+    /// 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.
@@ -235,11 +180,7 @@ private:
 	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;
 
-	/***********************************************/
-	/********* Functions: postprocess set **********/
-	/***********************************************/
 
-	/// \fn void PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray, std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) 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>.
@@ -248,7 +189,6 @@ private:
 	void PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray,
 												std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const;
 
-	/// \fn 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)
 	/// 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.
@@ -260,27 +200,23 @@ private:
 	/// \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);
 
-	/// \fn void PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> > pOutputList_Separated)
 	/// 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);
 
-	/// \fn void Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& pMetadataList, aiNode& pSceneNode) const
 	/// 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;
 
-	/// \fn void Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode)
 	/// 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);
 
-	/// \fn 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)
 	/// 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.
@@ -294,27 +230,20 @@ private:
 									const std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray, const CAMFImporter_NodeElement_Color* pObjectColor,
 									std::list<aiMesh*>& pMeshList, aiNode& pSceneNode);
 
-	/// \fn void Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial)
 	/// 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);
 
-	/// \fn void Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const
 	/// 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;
 
-	/// \fn void Postprocess_BuildScene()
 	/// 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);
 
-	/***********************************************/
-	/************* Functions: throw set ************/
-	/***********************************************/
 
-	/// \fn void Throw_CloseNotFound(const std::string& pNode)
 	/// Call that function when close tag of node not found and exception must be raised.
 	/// E.g.:
 	/// <amf>
@@ -324,19 +253,16 @@ private:
 	/// \param [in] pNode - node name in which exception happened.
 	void Throw_CloseNotFound(const std::string& pNode);
 
-	/// \fn void Throw_IncorrectAttr(const std::string& pAttrName)
 	/// 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);
 
-	/// \fn void Throw_IncorrectAttrValue(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);
 
-	/// \fn void Throw_MoreThanOnceDefined(const std::string& pNode, const std::string& pDescription)
 	/// 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>
@@ -348,216 +274,158 @@ private:
 	/// \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);
 
-	/// \fn void Throw_ID_NotFound(const std::string& pID) const
 	/// 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;
 
-	/***********************************************/
-	/************** Functions: LOG set *************/
-	/***********************************************/
-
-	/// \fn void LogInfo(const std::string& pMessage)
-	/// Short variant for calling \ref DefaultLogger::get()->info()
-	void LogInfo(const std::string& pMessage) { DefaultLogger::get()->info(pMessage); }
-
-	/// \fn void LogWarning(const std::string& pMessage)
-	/// Short variant for calling \ref DefaultLogger::get()->warn()
-	void LogWarning(const std::string& pMessage) { DefaultLogger::get()->warn(pMessage); }
-
-	/// \fn void LogError(const std::string& pMessage)
-	/// Short variant for calling \ref DefaultLogger::get()->error()
-	void LogError(const std::string& pMessage) { DefaultLogger::get()->error(pMessage); }
-
-	/***********************************************/
-	/************** Functions: XML set *************/
-	/***********************************************/
-
-	/// \fn void XML_CheckNode_MustHaveChildren()
 	/// Check if current node have children: <node>...</node>. If not then exception will throwed.
 	void XML_CheckNode_MustHaveChildren();
 
-	/// \fn bool XML_CheckNode_NameEqual(const std::string& pNodeName)
 	/// 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; }
 
-	/// \fn void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
 	/// 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);
 
-	/// \fn bool XML_SearchNode(const std::string& pNodeName)
 	/// 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);
 
-	/// \fn bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
 	/// Read attribute value.
 	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
 	/// \return read data.
 	bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx);
 
-	/// \fn float XML_ReadNode_GetAttrVal_AsFloat(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);
 
-	/// \fn uint32_t XML_ReadNode_GetAttrVal_AsU32(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);
 
-	/// \fn float XML_ReadNode_GetVal_AsFloat()
 	/// Read node value.
 	/// \return read data.
 	float XML_ReadNode_GetVal_AsFloat();
 
-	/// \fn uint32_t XML_ReadNode_GetVal_AsU32()
 	/// Read node value.
 	/// \return read data.
 	uint32_t XML_ReadNode_GetVal_AsU32();
 
-	/// \fn void XML_ReadNode_GetVal_AsString(std::string& pValue)
 	/// Read node value.
 	/// \return read data.
 	void XML_ReadNode_GetVal_AsString(std::string& pValue);
 
-	/***********************************************/
-	/******** Functions: parse set private *********/
-	/***********************************************/
-
-	/// \fn void ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
 	/// 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);
 
-	/// \fn void ParseHelper_Group_End()
 	/// This function must be called when exiting from grouping node. \ref ParseHelper_Group_Begin.
 	void ParseHelper_Node_Exit();
 
-	/// \fn void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
 	/// 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);
 
-	/// \fn void ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const
 	/// 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;
 
-	/// \fn void ParseNode_Root()
 	/// Parse <AMF> node of the file.
 	void ParseNode_Root();
 
-	/******** Functions: top nodes *********/
-
-	/// \fn void ParseNode_Constellation()
 	/// Parse <constellation> node of the file.
 	void ParseNode_Constellation();
 
-	/// \fn void ParseNode_Constellation()
 	/// Parse <instance> node of the file.
 	void ParseNode_Instance();
 
-	/// \fn void ParseNode_Material()
 	/// Parse <material> node of the file.
 	void ParseNode_Material();
 
-	/// \fn void ParseNode_Metadata()
 	/// Parse <metadata> node.
 	void ParseNode_Metadata();
 
-	/// \fn void ParseNode_Object()
 	/// Parse <object> node of the file.
 	void ParseNode_Object();
 
-	/// \fn void ParseNode_Texture()
 	/// Parse <texture> node of the file.
 	void ParseNode_Texture();
 
-	/******** Functions: geometry nodes *********/
-
-	/// \fn void ParseNode_Coordinates()
 	/// Parse <coordinates> node of the file.
 	void ParseNode_Coordinates();
 
-	/// \fn void ParseNode_Edge()
 	/// Parse <edge> node of the file.
 	void ParseNode_Edge();
 
-	/// \fn void ParseNode_Mesh()
 	/// Parse <mesh> node of the file.
 	void ParseNode_Mesh();
 
-	/// \fn void ParseNode_Triangle()
 	/// Parse <triangle> node of the file.
 	void ParseNode_Triangle();
 
-	/// \fn void ParseNode_Vertex()
 	/// Parse <vertex> node of the file.
 	void ParseNode_Vertex();
 
-	/// \fn void ParseNode_Vertices()
 	/// Parse <vertices> node of the file.
 	void ParseNode_Vertices();
 
-	/// \fn void ParseNode_Volume()
 	/// Parse <volume> node of the file.
 	void ParseNode_Volume();
 
-	/******** Functions: material nodes *********/
-
-	/// \fn void ParseNode_Color()
 	/// Parse <color> node of the file.
 	void ParseNode_Color();
 
-	/// \fn void ParseNode_TexMap(const bool pUseOldName = false)
 	/// 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);
 
 public:
-
-	/// \fn AMFImporter()
 	/// Default constructor.
-	AMFImporter()
-		: mNodeElement_Cur(nullptr), mReader(nullptr)
-	{}
+	AMFImporter() AI_NO_EXCEPT
+    : mNodeElement_Cur(nullptr)
+    , mReader(nullptr) {
+        // empty
+    }
 
-	/// \fn ~AMFImporter()
 	/// Default destructor.
 	~AMFImporter();
 
-	/***********************************************/
-	/******** Functions: parse set, public *********/
-	/***********************************************/
-
-	/// \fn void ParseFile(const std::string& pFile, IOSystem* pIOHandler)
 	/// 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 throwed if trouble will found.
+	/// 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);
 
-	/***********************************************/
-	/********* Functions: BaseImporter set *********/
-	/***********************************************/
-
 	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;
 
-};// class AMFImporter
+    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
+    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.
+
+};
 
 }// namespace Assimp
 

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

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 

+ 5 - 5
code/AMFImporter_Macro.hpp → code/AMF/AMFImporter_Macro.hpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -71,7 +71,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	}
 
 /// \def MACRO_ATTRREAD_CHECK_REF
-/// Check curent attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
+/// Check current attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
 /// "continue" will called.
 /// \param [in] pAttrName - attribute name.
 /// \param [out] pVarName - output variable name.
@@ -84,7 +84,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	}
 
 /// \def MACRO_ATTRREAD_CHECK_RET
-/// Check curent attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
+/// Check current attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
 /// If result was read then  "continue" will called.
 /// \param [in] pAttrName - attribute name.
 /// \param [out] pVarName - output variable name.
@@ -130,7 +130,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	} while(false)
 
 /// \def MACRO_NODECHECK_READCOMP_F
-/// Check curent node name and if it equal to requested then read value. Result write to output variable of type "float".
+/// Check current node name and if it equal to requested then read value. Result write to output variable of type "float".
 /// If result was read then  "continue" will called. Also check if node data already read then raise exception.
 /// \param [in] pNodeName - node name.
 /// \param [in, out] pReadFlag - read flag.
@@ -147,7 +147,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 	}
 
 /// \def MACRO_NODECHECK_READCOMP_U32
-/// Check curent node name and if it equal to requested then read value. Result write to output variable of type "uint32_t".
+/// Check current node name and if it equal to requested then read value. Result write to output variable of type "uint32_t".
 /// If result was read then  "continue" will called. Also check if node data already read then raise exception.
 /// \param [in] pNodeName - node name.
 /// \param [in, out] pReadFlag - read flag.

+ 60 - 42
code/AMFImporter_Material.cpp → code/AMF/AMFImporter_Material.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -68,10 +68,9 @@ 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;
+void AMFImporter::ParseNode_Color() {
+    std::string profile;
+    CAMFImporter_NodeElement* ne;
 
 	// Read attributes for node <color>.
 	MACRO_ATTRREAD_LOOPBEG;
@@ -98,15 +97,19 @@ CAMFImporter_NodeElement* ne;
 		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.");
-		// check if <a> is absent. Then manualy add "a == 1".
-		if(!read_flag[3]) als.Color.a = 1;
-
-	}// if(!mReader->isEmptyElement())
+        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
-	}// if(!mReader->isEmptyElement()) else
+	}
 
 	als.Composed = false;
 	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
@@ -119,10 +122,9 @@ CAMFImporter_NodeElement* ne;
 // An available material.
 // Multi elements - Yes.
 // Parent element - <amf>.
-void AMFImporter::ParseNode_Material()
-{
-std::string id;
-CAMFImporter_NodeElement* ne;
+void AMFImporter::ParseNode_Material() {
+    std::string id;
+    CAMFImporter_NodeElement* ne;
 
 	// Read attributes for node <color>.
 	MACRO_ATTRREAD_LOOPBEG;
@@ -131,9 +133,11 @@ CAMFImporter_NodeElement* ne;
 
 	// create new object.
 	ne = new CAMFImporter_NodeElement_Material(mNodeElement_Cur);
-	// and assign read data
+
+    // and assign read data
 	((CAMFImporter_NodeElement_Material*)ne)->ID = id;
-	// Check for child nodes
+
+    // Check for child nodes
 	if(!mReader->isEmptyElement())
 	{
 		bool col_read = false;
@@ -154,11 +158,11 @@ CAMFImporter_NodeElement* ne;
 			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
 		MACRO_NODECHECK_LOOPEND("material");
 		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.
 }
@@ -181,14 +185,13 @@ CAMFImporter_NodeElement* ne;
 // 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;
-CAMFImporter_NodeElement* ne;
+    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;
@@ -201,20 +204,34 @@ CAMFImporter_NodeElement* ne;
 	MACRO_ATTRREAD_LOOPEND;
 
 	// create new texture object.
-	ne = new CAMFImporter_NodeElement_Texture(mNodeElement_Cur);
+    CAMFImporter_NodeElement *ne = new CAMFImporter_NodeElement_Texture(mNodeElement_Cur);
 
 	CAMFImporter_NodeElement_Texture& als = *((CAMFImporter_NodeElement_Texture*)ne);// alias for convenience
 
 	// Check for child nodes
-	if(!mReader->isEmptyElement()) XML_ReadNode_GetVal_AsString(enc64_data);
+    if (!mReader->isEmptyElement()) {
+        XML_ReadNode_GetVal_AsString(enc64_data);
+    }
 
 	// check that all components was defined
-	if(id.empty()) throw DeadlyImportError("ID for texture must be defined.");
-	if(width < 1) Throw_IncorrectAttrValue("width");
-	if(height < 1) Throw_IncorrectAttrValue("height");
-	if(depth < 1) Throw_IncorrectAttrValue("depth");
-	if(type != "grayscale") Throw_IncorrectAttrValue("type");
-	if(enc64_data.empty()) throw DeadlyImportError("Texture data not defined.");
+    if (id.empty()) {
+        throw DeadlyImportError("ID for texture must be defined.");
+    }
+    if (width < 1) {
+        Throw_IncorrectAttrValue("width");
+    }
+    if (height < 1) {
+        Throw_IncorrectAttrValue("height");
+    }
+    if (depth < 1) {
+        Throw_IncorrectAttrValue("depth");
+    }
+    if (type != "grayscale") {
+        Throw_IncorrectAttrValue("type");
+    }
+    if (enc64_data.empty()) {
+        throw DeadlyImportError("Texture data not defined.");
+    }
 	// copy data
 	als.ID = id;
 	als.Width = width;
@@ -222,8 +239,11 @@ CAMFImporter_NodeElement* ne;
 	als.Depth = depth;
 	als.Tiled = tiled;
 	ParseHelper_Decode_Base64(enc64_data, als.Data);
-	// check data size
-	if((width * height * depth) != als.Data.size()) throw DeadlyImportError("Texture has incorrect data size.");
+
+    // check data size
+    if ((width * height * depth) != als.Data.size()) {
+        throw DeadlyImportError("Texture has incorrect data size.");
+    }
 
 	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.
@@ -243,10 +263,8 @@ CAMFImporter_NodeElement* ne;
 //   <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;
-CAMFImporter_NodeElement* ne;
+void AMFImporter::ParseNode_TexMap(const bool pUseOldName) {
+    std::string rtexid, gtexid, btexid, atexid;
 
 	// Read attributes for node <color>.
 	MACRO_ATTRREAD_LOOPBEG;
@@ -257,7 +275,7 @@ CAMFImporter_NodeElement* ne;
 	MACRO_ATTRREAD_LOOPEND;
 
 	// create new texture coordinates object.
-	ne = new CAMFImporter_NodeElement_TexMap(mNodeElement_Cur);
+    CAMFImporter_NodeElement *ne = new CAMFImporter_NodeElement_TexMap(mNodeElement_Cur);
 
 	CAMFImporter_NodeElement_TexMap& als = *((CAMFImporter_NodeElement_TexMap*)ne);// alias for convenience
 	// check data

+ 63 - 124
code/AMFImporter_Node.hpp → code/AMF/AMFImporter_Node.hpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -62,7 +62,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /// \class CAMFImporter_NodeElement
 /// Base class for elements of nodes.
 class CAMFImporter_NodeElement {
-
 public:
 	/// Define what data type contain node element.
 	enum EType {
@@ -96,15 +95,11 @@ public:                                               /// Destructor, virtual..
         // empty
     }
 
-private:
-	/// Disabled copy constructor.
-	CAMFImporter_NodeElement(const CAMFImporter_NodeElement& pNodeElement);
-
-	/// Disabled assign operator.
-	CAMFImporter_NodeElement& operator=(const CAMFImporter_NodeElement& pNodeElement);
-
-	/// Disabled default constructor.
-	CAMFImporter_NodeElement();
+	/// 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;
 
 protected:
 	/// In constructor inheritor must set element type.
@@ -121,9 +116,7 @@ protected:
 
 /// \struct CAMFImporter_NodeElement_Constellation
 /// A collection of objects or constellations with specific relative locations.
-struct CAMFImporter_NodeElement_Constellation : public CAMFImporter_NodeElement
-{
-	/// \fn CAMFImporter_NodeElement_Constellation(CAMFImporter_NodeElement* pParent)
+struct CAMFImporter_NodeElement_Constellation : public CAMFImporter_NodeElement {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Constellation(CAMFImporter_NodeElement* pParent)
@@ -134,9 +127,7 @@ struct CAMFImporter_NodeElement_Constellation : public CAMFImporter_NodeElement
 
 /// \struct CAMFImporter_NodeElement_Instance
 /// Part of constellation.
-struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement
-{
-	/****************** Variables ******************/
+struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement {
 
 	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
@@ -147,237 +138,185 @@ struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement
 	/// instance of the object in the current constellation. Rotations shall be executed in order of x first, then y, then z.
 	aiVector3D Rotation;
 
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_Instance(CAMFImporter_NodeElement* pParent)
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Instance(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Instance, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Instance
+};
 
 /// \struct CAMFImporter_NodeElement_Metadata
 /// Structure that define metadata node.
-struct CAMFImporter_NodeElement_Metadata : public CAMFImporter_NodeElement
-{
-	/****************** Variables ******************/
+struct CAMFImporter_NodeElement_Metadata : public CAMFImporter_NodeElement {
 
-	std::string Type;///< Type of "Value".
+	std::string Type;///< Type of "Value". 
 	std::string Value;///< Value.
 
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_Metadata(CAMFImporter_NodeElement* pParent)
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Metadata(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Metadata, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Metadata
+};
 
 /// \struct CAMFImporter_NodeElement_Root
 /// Structure that define root node.
-struct CAMFImporter_NodeElement_Root : public CAMFImporter_NodeElement
-{
-	/****************** Variables ******************/
+struct CAMFImporter_NodeElement_Root : public CAMFImporter_NodeElement {
 
 	std::string Unit;///< The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
 	std::string Version;///< Version of format.
 
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_Root(CAMFImporter_NodeElement* pParent)
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Root(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Root, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Root
+};
 
 /// \struct CAMFImporter_NodeElement_Color
 /// Structure that define object node.
-struct CAMFImporter_NodeElement_Color : public CAMFImporter_NodeElement
-{
-	/****************** Variables ******************/
-
-	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>..
-
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_Color(CAMFImporter_NodeElement* pParent)
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent 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..
+
+	/// @brief  Constructor.
+	/// @param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Color(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Color, pParent)
-	{}
-
-};// struct CAMFImporter_NodeElement_Color
+	: CAMFImporter_NodeElement(ENET_Color, pParent)
+    , Composed( false )
+    , Color()
+    , Profile() {
+        // empty
+    }
+};
 
 /// \struct CAMFImporter_NodeElement_Material
 /// Structure that define material node.
-struct CAMFImporter_NodeElement_Material : public CAMFImporter_NodeElement
-{
-	/// \fn CAMFImporter_NodeElement_Material(CAMFImporter_NodeElement* pParent)
+struct CAMFImporter_NodeElement_Material : public CAMFImporter_NodeElement {
+	
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Material(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Material, pParent)
 	{}
 
-};// struct CAMFImporter_NodeElement_Material
+};
 
 /// \struct CAMFImporter_NodeElement_Object
 /// Structure that define object node.
-struct CAMFImporter_NodeElement_Object : public CAMFImporter_NodeElement
-{
-	/// \fn CAMFImporter_NodeElement_Object(CAMFImporter_NodeElement* pParent)
-	/// Constructor.
+struct CAMFImporter_NodeElement_Object : public CAMFImporter_NodeElement {
+
+    /// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Object(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Object, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Object
+};
 
 /// \struct CAMFImporter_NodeElement_Mesh
 /// Structure that define mesh node.
-struct CAMFImporter_NodeElement_Mesh : public CAMFImporter_NodeElement
-{
-	/// \fn CAMFImporter_NodeElement_Mesh(CAMFImporter_NodeElement* pParent)
+struct CAMFImporter_NodeElement_Mesh : public CAMFImporter_NodeElement {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Mesh(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Mesh, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Mesh
+};
 
 /// \struct CAMFImporter_NodeElement_Vertex
 /// Structure that define vertex node.
-struct CAMFImporter_NodeElement_Vertex : public CAMFImporter_NodeElement
-{
-	/// \fn CAMFImporter_NodeElement_Vertex(CAMFImporter_NodeElement* pParent)
+struct CAMFImporter_NodeElement_Vertex : public CAMFImporter_NodeElement {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Vertex(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Vertex, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Vertex
+};
 
 /// \struct CAMFImporter_NodeElement_Edge
 /// Structure that define edge node.
-struct CAMFImporter_NodeElement_Edge : public CAMFImporter_NodeElement
-{
-	/// \fn CAMFImporter_NodeElement_Edge(CAMFImporter_NodeElement* pParent)
+struct CAMFImporter_NodeElement_Edge : public CAMFImporter_NodeElement {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Edge(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Edge, pParent)
 	{}
 
-};// struct CAMFImporter_NodeElement_Vertex
+};
 
 /// \struct CAMFImporter_NodeElement_Vertices
 /// Structure that define vertices node.
-struct CAMFImporter_NodeElement_Vertices : public CAMFImporter_NodeElement
-{
-	/// \fn CAMFImporter_NodeElement_Vertices(CAMFImporter_NodeElement* pParent)
+struct CAMFImporter_NodeElement_Vertices : public CAMFImporter_NodeElement {
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Vertices(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Vertices, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Vertices
+};
 
 /// \struct CAMFImporter_NodeElement_Volume
 /// Structure that define volume node.
-struct CAMFImporter_NodeElement_Volume : public CAMFImporter_NodeElement
-{
-	/****************** Variables ******************/
-
+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.
 
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_Volume(CAMFImporter_NodeElement* pParent)
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Volume(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Volume, pParent)
 	{}
-
-};// struct CAMFImporter_NodeElement_Volume
+};
 
 /// \struct CAMFImporter_NodeElement_Coordinates
 /// Structure that define coordinates node.
 struct CAMFImporter_NodeElement_Coordinates : public CAMFImporter_NodeElement
 {
-	/****************** Variables ******************/
-
 	aiVector3D Coordinate;///< Coordinate.
 
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_Coordinates(CAMFImporter_NodeElement* pParent)
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Coordinates(CAMFImporter_NodeElement* pParent)
 		: CAMFImporter_NodeElement(ENET_Coordinates, pParent)
 	{}
 
-};// struct CAMFImporter_NodeElement_Coordinates
+};
 
 /// \struct CAMFImporter_NodeElement_TexMap
 /// Structure that define texture coordinates node.
-struct CAMFImporter_NodeElement_TexMap : public CAMFImporter_NodeElement
-{
-	/****************** Variables ******************/
-
+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.
 
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_TexMap(CAMFImporter_NodeElement* pParent)
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_TexMap(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_TexMap, pParent)
-	{}
-
-};// struct CAMFImporter_NodeElement_TexMap
+	: CAMFImporter_NodeElement(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
-{
-	/****************** Variables ******************/
-
+struct CAMFImporter_NodeElement_Triangle : public CAMFImporter_NodeElement {
 	size_t V[3];///< Triangle vertices.
 
-	/****************** Functions ******************/
-
-	/// \fn CAMFImporter_NodeElement_Triangle(CAMFImporter_NodeElement* pParent)
 	/// Constructor.
 	/// \param [in] pParent - pointer to parent node.
 	CAMFImporter_NodeElement_Triangle(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Triangle, pParent)
-	{}
-
-};// struct CAMFImporter_NodeElement_Triangle
+	: CAMFImporter_NodeElement(ENET_Triangle, pParent) {
+        // empty
+    }
+};
 
 /// Structure that define texture node.
 struct CAMFImporter_NodeElement_Texture : public CAMFImporter_NodeElement {
@@ -396,6 +335,6 @@ struct CAMFImporter_NodeElement_Texture : public CAMFImporter_NodeElement {
     , Tiled( false ){
         // empty
     }
-};// struct CAMFImporter_NodeElement_Texture
+};
 
 #endif // INCLUDED_AI_AMF_IMPORTER_NODE_H

+ 15 - 15
code/AMFImporter_Postprocess.cpp → code/AMF/AMFImporter_Postprocess.cpp

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

+ 25 - 25
code/ASELoader.cpp → code/ASE/ASELoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -53,7 +53,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ASELoader.h"
 #include <assimp/StringComparison.h>
 #include <assimp/SkeletonMeshBuilder.h>
-#include "TargetAnimation.h"
+#include "Common/TargetAnimation.h"
+
 #include <assimp/Importer.hpp>
 #include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
@@ -88,23 +89,25 @@ ASEImporter::ASEImporter()
 , mBuffer()
 , pcScene()
 , configRecomputeNormals()
-, noSkeletonMesh()
-{}
+, noSkeletonMesh() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-ASEImporter::~ASEImporter()
-{}
+ASEImporter::~ASEImporter() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
-{
+bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const {
     // check file extension
     const std::string extension = GetExtension(pFile);
 
-    if( extension == "ase" || extension == "ask")
+    if (extension == "ase" || extension == "ask") {
         return true;
+    }
 
     if ((!extension.length() || cs) && pIOHandler) {
         const char* tokens[] = {"*3dsmax_asciiexport"};
@@ -115,15 +118,13 @@ bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
 
 // ------------------------------------------------------------------------------------------------
 // Loader meta information
-const aiImporterDesc* ASEImporter::GetInfo () const
-{
+const aiImporterDesc* ASEImporter::GetInfo () const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup configuration options
-void ASEImporter::SetupProperties(const Importer* pImp)
-{
+void ASEImporter::SetupProperties(const Importer* pImp) {
     configRecomputeNormals = (pImp->GetPropertyInteger(
         AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false);
 
@@ -133,12 +134,11 @@ void ASEImporter::SetupProperties(const Importer* pImp)
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 void ASEImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+    aiScene* pScene, IOSystem* pIOHandler) {
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
 
     // Check whether we can read from the file
-    if( file.get() == NULL) {
+    if( file.get() == nullptr) {
         throw DeadlyImportError( "Failed to open ASE file " + pFile + ".");
     }
 
@@ -200,7 +200,7 @@ void ASEImporter::InternReadFile( const std::string& pFile,
             ConvertMeshes(*i,avOutMeshes);
         }
         if (tookNormals)    {
-            DefaultLogger::get()->debug("ASE: Taking normals from the file. Use "
+            ASSIMP_LOG_DEBUG("ASE: Taking normals from the file. Use "
                 "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you "
                 "experience problems");
         }
@@ -297,15 +297,15 @@ void ASEImporter::BuildAnimations(const std::vector<BaseNode*>& nodes)
 
         // TODO: Implement Bezier & TCB support
         if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) {
-            DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
+            ASSIMP_LOG_WARN("ASE: Position controller uses Bezier/TCB keys. "
                 "This is not supported.");
         }
         if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) {
-            DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. "
+            ASSIMP_LOG_WARN("ASE: Rotation controller uses Bezier/TCB keys. "
                 "This is not supported.");
         }
         if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK)  {
-            DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
+            ASSIMP_LOG_WARN("ASE: Position controller uses Bezier/TCB keys. "
                 "This is not supported.");
         }
 
@@ -624,7 +624,7 @@ void ASEImporter::AddNodes (const std::vector<BaseNode*>& nodes,
             node->mNumChildren++;
 
             // What we did is so great, it is at least worth a debug message
-            DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")");
+            ASSIMP_LOG_DEBUG("ASE: Generating separate target node ("+snode->mName+")");
         }
     }
 
@@ -947,7 +947,7 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMesh
     // validate the material index of the mesh
     if (mesh.iMaterialIndex >= mParser->m_vMaterials.size())    {
         mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1;
-        DefaultLogger::get()->warn("Material index is out of range");
+        ASSIMP_LOG_WARN("Material index is out of range");
     }
 
     // If the material the mesh is assigned to is consisting of submeshes, split it
@@ -957,11 +957,11 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMesh
 
         std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[vSubMaterials.size()];
 
-        // build a list of all faces per submaterial
+        // build a list of all faces per sub-material
         for (unsigned int i = 0; i < mesh.mFaces.size();++i)    {
             // check range
             if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) {
-                DefaultLogger::get()->warn("Submaterial index is out of range");
+                ASSIMP_LOG_WARN("Submaterial index is out of range");
 
                 // use the last material instead
                 aiSplit[vSubMaterials.size()-1].push_back(i);
@@ -1299,7 +1299,7 @@ void ASEImporter::BuildMaterialIndices()
         }
     }
 
-    // Dekete our temporary array
+    // Delete our temporary array
     delete[] pcIntMaterials;
 }
 

+ 1 - 2
code/ASELoader.h → code/ASE/ASELoader.h

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

+ 17 - 16
code/ASEParser.cpp → code/ASE/ASEParser.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -45,20 +45,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of the ASE parser class
  */
 
-
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 // internal headers
-#include "TextureTransform.h"
+#include "PostProcessing/TextureTransform.h"
 #include "ASELoader.h"
+
 #include <assimp/fast_atof.h>
 #include <assimp/DefaultLogger.hpp>
 
 using namespace Assimp;
 using namespace Assimp::ASE;
 
-
 // ------------------------------------------------------------------------------------------------
 // Begin an ASE parsing function
 
@@ -151,7 +150,7 @@ void Parser::LogWarning(const char* szWarn)
 #endif
 
     // output the warning to the logger ...
-    DefaultLogger::get()->warn(szTemp);
+    ASSIMP_LOG_WARN(szTemp);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -167,7 +166,7 @@ void Parser::LogInfo(const char* szWarn)
 #endif
 
     // output the information to the logger ...
-    DefaultLogger::get()->info(szTemp);
+    ASSIMP_LOG_INFO(szTemp);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -758,7 +757,7 @@ void Parser::ParseLV3MapBlock(Texture& map)
                     SkipToNextToken();
                 if (temp != "Bitmap" && temp != "Normal Bump")
                 {
-                    DefaultLogger::get()->warn("ASE: Skipping unknown map type: " + temp);
+                    ASSIMP_LOG_WARN_F("ASE: Skipping unknown map type: ", temp);
                     parsePath = false;
                 }
                 continue;
@@ -773,7 +772,7 @@ void Parser::ParseLV3MapBlock(Texture& map)
                 {
                     // Files with 'None' as map name are produced by
                     // an Maja to ASE exporter which name I forgot ..
-                    DefaultLogger::get()->warn("ASE: Skipping invalid map entry");
+                    ASSIMP_LOG_WARN("ASE: Skipping invalid map entry");
                     map.mMapName = "";
                 }
 
@@ -1072,7 +1071,7 @@ void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh)
                         ( mesh.mType != BaseNode::Light  || ((ASE::Light&)mesh).mLightType   != ASE::Light::TARGET))
                     {
 
-                        DefaultLogger::get()->error("ASE: Found target animation channel "
+                        ASSIMP_LOG_ERROR("ASE: Found target animation channel "
                             "but the node is neither a camera nor a spot light");
                         anim = NULL;
                     }
@@ -1098,7 +1097,7 @@ void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh)
                 if (!anim || anim == &mesh.mTargetAnim)
                 {
                     // Target animation channels may have no rotation channels
-                    DefaultLogger::get()->error("ASE: Ignoring scaling channel in target animation");
+                    ASSIMP_LOG_ERROR("ASE: Ignoring scaling channel in target animation");
                     SkipSection();
                 }
                 else ParseLV3ScaleAnimationBlock(*anim);
@@ -1112,7 +1111,7 @@ void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh)
                 if (!anim || anim == &mesh.mTargetAnim)
                 {
                     // Target animation channels may have no rotation channels
-                    DefaultLogger::get()->error("ASE: Ignoring rotation channel in target animation");
+                    ASSIMP_LOG_ERROR("ASE: Ignoring rotation channel in target animation");
                     SkipSection();
                 }
                 else ParseLV3RotAnimationBlock(*anim);
@@ -1295,12 +1294,14 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode& mesh)
                     {
                         mode = 2;
                     }
-                    else DefaultLogger::get()->error("ASE: Ignoring target transform, "
-                        "this is no spot light or target camera");
+                    else {
+                        ASSIMP_LOG_ERROR("ASE: Ignoring target transform, "
+                            "this is no spot light or target camera");
+                    }
                 }
                 else
                 {
-                    DefaultLogger::get()->error("ASE: Unknown node transformation: " + temp);
+                    ASSIMP_LOG_ERROR("ASE: Unknown node transformation: " + temp);
                     // mode = 0
                 }
                 continue;
@@ -1916,7 +1917,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh)
                 else if (index == face.mIndices[2])
                     index = 2;
                 else    {
-                    DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_VERTEXNORMAL section");
+                    ASSIMP_LOG_ERROR("ASE: Invalid vertex index in MESH_VERTEXNORMAL section");
                     continue;
                 }
                 // We'll renormalize later
@@ -1928,7 +1929,7 @@ void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh)
                 ParseLV4MeshFloatTriple(&vNormal.x,faceIdx);
 
                 if (faceIdx >= sMesh.mFaces.size()) {
-                    DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_FACENORMAL section");
+                    ASSIMP_LOG_ERROR("ASE: Invalid vertex index in MESH_FACENORMAL section");
                     continue;
                 }
 

+ 54 - 75
code/ASEParser.h → code/ASE/ASEParser.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/qnan.h>
 
 // ASE is quite similar to 3ds. We can reuse some structures
-#include "3DSLoader.h"
+#include "3DS/3DSLoader.h"
 
 namespace Assimp    {
 namespace ASE   {
@@ -71,21 +71,20 @@ struct Material : public D3DS::Material
     //! Default constructor has been deleted
     Material() = delete;
 
-
     //! Constructor with explicit name
     explicit Material(const std::string &name)
     : D3DS::Material(name)
     , pcInstance(NULL)
-    , bNeed (false)
-    {}
-
+    , bNeed (false) {
+        // empty
+    }
 
     Material(const Material &other)            = default;
     Material &operator=(const Material &other) = default;
 
 
     //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
-    Material(Material &&other)
+    Material(Material &&other) AI_NO_EXCEPT
     : D3DS::Material(std::move(other))
     , avSubMaterials(std::move(other.avSubMaterials))
     , pcInstance(std::move(other.pcInstance))
@@ -95,7 +94,7 @@ struct Material : public D3DS::Material
     }
 
 
-    Material &operator=(Material &&other) {
+    Material &operator=(Material &&other) AI_NO_EXCEPT {
         if (this == &other) {
             return *this;
         }
@@ -127,19 +126,12 @@ struct Material : public D3DS::Material
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file face */
-struct Face : public FaceWithSmoothingGroup
-{
+struct Face : public FaceWithSmoothingGroup {
     //! Default constructor. Initializes everything with 0
-    Face()
-    {
-        mColorIndices[0] = mColorIndices[1] = mColorIndices[2] = 0;
-        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
-        {
-            amUVIndices[i][0] = amUVIndices[i][1] = amUVIndices[i][2] = 0;
-        }
-
-        iMaterial = DEFAULT_MATINDEX;
-        iFace = 0;
+    Face() AI_NO_EXCEPT
+    : iMaterial(DEFAULT_MATINDEX)
+    , iFace(0) {
+        // empty
     }
 
     //! special value to indicate that no material index has
@@ -147,8 +139,6 @@ struct Face : public FaceWithSmoothingGroup
     //! will replace this value later.
     static const unsigned int DEFAULT_MATINDEX = 0xFFFFFFFF;
 
-
-
     //! Indices into each list of texture coordinates
     unsigned int amUVIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS][3];
 
@@ -166,15 +156,15 @@ struct Face : public FaceWithSmoothingGroup
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file bone */
-struct Bone
-{
+struct Bone {
     //! Constructor
     Bone() = delete;
 
     //! Construction from an existing name
     explicit Bone( const std::string& name)
-        :   mName   (name)
-    {}
+    : mName(name) {
+        // empty
+    }
 
     //! Name of the bone
     std::string mName;
@@ -182,33 +172,27 @@ struct Bone
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file bone vertex */
-struct BoneVertex
-{
+struct BoneVertex {
     //! Bone and corresponding vertex weight.
     //! -1 for unrequired bones ....
     std::vector<std::pair<int,float> > mBoneWeights;
-
-    //! Position of the bone vertex.
-    //! MUST be identical to the vertex position
-    //aiVector3D mPosition;
 };
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file animation */
-struct Animation
-{
-    enum Type
-    {
+struct Animation {
+    enum Type {
         TRACK   = 0x0,
         BEZIER  = 0x1,
         TCB     = 0x2
     } mRotationType, mScalingType, mPositionType;
 
-    Animation()
-        :   mRotationType   (TRACK)
-        ,   mScalingType    (TRACK)
-        ,   mPositionType   (TRACK)
-    {}
+    Animation() AI_NO_EXCEPT
+    :   mRotationType   (TRACK)
+    ,   mScalingType    (TRACK)
+    ,   mPositionType   (TRACK) {
+        // empty
+    }
 
     //! List of track rotation keyframes
     std::vector< aiQuatKey > akeyRotations;
@@ -218,19 +202,16 @@ struct Animation
 
     //! List of track scaling keyframes
     std::vector< aiVectorKey > akeyScaling;
-
 };
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent the inheritance information of an ASE node */
-struct InheritanceInfo
-{
+struct InheritanceInfo {
     //! Default constructor
-    InheritanceInfo()
-    {
-        // set the inheritance flag for all axes by default to true
-        for (unsigned int i = 0; i < 3;++i)
+    InheritanceInfo() AI_NO_EXCEPT {
+        for ( size_t i=0; i<3; ++i ) {
             abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true;
+        }
     }
 
     //! Inherit the parent's position?, axis order is x,y,z
@@ -245,23 +226,24 @@ struct InheritanceInfo
 
 // ---------------------------------------------------------------------------
 /** Represents an ASE file node. Base class for mesh, light and cameras */
-struct BaseNode
-{
-    enum Type {Light, Camera, Mesh, Dummy} mType;
-
+struct BaseNode {
+    enum Type {
+        Light, 
+        Camera, 
+        Mesh, 
+        Dummy
+    } mType;
 
     //! Construction from an existing name
     BaseNode(Type _mType, const std::string &name)
     : mType         (_mType)
     , mName         (name)
-    , mProcessed    (false)
-    {
+    , mProcessed    (false) {
         // Set mTargetPosition to qnan
         const ai_real qnan = get_qnan();
         mTargetPosition.x = qnan;
     }
 
-
     //! Name of the mesh
     std::string mName;
 
@@ -291,24 +273,23 @@ struct BaseNode
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file mesh */
-struct Mesh : public MeshWithSmoothingGroups<ASE::Face>, public BaseNode
-{
+struct Mesh : public MeshWithSmoothingGroups<ASE::Face>, public BaseNode {
     //! Default constructor has been deleted
     Mesh() = delete;
 
-
     //! Construction from an existing name
     explicit Mesh(const std::string &name)
-    : BaseNode  (BaseNode::Mesh, name)
+    : BaseNode( BaseNode::Mesh, name )
+    , mVertexColors()
+    , mBoneVertices()
+    , mBones()
     , iMaterialIndex(Face::DEFAULT_MATINDEX)
-    , bSkip     (false)
-    {
-        // use 2 texture vertex components by default
-        for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+    , bSkip     (false) {
+        for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
             this->mNumUVComponents[c] = 2;
+        }
     }
 
-
     //! List of all texture coordinate sets
     std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 
@@ -396,12 +377,11 @@ struct Camera : public BaseNode
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE helper object (dummy) */
-struct Dummy : public BaseNode
-{
+struct Dummy : public BaseNode {
     //! Constructor
-    Dummy()
-        : BaseNode  (BaseNode::Dummy, "DUMMY")
-    {
+    Dummy() AI_NO_EXCEPT
+    : BaseNode  (BaseNode::Dummy, "DUMMY") {
+        // empty
     }
 };
 
@@ -416,18 +396,17 @@ struct Dummy : public BaseNode
 // -------------------------------------------------------------------------------
 /** \brief Class to parse ASE files
  */
-class Parser
-{
-
+class Parser {
 private:
-
-    Parser() {}
+    Parser() AI_NO_EXCEPT {
+        // empty
+    }
 
 public:
 
     // -------------------------------------------------------------------
     //! Construct a parser from a given input file which is
-    //! guaranted to be terminated with zero.
+    //! guaranteed to be terminated with zero.
     //! @param szFile Input file
     //! @param fileFormatDefault Assumed file format version. If the
     //!   file format is specified in the file the new value replaces

+ 846 - 0
code/Assbin/AssbinExporter.cpp

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

+ 9 - 2
code/AssbinExporter.h → code/Assbin/AssbinExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -46,6 +46,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_ASSBINEXPORTER_H_INC
 #define AI_ASSBINEXPORTER_H_INC
 
+#include <assimp/defs.h>
+
 // nothing really needed here - reserved for future use like properties
+namespace Assimp {
+
+void ASSIMP_API ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/);
+
+}
 
-#endif
+#endif // AI_ASSBINEXPORTER_H_INC

+ 182 - 190
code/AssbinLoader.cpp → code/Assbin/AssbinLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -50,13 +50,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 
 // internal headers
-#include "AssbinLoader.h"
-#include "assbin_chunks.h"
+#include "Assbin/AssbinLoader.h"
+#include "Common/assbin_chunks.h"
 #include <assimp/MemoryIOWrapper.h>
 #include <assimp/mesh.h>
 #include <assimp/anim.h>
 #include <assimp/scene.h>
 #include <assimp/importerdesc.h>
+#include <memory>
 
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
 #   include <zlib.h>
@@ -67,7 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 
 static const aiImporterDesc desc = {
-    ".assbin Importer",
+    "Assimp Binary Importer",
     "Gargaj / Conspiracy",
     "",
     "",
@@ -79,16 +80,17 @@ static const aiImporterDesc desc = {
     "assbin"
 };
 
-const aiImporterDesc* AssbinImporter::GetInfo() const
-{
+// -----------------------------------------------------------------------------------
+const aiImporterDesc* AssbinImporter::GetInfo() const {
     return &desc;
 }
 
-bool AssbinImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/ ) const
-{
+// -----------------------------------------------------------------------------------
+bool AssbinImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
     IOStream * in = pIOHandler->Open(pFile);
-    if (!in)
+    if (nullptr == in) {
         return false;
+    }
 
     char s[32];
     in->Read( s, sizeof(char), 32 );
@@ -98,17 +100,19 @@ bool AssbinImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bo
     return strncmp( s, "ASSIMP.binary-dump.", 19 ) == 0;
 }
 
+// -----------------------------------------------------------------------------------
 template <typename T>
-T Read(IOStream * stream)
-{
+T Read(IOStream * stream) {
     T t;
-    stream->Read( &t, sizeof(T), 1 );
+    size_t res = stream->Read( &t, sizeof(T), 1 );
+    if(res != 1)
+        throw DeadlyImportError("Unexpected EOF");
     return t;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiVector3D Read<aiVector3D>(IOStream * stream)
-{
+aiVector3D Read<aiVector3D>(IOStream * stream) {
     aiVector3D v;
     v.x = Read<float>(stream);
     v.y = Read<float>(stream);
@@ -116,9 +120,9 @@ aiVector3D Read<aiVector3D>(IOStream * stream)
     return v;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiColor4D Read<aiColor4D>(IOStream * stream)
-{
+aiColor4D Read<aiColor4D>(IOStream * stream) {
     aiColor4D c;
     c.r = Read<float>(stream);
     c.g = Read<float>(stream);
@@ -127,9 +131,9 @@ aiColor4D Read<aiColor4D>(IOStream * stream)
     return c;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiQuaternion Read<aiQuaternion>(IOStream * stream)
-{
+aiQuaternion Read<aiQuaternion>(IOStream * stream) {
     aiQuaternion v;
     v.w = Read<float>(stream);
     v.x = Read<float>(stream);
@@ -138,28 +142,29 @@ aiQuaternion Read<aiQuaternion>(IOStream * stream)
     return v;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiString Read<aiString>(IOStream * stream)
-{
+aiString Read<aiString>(IOStream * stream) {
     aiString s;
     stream->Read(&s.length,4,1);
-    stream->Read(s.data,s.length,1);
+    if(s.length)
+        stream->Read(s.data,s.length,1);
     s.data[s.length] = 0;
     return s;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiVertexWeight Read<aiVertexWeight>(IOStream * stream)
-{
+aiVertexWeight Read<aiVertexWeight>(IOStream * stream) {
     aiVertexWeight w;
     w.mVertexId = Read<unsigned int>(stream);
     w.mWeight = Read<float>(stream);
     return w;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream)
-{
+aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream) {
     aiMatrix4x4 m;
     for (unsigned int i = 0; i < 4;++i) {
         for (unsigned int i2 = 0; i2 < 4;++i2) {
@@ -169,76 +174,85 @@ aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream)
     return m;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiVectorKey Read<aiVectorKey>(IOStream * stream)
-{
+aiVectorKey Read<aiVectorKey>(IOStream * stream) {
     aiVectorKey v;
     v.mTime = Read<double>(stream);
     v.mValue = Read<aiVector3D>(stream);
     return v;
 }
 
+// -----------------------------------------------------------------------------------
 template <>
-aiQuatKey Read<aiQuatKey>(IOStream * stream)
-{
+aiQuatKey Read<aiQuatKey>(IOStream * stream) {
     aiQuatKey v;
     v.mTime = Read<double>(stream);
     v.mValue = Read<aiQuaternion>(stream);
     return v;
 }
 
+// -----------------------------------------------------------------------------------
 template <typename T>
-void ReadArray(IOStream * stream, T * out, unsigned int size)
-{
-    for (unsigned int i=0; i<size; i++) out[i] = Read<T>(stream);
+void ReadArray( IOStream *stream, T * out, unsigned int size) {
+    ai_assert( nullptr != stream );
+    ai_assert( nullptr != out );
+
+    for (unsigned int i=0; i<size; i++) {
+        out[i] = Read<T>(stream);
+    }
 }
 
-template <typename T> void ReadBounds( IOStream * stream, T* /*p*/, unsigned int n )
-{
+// -----------------------------------------------------------------------------------
+template <typename T>
+void ReadBounds( IOStream * stream, T* /*p*/, unsigned int n ) {
     // not sure what to do here, the data isn't really useful.
     stream->Seek( sizeof(T) * n, aiOrigin_CUR );
 }
 
-void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* parent ) {
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AINODE);
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** onode, aiNode* parent ) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODE)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
-    *node = new aiNode();
+    std::unique_ptr<aiNode> node(new aiNode());
 
-    (*node)->mName = Read<aiString>(stream);
-    (*node)->mTransformation = Read<aiMatrix4x4>(stream);
-    (*node)->mNumChildren = Read<unsigned int>(stream);
-    (*node)->mNumMeshes = Read<unsigned int>(stream);
+    node->mName = Read<aiString>(stream);
+    node->mTransformation = Read<aiMatrix4x4>(stream);
+    unsigned numChildren = Read<unsigned int>(stream);
+    unsigned numMeshes = Read<unsigned int>(stream);
 	unsigned int nb_metadata = Read<unsigned int>(stream);
 
     if(parent) {
-        (*node)->mParent = parent;
+        node->mParent = parent;
     }
 
-    if ((*node)->mNumMeshes) {
-        (*node)->mMeshes = new unsigned int[(*node)->mNumMeshes];
-        for (unsigned int i = 0; i < (*node)->mNumMeshes; ++i) {
-            (*node)->mMeshes[i] = Read<unsigned int>(stream);
+    if (numMeshes)
+    {
+        node->mMeshes = new unsigned int[numMeshes];
+        for (unsigned int i = 0; i < numMeshes; ++i) {
+            node->mMeshes[i] = Read<unsigned int>(stream);
+            node->mNumMeshes++;
         }
     }
 
-    if ((*node)->mNumChildren) {
-        (*node)->mChildren = new aiNode*[(*node)->mNumChildren];
-        for (unsigned int i = 0; i < (*node)->mNumChildren; ++i) {
-            ReadBinaryNode( stream, &(*node)->mChildren[i], *node );
+    if (numChildren) {
+        node->mChildren = new aiNode*[numChildren];
+        for (unsigned int i = 0; i < numChildren; ++i) {
+            ReadBinaryNode( stream, &node->mChildren[i], node.get() );
+            node->mNumChildren++;
         }
     }
 
     if ( nb_metadata > 0 ) {
-        (*node)->mMetaData = aiMetadata::Alloc(nb_metadata);
+        node->mMetaData = aiMetadata::Alloc(nb_metadata);
         for (unsigned int i = 0; i < nb_metadata; ++i) {
-            (*node)->mMetaData->mKeys[i] = Read<aiString>(stream);
-            (*node)->mMetaData->mValues[i].mType = (aiMetadataType) Read<uint16_t>(stream);
-            void* data( nullptr );
+            node->mMetaData->mKeys[i] = Read<aiString>(stream);
+            node->mMetaData->mValues[i].mType = (aiMetadataType) Read<uint16_t>(stream);
+            void* data = nullptr;
 
-            switch ((*node)->mMetaData->mValues[i].mType) {
+            switch (node->mMetaData->mValues[i].mType) {
                 case AI_BOOL:
                     data = new bool(Read<bool>(stream));
                     break;
@@ -267,17 +281,16 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* p
                     break;
             }
 
-			(*node)->mMetaData->mValues[i].mData = data;
+			node->mMetaData->mValues[i].mData = data;
 		}
 	}
+    *onode = node.release();
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b )
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AIBONE);
+void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b ) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIBONE)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     b->mName = Read<aiString>(stream);
@@ -286,23 +299,23 @@ void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b )
 
     // for the moment we write dumb min/max values for the bones, too.
     // maybe I'll add a better, hash-like solution later
-    if (shortened)
-    {
+    if (shortened) {
         ReadBounds(stream,b->mWeights,b->mNumWeights);
-    } // else write as usual
-    else
-    {
+    } else {
+        // else write as usual
         b->mWeights = new aiVertexWeight[b->mNumWeights];
         ReadArray<aiVertexWeight>(stream,b->mWeights,b->mNumWeights);
     }
 }
 
-
-void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AIMESH);
+// -----------------------------------------------------------------------------------
+static bool fitsIntoUI16(unsigned int mNumVertices) {
+    return ( mNumVertices < (1u<<16) );
+}
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMESH)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     mesh->mPrimitiveTypes = Read<unsigned int>(stream);
@@ -314,70 +327,61 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
     // first of all, write bits for all existent vertex components
     unsigned int c = Read<unsigned int>(stream);
 
-    if (c & ASSBIN_MESH_HAS_POSITIONS)
-    {
+    if (c & ASSBIN_MESH_HAS_POSITIONS) {
         if (shortened) {
             ReadBounds(stream,mesh->mVertices,mesh->mNumVertices);
-        } // else write as usual
-        else
-        {
+        }  else {
+            // else write as usual
             mesh->mVertices = new aiVector3D[mesh->mNumVertices];
             ReadArray<aiVector3D>(stream,mesh->mVertices,mesh->mNumVertices);
         }
     }
-    if (c & ASSBIN_MESH_HAS_NORMALS)
-    {
+    if (c & ASSBIN_MESH_HAS_NORMALS) {
         if (shortened) {
             ReadBounds(stream,mesh->mNormals,mesh->mNumVertices);
-        } // else write as usual
-        else
-        {
+        }  else {
+            // else write as usual
             mesh->mNormals = new aiVector3D[mesh->mNumVertices];
             ReadArray<aiVector3D>(stream,mesh->mNormals,mesh->mNumVertices);
         }
     }
-    if (c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS)
-    {
+    if (c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS) {
         if (shortened) {
             ReadBounds(stream,mesh->mTangents,mesh->mNumVertices);
             ReadBounds(stream,mesh->mBitangents,mesh->mNumVertices);
-        } // else write as usual
-        else
-        {
+        }  else {
+            // else write as usual
             mesh->mTangents = new aiVector3D[mesh->mNumVertices];
             ReadArray<aiVector3D>(stream,mesh->mTangents,mesh->mNumVertices);
             mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
             ReadArray<aiVector3D>(stream,mesh->mBitangents,mesh->mNumVertices);
         }
     }
-    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n)
-    {
-        if (!(c & ASSBIN_MESH_HAS_COLOR(n)))
+    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
+        if (!(c & ASSBIN_MESH_HAS_COLOR(n))) {
             break;
+        }
 
-        if (shortened)
-        {
+        if (shortened) {
             ReadBounds(stream,mesh->mColors[n],mesh->mNumVertices);
-        } // else write as usual
-        else
-        {
+        }  else {
+            // else write as usual
             mesh->mColors[n] = new aiColor4D[mesh->mNumVertices];
             ReadArray<aiColor4D>(stream,mesh->mColors[n],mesh->mNumVertices);
         }
     }
-    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
-    {
-        if (!(c & ASSBIN_MESH_HAS_TEXCOORD(n)))
+    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
+        if (!(c & ASSBIN_MESH_HAS_TEXCOORD(n))) {
             break;
+        }
 
         // write number of UV components
         mesh->mNumUVComponents[n] = Read<unsigned int>(stream);
 
         if (shortened) {
             ReadBounds(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
-        } // else write as usual
-        else
-        {
+        }  else {
+            // else write as usual
             mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
             ReadArray<aiVector3D>(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
         }
@@ -389,9 +393,8 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
     // using Assimp's standard hashing function.
     if (shortened) {
         Read<unsigned int>(stream);
-    }
-    else // else write as usual
-    {
+    } else  {
+        // else write as usual
         // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
         mesh->mFaces = new aiFace[mesh->mNumFaces];
         for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
@@ -402,12 +405,10 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
             f.mIndices = new unsigned int[f.mNumIndices];
 
             for (unsigned int a = 0; a < f.mNumIndices;++a) {
-                if (mesh->mNumVertices < (1u<<16))
-                {
+                // Check if unsigned  short ( 16 bit  ) are big enought for the indices
+                if ( fitsIntoUI16( mesh->mNumVertices ) ) {
                     f.mIndices[a] = Read<uint16_t>(stream);
-                }
-                else
-                {
+                } else {
                     f.mIndices[a] = Read<unsigned int>(stream);
                 }
             }
@@ -424,11 +425,10 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
     }
 }
 
-void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop)
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY);
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIALPROPERTY)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     prop->mKey = Read<aiString>(stream);
@@ -442,11 +442,9 @@ void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialPro
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat)
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIAL);
+void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIAL)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     mat->mNumAllocated = mat->mNumProperties = Read<unsigned int>(stream);
@@ -465,11 +463,9 @@ void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat)
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd)
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AINODEANIM);
+void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODEANIM)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     nd->mNodeName = Read<aiString>(stream);
@@ -493,9 +489,8 @@ void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd)
         if (shortened) {
             ReadBounds(stream,nd->mRotationKeys,nd->mNumRotationKeys);
 
-        } // else write as usual
-        else
-        {
+        }  else {
+            // else write as usual
             nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
             ReadArray<aiQuatKey>(stream,nd->mRotationKeys,nd->mNumRotationKeys);
         }
@@ -504,22 +499,18 @@ void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd)
         if (shortened) {
             ReadBounds(stream,nd->mScalingKeys,nd->mNumScalingKeys);
 
-        } // else write as usual
-        else
-        {
+        }  else {
+            // else write as usual
             nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys];
             ReadArray<aiVectorKey>(stream,nd->mScalingKeys,nd->mNumScalingKeys);
         }
     }
 }
 
-
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim )
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AIANIMATION);
+void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim ) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIANIMATION)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     anim->mName = Read<aiString> (stream);
@@ -527,8 +518,7 @@ void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim )
     anim->mTicksPerSecond = Read<double> (stream);
     anim->mNumChannels = Read<unsigned int>(stream);
 
-    if (anim->mNumChannels)
-    {
+    if (anim->mNumChannels) {
         anim->mChannels = new aiNodeAnim*[ anim->mNumChannels ];
         for (unsigned int a = 0; a < anim->mNumChannels;++a) {
             anim->mChannels[a] = new aiNodeAnim();
@@ -537,11 +527,10 @@ void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim )
     }
 }
 
-void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex)
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AITEXTURE);
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AITEXTURE)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     tex->mWidth = Read<unsigned int>(stream);
@@ -552,21 +541,17 @@ void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex)
         if (!tex->mHeight) {
             tex->pcData = new aiTexel[ tex->mWidth ];
             stream->Read(tex->pcData,1,tex->mWidth);
-        }
-        else {
+        } else {
             tex->pcData = new aiTexel[ tex->mWidth*tex->mHeight ];
             stream->Read(tex->pcData,1,tex->mWidth*tex->mHeight*4);
         }
     }
-
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l )
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AILIGHT);
+void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l ) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AILIGHT)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     l->mName = Read<aiString>(stream);
@@ -586,15 +571,12 @@ void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l )
         l->mAngleInnerCone = Read<float>(stream);
         l->mAngleOuterCone = Read<float>(stream);
     }
-
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam )
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AICAMERA);
+void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AICAMERA)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     cam->mName = Read<aiString>(stream);
@@ -607,11 +589,10 @@ void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam )
     cam->mAspect = Read<float>(stream);
 }
 
-void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
-{
-    uint32_t chunkID = Read<uint32_t>(stream);
-    (void)(chunkID);
-    ai_assert(chunkID == ASSBIN_CHUNK_AISCENE);
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene ) {
+    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AISCENE)
+        throw DeadlyImportError("Magic chunk identifiers are wrong!");
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
     scene->mFlags         = Read<unsigned int>(stream);
@@ -623,13 +604,13 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
     scene->mNumCameras    = Read<unsigned int>(stream);
 
     // Read node graph
-    scene->mRootNode = new aiNode[1];
+    //scene->mRootNode = new aiNode[1];
     ReadBinaryNode( stream, &scene->mRootNode, (aiNode*)NULL );
 
     // Read all meshes
-    if (scene->mNumMeshes)
-    {
+    if (scene->mNumMeshes) {
         scene->mMeshes = new aiMesh*[scene->mNumMeshes];
+        memset(scene->mMeshes, 0, scene->mNumMeshes*sizeof(aiMesh*));
         for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
             scene->mMeshes[i] = new aiMesh();
             ReadBinaryMesh( stream,scene->mMeshes[i]);
@@ -637,9 +618,9 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
     }
 
     // Read materials
-    if (scene->mNumMaterials)
-    {
+    if (scene->mNumMaterials) {
         scene->mMaterials = new aiMaterial*[scene->mNumMaterials];
+        memset(scene->mMaterials, 0, scene->mNumMaterials*sizeof(aiMaterial*));
         for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
             scene->mMaterials[i] = new aiMaterial();
             ReadBinaryMaterial(stream,scene->mMaterials[i]);
@@ -647,9 +628,9 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
     }
 
     // Read all animations
-    if (scene->mNumAnimations)
-    {
+    if (scene->mNumAnimations) {
         scene->mAnimations = new aiAnimation*[scene->mNumAnimations];
+        memset(scene->mAnimations, 0, scene->mNumAnimations*sizeof(aiAnimation*));
         for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
             scene->mAnimations[i] = new aiAnimation();
             ReadBinaryAnim(stream,scene->mAnimations[i]);
@@ -657,9 +638,9 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
     }
 
     // Read all textures
-    if (scene->mNumTextures)
-    {
+    if (scene->mNumTextures) {
         scene->mTextures = new aiTexture*[scene->mNumTextures];
+        memset(scene->mTextures, 0, scene->mNumTextures*sizeof(aiTexture*));
         for (unsigned int i = 0; i < scene->mNumTextures;++i) {
             scene->mTextures[i] = new aiTexture();
             ReadBinaryTexture(stream,scene->mTextures[i]);
@@ -667,9 +648,9 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
     }
 
     // Read lights
-    if (scene->mNumLights)
-    {
+    if (scene->mNumLights) {
         scene->mLights = new aiLight*[scene->mNumLights];
+        memset(scene->mLights, 0, scene->mNumLights*sizeof(aiLight*));
         for (unsigned int i = 0; i < scene->mNumLights;++i) {
             scene->mLights[i] = new aiLight();
             ReadBinaryLight(stream,scene->mLights[i]);
@@ -677,9 +658,9 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
     }
 
     // Read cameras
-    if (scene->mNumCameras)
-    {
+    if (scene->mNumCameras) {
         scene->mCameras = new aiCamera*[scene->mNumCameras];
+        memset(scene->mCameras, 0, scene->mNumCameras*sizeof(aiCamera*));
         for (unsigned int i = 0; i < scene->mNumCameras;++i) {
             scene->mCameras[i] = new aiCamera();
             ReadBinaryCamera(stream,scene->mCameras[i]);
@@ -688,16 +669,22 @@ void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
 
 }
 
-void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler )
-{
+// -----------------------------------------------------------------------------------
+void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ) {
     IOStream * stream = pIOHandler->Open(pFile,"rb");
-    if (!stream)
+    if (nullptr == stream) {
         return;
+    }
 
-    stream->Seek( 44, aiOrigin_CUR ); // signature
+    // signature
+    stream->Seek( 44, aiOrigin_CUR );
+
+    unsigned int versionMajor = Read<unsigned int>(stream);
+    unsigned int versionMinor = Read<unsigned int>(stream);
+    if (versionMinor != ASSBIN_VERSION_MINOR || versionMajor != ASSBIN_VERSION_MAJOR) {
+        throw DeadlyImportError( "Invalid version, data format not compatible!" );
+    }
 
-    /*unsigned int versionMajor =*/ Read<unsigned int>(stream);
-    /*unsigned int versionMinor =*/ Read<unsigned int>(stream);
     /*unsigned int versionRevision =*/ Read<unsigned int>(stream);
     /*unsigned int compileFlags =*/ Read<unsigned int>(stream);
 
@@ -711,17 +698,24 @@ void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
     stream->Seek( 128, aiOrigin_CUR ); // options
     stream->Seek( 64, aiOrigin_CUR ); // padding
 
-    if (compressed)
-    {
+    if (compressed) {
         uLongf uncompressedSize = Read<uint32_t>(stream);
         uLongf compressedSize = static_cast<uLongf>(stream->FileSize() - stream->Tell());
 
         unsigned char * compressedData = new unsigned char[ compressedSize ];
-        stream->Read( compressedData, 1, compressedSize );
+        size_t len = stream->Read( compressedData, 1, compressedSize );
+        ai_assert(len == compressedSize);
 
         unsigned char * uncompressedData = new unsigned char[ uncompressedSize ];
 
-        uncompress( uncompressedData, &uncompressedSize, compressedData, compressedSize );
+        int res = uncompress( uncompressedData, &uncompressedSize, compressedData, (uLong) len );
+        if(res != Z_OK)
+        {
+            delete [] uncompressedData;
+            delete [] compressedData;
+            pIOHandler->Close(stream);
+            throw DeadlyImportError("Zlib decompression failed.");
+        }
 
         MemoryIOStream io( uncompressedData, uncompressedSize );
 
@@ -729,9 +723,7 @@ void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
 
         delete[] uncompressedData;
         delete[] compressedData;
-    }
-    else
-    {
+    } else {
         ReadBinaryScene(stream,pScene);
     }
 

+ 23 - 22
code/AssbinLoader.h → code/Assbin/AssbinLoader.h

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -70,32 +70,33 @@ namespace Assimp    {
 class AssbinImporter : public BaseImporter
 {
 private:
-  bool shortened;
-  bool compressed;
+    bool shortened;
+    bool compressed;
 
 public:
-  virtual bool CanRead(
-    const std::string& pFile,
-    IOSystem* pIOHandler,
-    bool checkSig
+    virtual bool CanRead(
+        const std::string& pFile,
+        IOSystem* pIOHandler,
+        bool checkSig
     ) const;
-  virtual const aiImporterDesc* GetInfo() const;
-  virtual void InternReadFile(
+    virtual const aiImporterDesc* GetInfo() const;
+    virtual void InternReadFile(
     const std::string& pFile,
-    aiScene* pScene,
-    IOSystem* pIOHandler
+        aiScene* pScene,
+        IOSystem* pIOHandler
     );
-  void ReadBinaryScene( IOStream * stream, aiScene* pScene );
-  void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent );
-  void ReadBinaryMesh( IOStream * stream, aiMesh* mesh );
-  void ReadBinaryBone( IOStream * stream, aiBone* bone );
-  void ReadBinaryMaterial(IOStream * stream, aiMaterial* mat);
-  void ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop);
-  void ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd);
-  void ReadBinaryAnim( IOStream * stream, aiAnimation* anim );
-  void ReadBinaryTexture(IOStream * stream, aiTexture* tex);
-  void ReadBinaryLight( IOStream * stream, aiLight* l );
-  void ReadBinaryCamera( IOStream * stream, aiCamera* cam );
+    void ReadHeader();
+    void ReadBinaryScene( IOStream * stream, aiScene* pScene );
+    void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent );
+    void ReadBinaryMesh( IOStream * stream, aiMesh* mesh );
+    void ReadBinaryBone( IOStream * stream, aiBone* bone );
+    void ReadBinaryMaterial(IOStream * stream, aiMaterial* mat);
+    void ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop);
+    void ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd);
+    void ReadBinaryAnim( IOStream * stream, aiAnimation* anim );
+    void ReadBinaryTexture(IOStream * stream, aiTexture* tex);
+    void ReadBinaryLight( IOStream * stream, aiLight* l );
+    void ReadBinaryCamera( IOStream * stream, aiCamera* cam );
 };
 
 } // end of namespace Assimp

+ 0 - 823
code/AssbinExporter.cpp

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

+ 109 - 0
code/Assjson/cencode.c

@@ -0,0 +1,109 @@
+/*
+cencoder.c - c source to a base64 encoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include "cencode.h" // changed from <B64/cencode.h>
+
+const int CHARS_PER_LINE = 72;
+
+void base64_init_encodestate(base64_encodestate* state_in)
+{
+	state_in->step = step_A;
+	state_in->result = 0;
+	state_in->stepcount = 0;
+}
+
+char base64_encode_value(char value_in)
+{
+	static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	if (value_in > 63) return '=';
+	return encoding[(int)value_in];
+}
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
+{
+	const char* plainchar = plaintext_in;
+	const char* const plaintextend = plaintext_in + length_in;
+	char* codechar = code_out;
+	char result;
+	char fragment;
+	
+	result = state_in->result;
+	
+	switch (state_in->step)
+	{
+		while (1)
+		{
+	case step_A:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_A;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result = (fragment & 0x0fc) >> 2;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x003) << 4;
+	case step_B:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_B;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0f0) >> 4;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x00f) << 2;
+	case step_C:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_C;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0c0) >> 6;
+			*codechar++ = base64_encode_value(result);
+			result  = (fragment & 0x03f) >> 0;
+			*codechar++ = base64_encode_value(result);
+			
+			++(state_in->stepcount);
+			if (state_in->stepcount == CHARS_PER_LINE/4)
+			{
+				*codechar++ = '\n';
+				state_in->stepcount = 0;
+			}
+		}
+	}
+	/* control should not reach here */
+	return codechar - code_out;
+}
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
+{
+	char* codechar = code_out;
+	
+	switch (state_in->step)
+	{
+	case step_B:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		*codechar++ = '=';
+		break;
+	case step_C:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		break;
+	case step_A:
+		break;
+	}
+	*codechar++ = '\n';
+	
+	return codechar - code_out;
+}
+

+ 31 - 0
code/Assjson/cencode.h

@@ -0,0 +1,31 @@
+/*
+cencode.h - c header for a base64 encoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CENCODE_H
+#define BASE64_CENCODE_H
+
+typedef enum
+{
+	step_A, step_B, step_C
+} base64_encodestep;
+
+typedef struct
+{
+	base64_encodestep step;
+	char result;
+	int stepcount;
+} base64_encodestate;
+
+void base64_init_encodestate(base64_encodestate* state_in);
+
+char base64_encode_value(char value_in);
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
+
+#endif /* BASE64_CENCODE_H */

+ 809 - 0
code/Assjson/json_exporter.cpp

@@ -0,0 +1,809 @@
+/*
+Assimp2Json
+Copyright (c) 2011, Alexander C. Gessler
+
+Licensed under a 3-clause BSD license. See the LICENSE file for more information.
+
+*/
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
+
+#include <assimp/Importer.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/scene.h>
+
+#include <sstream>
+#include <limits>
+#include <cassert>
+#include <memory>
+
+#define CURRENT_FORMAT_VERSION 100
+
+// grab scoped_ptr from assimp to avoid a dependency on boost. 
+//#include <assimp/../../code/BoostWorkaround/boost/scoped_ptr.hpp>
+
+#include "mesh_splitter.h"
+
+extern "C" {
+    #include "cencode.h"
+}
+namespace Assimp {
+
+void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
+
+// small utility class to simplify serializing the aiScene to Json
+class JSONWriter {
+public:
+    enum {
+        Flag_DoNotIndent = 0x1,
+        Flag_WriteSpecialFloats = 0x2,
+    };
+
+    JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u)
+    : out(out)
+    , first()
+    , flags(flags) {
+        // make sure that all formatting happens using the standard, C locale and not the user's current locale
+        buff.imbue(std::locale("C"));
+    }
+
+    ~JSONWriter() {
+        Flush();
+    }
+
+    void Flush() {
+        const std::string s = buff.str();
+        out.Write(s.c_str(), s.length(), 1);
+        buff.clear();
+    }
+
+    void PushIndent() {
+        indent += '\t';
+    }
+
+    void PopIndent() {
+        indent.erase(indent.end() - 1);
+    }
+
+    void Key(const std::string& name) {
+        AddIndentation();
+        Delimit();
+        buff << '\"' + name + "\": ";
+    }
+
+    template<typename Literal>
+    void Element(const Literal& name) {
+        AddIndentation();
+        Delimit();
+
+        LiteralToString(buff, name) << '\n';
+    }
+
+    template<typename Literal>
+    void SimpleValue(const Literal& s) {
+        LiteralToString(buff, s) << '\n';
+    }
+
+    void SimpleValue(const void* buffer, size_t len) {
+        base64_encodestate s;
+        base64_init_encodestate(&s);
+
+        char* const out = new char[std::max(len * 2, static_cast<size_t>(16u))];
+        const int n = base64_encode_block(reinterpret_cast<const char*>(buffer), static_cast<int>(len), out, &s);
+        out[n + base64_encode_blockend(out + n, &s)] = '\0';
+
+        // base64 encoding may add newlines, but JSON strings may not contain 'real' newlines
+        // (only escaped ones). Remove any newlines in out.
+        for (char* cur = out; *cur; ++cur) {
+            if (*cur == '\n') {
+                *cur = ' ';
+            }
+        }
+
+        buff << '\"' << out << "\"\n";
+        delete[] out;
+    }
+
+    void StartObj(bool is_element = false) {
+        // if this appears as a plain array element, we need to insert a delimiter and we should also indent it
+        if (is_element) {
+            AddIndentation();
+            if (!first) {
+                buff << ',';
+            }
+        }
+        first = true;
+        buff << "{\n";
+        PushIndent();
+    }
+
+    void EndObj() {
+        PopIndent();
+        AddIndentation();
+        first = false;
+        buff << "}\n";
+    }
+
+    void StartArray(bool is_element = false) {
+        // if this appears as a plain array element, we need to insert a delimiter and we should also indent it
+        if (is_element) {
+            AddIndentation();
+            if (!first) {
+                buff << ',';
+            }
+        }
+        first = true;
+        buff << "[\n";
+        PushIndent();
+    }
+
+    void EndArray() {
+        PopIndent();
+        AddIndentation();
+        buff << "]\n";
+        first = false;
+    }
+
+    void AddIndentation() {
+        if (!(flags & Flag_DoNotIndent)) {
+            buff << indent;
+        }
+    }
+
+    void Delimit() {
+        if (!first) {
+            buff << ',';
+        }
+        else {
+            buff << ' ';
+            first = false;
+        }
+    }
+
+private:
+    template<typename Literal>
+    std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) {
+        stream << s;
+        return stream;
+    }
+
+    std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) {
+        std::string t;
+
+        // escape backslashes and single quotes, both would render the JSON invalid if left as is
+        t.reserve(s.length);
+        for (size_t i = 0; i < s.length; ++i) {
+
+            if (s.data[i] == '\\' || s.data[i] == '\'' || s.data[i] == '\"') {
+                t.push_back('\\');
+            }
+
+            t.push_back(s.data[i]);
+        }
+        stream << "\"";
+        stream << t;
+        stream << "\"";
+        return stream;
+    }
+
+    std::stringstream& LiteralToString(std::stringstream& stream, float f) {
+        if (!std::numeric_limits<float>::is_iec559) {
+            // on a non IEEE-754 platform, we make no assumptions about the representation or existence
+            // of special floating-point numbers. 
+            stream << f;
+            return stream;
+        }
+
+        // JSON does not support writing Inf/Nan
+        // [RFC 4672: "Numeric values that cannot be represented as sequences of digits
+        // (such as Infinity and NaN) are not permitted."]
+        // Nevertheless, many parsers will accept the special keywords Infinity, -Infinity and NaN
+        if (std::numeric_limits<float>::infinity() == fabs(f)) {
+            if (flags & Flag_WriteSpecialFloats) {
+                stream << (f < 0 ? "\"-" : "\"") + std::string("Infinity\"");
+                return stream;
+            }
+            //  we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr
+            //	std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl;
+            stream << "0.0";
+            return stream;
+        }
+        // f!=f is the most reliable test for NaNs that I know of
+        else if (f != f) {
+            if (flags & Flag_WriteSpecialFloats) {
+                stream << "\"NaN\"";
+                return stream;
+            }
+            //  we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr
+            //	std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl;
+            stream << "0.0";
+            return stream;
+        }
+
+        stream << f;
+        return stream;
+    }
+
+private:
+    Assimp::IOStream& out;
+    std::string indent, newline;
+    std::stringstream buff;
+    bool first;
+
+    unsigned int flags;
+};
+
+void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    out.Element(ai.x);
+    out.Element(ai.y);
+    out.Element(ai.z);
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    out.Element(ai.w);
+    out.Element(ai.x);
+    out.Element(ai.y);
+    out.Element(ai.z);
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    out.Element(ai.r);
+    out.Element(ai.g);
+    out.Element(ai.b);
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    for (unsigned int x = 0; x < 4; ++x) {
+        for (unsigned int y = 0; y < 4; ++y) {
+            out.Element(ai[x][y]);
+        }
+    }
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("offsetmatrix");
+    Write(out, ai.mOffsetMatrix, false);
+
+    out.Key("weights");
+    out.StartArray();
+    for (unsigned int i = 0; i < ai.mNumWeights; ++i) {
+        out.StartArray(true);
+        out.Element(ai.mWeights[i].mVertexId);
+        out.Element(ai.mWeights[i].mWeight);
+        out.EndArray();
+    }
+    out.EndArray();
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    for (unsigned int i = 0; i < ai.mNumIndices; ++i) {
+        out.Element(ai.mIndices[i]);
+    }
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("materialindex");
+    out.SimpleValue(ai.mMaterialIndex);
+
+    out.Key("primitivetypes");
+    out.SimpleValue(ai.mPrimitiveTypes);
+
+    out.Key("vertices");
+    out.StartArray();
+    for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+        out.Element(ai.mVertices[i].x);
+        out.Element(ai.mVertices[i].y);
+        out.Element(ai.mVertices[i].z);
+    }
+    out.EndArray();
+
+    if (ai.HasNormals()) {
+        out.Key("normals");
+        out.StartArray();
+        for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+            out.Element(ai.mNormals[i].x);
+            out.Element(ai.mNormals[i].y);
+            out.Element(ai.mNormals[i].z);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasTangentsAndBitangents()) {
+        out.Key("tangents");
+        out.StartArray();
+        for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+            out.Element(ai.mTangents[i].x);
+            out.Element(ai.mTangents[i].y);
+            out.Element(ai.mTangents[i].z);
+        }
+        out.EndArray();
+
+        out.Key("bitangents");
+        out.StartArray();
+        for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+            out.Element(ai.mBitangents[i].x);
+            out.Element(ai.mBitangents[i].y);
+            out.Element(ai.mBitangents[i].z);
+        }
+        out.EndArray();
+    }
+
+    if (ai.GetNumUVChannels()) {
+        out.Key("numuvcomponents");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) {
+            out.Element(ai.mNumUVComponents[n]);
+        }
+        out.EndArray();
+
+        out.Key("texturecoords");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) {
+            const unsigned int numc = ai.mNumUVComponents[n] ? ai.mNumUVComponents[n] : 2;
+
+            out.StartArray(true);
+            for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+                for (unsigned int c = 0; c < numc; ++c) {
+                    out.Element(ai.mTextureCoords[n][i][c]);
+                }
+            }
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.GetNumColorChannels()) {
+        out.Key("colors");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.GetNumColorChannels(); ++n) {
+            out.StartArray(true);
+            for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+                out.Element(ai.mColors[n][i].r);
+                out.Element(ai.mColors[n][i].g);
+                out.Element(ai.mColors[n][i].b);
+                out.Element(ai.mColors[n][i].a);
+            }
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumBones) {
+        out.Key("bones");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumBones; ++n) {
+            Write(out, *ai.mBones[n]);
+        }
+        out.EndArray();
+    }
+
+    out.Key("faces");
+    out.StartArray();
+    for (unsigned int n = 0; n < ai.mNumFaces; ++n) {
+        Write(out, ai.mFaces[n]);
+    }
+    out.EndArray();
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("transformation");
+    Write(out, ai.mTransformation, false);
+
+    if (ai.mNumMeshes) {
+        out.Key("meshes");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumMeshes; ++n) {
+            out.Element(ai.mMeshes[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumChildren) {
+        out.Key("children");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumChildren; ++n) {
+            Write(out, *ai.mChildren[n]);
+        }
+        out.EndArray();
+    }
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("properties");
+    out.StartArray();
+    for (unsigned int i = 0; i < ai.mNumProperties; ++i) {
+        const aiMaterialProperty* const prop = ai.mProperties[i];
+        out.StartObj(true);
+        out.Key("key");
+        out.SimpleValue(prop->mKey);
+        out.Key("semantic");
+        out.SimpleValue(prop->mSemantic);
+        out.Key("index");
+        out.SimpleValue(prop->mIndex);
+
+        out.Key("type");
+        out.SimpleValue(prop->mType);
+
+        out.Key("value");
+        switch (prop->mType) {
+            case aiPTI_Float:
+                if (prop->mDataLength / sizeof(float) > 1) {
+                    out.StartArray();
+                    for (unsigned int i = 0; i < prop->mDataLength / sizeof(float); ++i) {
+                        out.Element(reinterpret_cast<float*>(prop->mData)[i]);
+                    }
+                    out.EndArray();
+                }
+                else {
+                    out.SimpleValue(*reinterpret_cast<float*>(prop->mData));
+                }
+                break;
+
+            case aiPTI_Integer:
+                if (prop->mDataLength / sizeof(int) > 1) {
+                    out.StartArray();
+                    for (unsigned int i = 0; i < prop->mDataLength / sizeof(int); ++i) {
+                        out.Element(reinterpret_cast<int*>(prop->mData)[i]);
+                    }
+                    out.EndArray();
+                } else {
+                    out.SimpleValue(*reinterpret_cast<int*>(prop->mData));
+                }
+                break;
+
+            case aiPTI_String:
+                {
+                    aiString s;
+                    aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
+                    out.SimpleValue(s);
+                }
+                break;
+            case aiPTI_Buffer:
+                {
+                    // binary data is written as series of hex-encoded octets
+                    out.SimpleValue(prop->mData, prop->mDataLength);
+                }
+                break;
+            default:
+                assert(false);
+        }
+
+        out.EndObj();
+    }
+
+    out.EndArray();
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("width");
+    out.SimpleValue(ai.mWidth);
+
+    out.Key("height");
+    out.SimpleValue(ai.mHeight);
+
+    out.Key("formathint");
+    out.SimpleValue(aiString(ai.achFormatHint));
+
+    out.Key("data");
+    if (!ai.mHeight) {
+        out.SimpleValue(ai.pcData, ai.mWidth);
+    }
+    else {
+        out.StartArray();
+        for (unsigned int y = 0; y < ai.mHeight; ++y) {
+            out.StartArray(true);
+            for (unsigned int x = 0; x < ai.mWidth; ++x) {
+                const aiTexel& tx = ai.pcData[y*ai.mWidth + x];
+                out.StartArray(true);
+                out.Element(static_cast<unsigned int>(tx.r));
+                out.Element(static_cast<unsigned int>(tx.g));
+                out.Element(static_cast<unsigned int>(tx.b));
+                out.Element(static_cast<unsigned int>(tx.a));
+                out.EndArray();
+            }
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("type");
+    out.SimpleValue(ai.mType);
+
+    if (ai.mType == aiLightSource_SPOT || ai.mType == aiLightSource_UNDEFINED) {
+        out.Key("angleinnercone");
+        out.SimpleValue(ai.mAngleInnerCone);
+
+        out.Key("angleoutercone");
+        out.SimpleValue(ai.mAngleOuterCone);
+    }
+
+    out.Key("attenuationconstant");
+    out.SimpleValue(ai.mAttenuationConstant);
+
+    out.Key("attenuationlinear");
+    out.SimpleValue(ai.mAttenuationLinear);
+
+    out.Key("attenuationquadratic");
+    out.SimpleValue(ai.mAttenuationQuadratic);
+
+    out.Key("diffusecolor");
+    Write(out, ai.mColorDiffuse, false);
+
+    out.Key("specularcolor");
+    Write(out, ai.mColorSpecular, false);
+
+    out.Key("ambientcolor");
+    Write(out, ai.mColorAmbient, false);
+
+    if (ai.mType != aiLightSource_POINT) {
+        out.Key("direction");
+        Write(out, ai.mDirection, false);
+
+    }
+
+    if (ai.mType != aiLightSource_DIRECTIONAL) {
+        out.Key("position");
+        Write(out, ai.mPosition, false);
+    }
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mNodeName);
+
+    out.Key("prestate");
+    out.SimpleValue(ai.mPreState);
+
+    out.Key("poststate");
+    out.SimpleValue(ai.mPostState);
+
+    if (ai.mNumPositionKeys) {
+        out.Key("positionkeys");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) {
+            const aiVectorKey& pos = ai.mPositionKeys[n];
+            out.StartArray(true);
+            out.Element(pos.mTime);
+            Write(out, pos.mValue);
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumRotationKeys) {
+        out.Key("rotationkeys");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) {
+            const aiQuatKey& rot = ai.mRotationKeys[n];
+            out.StartArray(true);
+            out.Element(rot.mTime);
+            Write(out, rot.mValue);
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumScalingKeys) {
+        out.Key("scalingkeys");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) {
+            const aiVectorKey& scl = ai.mScalingKeys[n];
+            out.StartArray(true);
+            out.Element(scl.mTime);
+            Write(out, scl.mValue);
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("tickspersecond");
+    out.SimpleValue(ai.mTicksPerSecond);
+
+    out.Key("duration");
+    out.SimpleValue(ai.mDuration);
+
+    out.Key("channels");
+    out.StartArray();
+    for (unsigned int n = 0; n < ai.mNumChannels; ++n) {
+        Write(out, *ai.mChannels[n]);
+    }
+    out.EndArray();
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("aspect");
+    out.SimpleValue(ai.mAspect);
+
+    out.Key("clipplanefar");
+    out.SimpleValue(ai.mClipPlaneFar);
+
+    out.Key("clipplanenear");
+    out.SimpleValue(ai.mClipPlaneNear);
+
+    out.Key("horizontalfov");
+    out.SimpleValue(ai.mHorizontalFOV);
+
+    out.Key("up");
+    Write(out, ai.mUp, false);
+
+    out.Key("lookat");
+    Write(out, ai.mLookAt, false);
+
+    out.EndObj();
+}
+
+void WriteFormatInfo(JSONWriter& out) {
+    out.StartObj();
+    out.Key("format");
+    out.SimpleValue("\"assimp2json\"");
+    out.Key("version");
+    out.SimpleValue(CURRENT_FORMAT_VERSION);
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiScene& ai) {
+    out.StartObj();
+
+    out.Key("__metadata__");
+    WriteFormatInfo(out);
+
+    out.Key("rootnode");
+    Write(out, *ai.mRootNode, false);
+
+    out.Key("flags");
+    out.SimpleValue(ai.mFlags);
+
+    if (ai.HasMeshes()) {
+        out.Key("meshes");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumMeshes; ++n) {
+            Write(out, *ai.mMeshes[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasMaterials()) {
+        out.Key("materials");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumMaterials; ++n) {
+            Write(out, *ai.mMaterials[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasAnimations()) {
+        out.Key("animations");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumAnimations; ++n) {
+            Write(out, *ai.mAnimations[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasLights()) {
+        out.Key("lights");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumLights; ++n) {
+            Write(out, *ai.mLights[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasCameras()) {
+        out.Key("cameras");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumCameras; ++n) {
+            Write(out, *ai.mCameras[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasTextures()) {
+        out.Key("textures");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumTextures; ++n) {
+            Write(out, *ai.mTextures[n]);
+        }
+        out.EndArray();
+    }
+    out.EndObj();
+}
+
+
+void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) {
+    std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
+    if (!str) {
+        //throw Assimp::DeadlyExportError("could not open output file");
+    }
+
+    // get a copy of the scene so we can modify it
+    aiScene* scenecopy_tmp;
+    aiCopyScene(scene, &scenecopy_tmp);
+
+    try {
+        // split meshes so they fit into a 16 bit index buffer
+        MeshSplitter splitter;
+        splitter.SetLimit(1 << 16);
+        splitter.Execute(scenecopy_tmp);
+
+        // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters
+        JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
+        Write(s, *scenecopy_tmp);
+
+    }
+    catch (...) {
+        aiFreeScene(scenecopy_tmp);
+        throw;
+    }
+    aiFreeScene(scenecopy_tmp);
+}
+
+}
+
+#endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 320 - 0
code/Assjson/mesh_splitter.cpp

@@ -0,0 +1,320 @@
+/*
+Assimp2Json
+Copyright (c) 2011, Alexander C. Gessler
+
+Licensed under a 3-clause BSD license. See the LICENSE file for more information.
+
+*/
+
+#include "mesh_splitter.h"
+
+#include <assimp/scene.h>
+
+// ----------------------------------------------------------------------------
+// Note: this is largely based on assimp's SplitLargeMeshes_Vertex process.
+// it is refactored and the coding style is slightly improved, though.
+// ----------------------------------------------------------------------------
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void MeshSplitter::Execute( aiScene* pScene) {
+	std::vector<std::pair<aiMesh*, unsigned int> > source_mesh_map;
+
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+		SplitMesh(a, pScene->mMeshes[a],source_mesh_map);
+	}
+
+	const unsigned int size = static_cast<unsigned int>(source_mesh_map.size());
+	if (size != pScene->mNumMeshes) {
+		// it seems something has been split. rebuild the mesh list
+		delete[] pScene->mMeshes;
+		pScene->mNumMeshes = size;
+		pScene->mMeshes = new aiMesh*[size]();
+
+		for (unsigned int i = 0; i < size;++i) {
+			pScene->mMeshes[i] = source_mesh_map[i].first;
+		}
+
+		// now we need to update all nodes
+		UpdateNode(pScene->mRootNode,source_mesh_map);
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) {
+	// TODO: should better use std::(multi)set for source_mesh_map.
+
+	// for every index in out list build a new entry
+	std::vector<unsigned int> aiEntries;
+	aiEntries.reserve(pcNode->mNumMeshes + 1);
+	for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)	{
+		for (unsigned int a = 0, end = static_cast<unsigned int>(source_mesh_map.size()); a < end;++a)	{
+			if (source_mesh_map[a].second == pcNode->mMeshes[i])	{
+				aiEntries.push_back(a);
+			}
+		}
+	}
+
+	// now build the new list
+	delete pcNode->mMeshes;
+	pcNode->mNumMeshes = static_cast<unsigned int>(aiEntries.size());
+	pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+
+	for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) {
+		pcNode->mMeshes[b] = aiEntries[b];
+	}
+
+	// recursively update children
+	for (unsigned int i = 0, end = pcNode->mNumChildren; i < end;++i)	{
+		UpdateNode ( pcNode->mChildren[i], source_mesh_map );
+	}
+	return;
+}
+
+#define WAS_NOT_COPIED 0xffffffff
+
+typedef std::pair <unsigned int,float> PerVertexWeight;
+typedef std::vector	<PerVertexWeight> VertexWeightTable;
+
+// ------------------------------------------------------------------------------------------------
+VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) {
+	if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
+		return nullptr;
+	}
+
+	VertexWeightTable* const avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
+	for (unsigned int i = 0; i < pMesh->mNumBones;++i)	{
+
+		aiBone* bone = pMesh->mBones[i];
+		for (unsigned int a = 0; a < bone->mNumWeights;++a)	{
+			const aiVertexWeight& weight = bone->mWeights[a];
+			avPerVertexWeights[weight.mVertexId].push_back( std::make_pair(i,weight.mWeight) );
+		}
+	}
+	return avPerVertexWeights;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) {
+	// TODO: should better use std::(multi)set for source_mesh_map.
+
+	if (in_mesh->mNumVertices <= LIMIT)	{
+		source_mesh_map.push_back(std::make_pair(in_mesh,a));
+		return;
+	}
+
+	// build a per-vertex weight list if necessary
+	VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(in_mesh);
+
+	// we need to split this mesh into sub meshes. Estimate submesh size
+	const unsigned int sub_meshes = (in_mesh->mNumVertices / LIMIT) + 1;
+
+	// create a std::vector<unsigned int> to remember which vertices have already 
+	// been copied and to which position (i.e. output index)
+	std::vector<unsigned int> was_copied_to;
+	was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED);
+
+	// Try to find a good estimate for the number of output faces
+	// per mesh. Add 12.5% as buffer
+	unsigned int size_estimated = in_mesh->mNumFaces / sub_meshes;
+	size_estimated += size_estimated / 8;
+
+	// now generate all submeshes
+	unsigned int base = 0;
+	while (true) {
+		const unsigned int out_vertex_index = LIMIT;
+
+		aiMesh* out_mesh = new aiMesh();			
+		out_mesh->mNumVertices = 0;
+		out_mesh->mMaterialIndex = in_mesh->mMaterialIndex;
+
+		// the name carries the adjacency information between the meshes
+		out_mesh->mName = in_mesh->mName;
+
+		typedef std::vector<aiVertexWeight> BoneWeightList;
+		if (in_mesh->HasBones())	{
+			out_mesh->mBones = new aiBone*[in_mesh->mNumBones]();
+		}
+
+		// clear the temporary helper array
+		if (base)	{
+			std::fill(was_copied_to.begin(), was_copied_to.end(), WAS_NOT_COPIED);
+		}
+
+		std::vector<aiFace> vFaces;
+
+		// reserve enough storage for most cases
+		if (in_mesh->HasPositions()) {
+			out_mesh->mVertices = new aiVector3D[out_vertex_index];
+		}
+
+		if (in_mesh->HasNormals()) {
+			out_mesh->mNormals = new aiVector3D[out_vertex_index];
+		}
+
+		if (in_mesh->HasTangentsAndBitangents())	{
+			out_mesh->mTangents = new aiVector3D[out_vertex_index];
+			out_mesh->mBitangents = new aiVector3D[out_vertex_index];
+		}
+
+		for (unsigned int c = 0; in_mesh->HasVertexColors(c);++c)	{
+			out_mesh->mColors[c] = new aiColor4D[out_vertex_index];
+		}
+
+		for (unsigned int c = 0; in_mesh->HasTextureCoords(c);++c)	{
+			out_mesh->mNumUVComponents[c] = in_mesh->mNumUVComponents[c];
+			out_mesh->mTextureCoords[c] = new aiVector3D[out_vertex_index];
+		}
+		vFaces.reserve(size_estimated);
+
+		// (we will also need to copy the array of indices)
+		while (base < in_mesh->mNumFaces) {
+			const unsigned int iNumIndices = in_mesh->mFaces[base].mNumIndices;
+
+			// doesn't catch degenerates but is quite fast
+			unsigned int iNeed = 0;
+			for (unsigned int v = 0; v < iNumIndices;++v)	{
+				unsigned int index = in_mesh->mFaces[base].mIndices[v];
+
+				// check whether we do already have this vertex
+				if (WAS_NOT_COPIED == was_copied_to[index])	{
+					iNeed++; 
+				}
+			}
+			if (out_mesh->mNumVertices + iNeed > out_vertex_index)	{
+				// don't use this face
+				break;
+			}
+
+			vFaces.push_back(aiFace());
+			aiFace& rFace = vFaces.back();
+
+			// setup face type and number of indices
+			rFace.mNumIndices = iNumIndices;
+			rFace.mIndices = new unsigned int[iNumIndices];
+
+			// need to update the output primitive types
+			switch (rFace.mNumIndices)
+			{
+			case 1:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+				break;
+			case 2:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+				break;
+			case 3:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+				break;
+			default:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+			}
+
+			// and copy the contents of the old array, offset them by current base
+			for (unsigned int v = 0; v < iNumIndices;++v) {
+				const unsigned int index = in_mesh->mFaces[base].mIndices[v];
+
+				// check whether we do already have this vertex
+				if (WAS_NOT_COPIED != was_copied_to[index]) {
+					rFace.mIndices[v] = was_copied_to[index];
+					continue;
+				}
+
+				// copy positions
+				out_mesh->mVertices[out_mesh->mNumVertices] = (in_mesh->mVertices[index]);
+
+				// copy normals
+				if (in_mesh->HasNormals()) {
+					out_mesh->mNormals[out_mesh->mNumVertices] = (in_mesh->mNormals[index]);
+				}
+
+				// copy tangents/bi-tangents
+				if (in_mesh->HasTangentsAndBitangents()) {
+					out_mesh->mTangents[out_mesh->mNumVertices] = (in_mesh->mTangents[index]);
+					out_mesh->mBitangents[out_mesh->mNumVertices] = (in_mesh->mBitangents[index]);
+				}
+
+				// texture coordinates
+				for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+					if (in_mesh->HasTextureCoords( c)) {
+						out_mesh->mTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index];
+					}
+				}
+				// vertex colors 
+				for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) {
+					if (in_mesh->HasVertexColors( c)) {
+						out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index];
+					}
+				}
+				// check whether we have bone weights assigned to this vertex
+				rFace.mIndices[v] = out_mesh->mNumVertices;
+				if (avPerVertexWeights) {
+					VertexWeightTable& table = avPerVertexWeights[ out_mesh->mNumVertices ];
+					for (VertexWeightTable::const_iterator iter = table.begin(), end = table.end(); iter != end;++iter) {
+						// allocate the bone weight array if necessary and store it in the mBones field (HACK!)
+						BoneWeightList* weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[(*iter).first]);
+						if (!weight_list) {
+							weight_list = new BoneWeightList();
+							out_mesh->mBones[(*iter).first] = reinterpret_cast<aiBone*>(weight_list);
+						}
+						weight_list->push_back(aiVertexWeight(out_mesh->mNumVertices,(*iter).second));
+					}
+				}
+
+				was_copied_to[index] = out_mesh->mNumVertices;
+				out_mesh->mNumVertices++;
+			}
+			base++;
+			if(out_mesh->mNumVertices == out_vertex_index) {
+				// break here. The face is only added if it was complete
+				break;
+			}
+		}
+
+		// check which bones we'll need to create for this submesh
+		if (in_mesh->HasBones()) {
+			aiBone** ppCurrent = out_mesh->mBones;
+			for (unsigned int k = 0; k < in_mesh->mNumBones;++k) {
+				// check whether the bone exists
+				BoneWeightList* const weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[k]);
+
+				if (weight_list) {
+					const aiBone* const bone_in = in_mesh->mBones[k];
+					aiBone* const bone_out = new aiBone();
+					*ppCurrent++ = bone_out;
+					bone_out->mName = aiString(bone_in->mName);
+					bone_out->mOffsetMatrix =bone_in->mOffsetMatrix;
+					bone_out->mNumWeights = (unsigned int)weight_list->size();
+					bone_out->mWeights = new aiVertexWeight[bone_out->mNumWeights];
+
+					// copy the vertex weights
+					::memcpy(bone_out->mWeights, &(*weight_list)[0],bone_out->mNumWeights * sizeof(aiVertexWeight));
+
+					delete weight_list;
+					out_mesh->mNumBones++;
+				}
+			}
+		}
+
+		// copy the face list to the mesh
+		out_mesh->mFaces = new aiFace[vFaces.size()];
+		out_mesh->mNumFaces = (unsigned int)vFaces.size();
+
+		for (unsigned int p = 0; p < out_mesh->mNumFaces;++p) {
+			out_mesh->mFaces[p] = vFaces[p];
+		}
+
+		// add the newly created mesh to the list
+		source_mesh_map.push_back(std::make_pair(out_mesh,a));
+
+		if (base == in_mesh->mNumFaces) {
+			break;
+		}
+	}
+
+	// delete the per-vertex weight list again
+	delete[] avPerVertexWeights;
+
+	// now delete the old mesh data
+	delete in_mesh;
+}

+ 61 - 0
code/Assjson/mesh_splitter.h

@@ -0,0 +1,61 @@
+/*
+Assimp2Json
+Copyright (c) 2011, Alexander C. Gessler
+
+Licensed under a 3-clause BSD license. See the LICENSE file for more information.
+
+*/
+
+#ifndef INCLUDED_MESH_SPLITTER
+#define INCLUDED_MESH_SPLITTER
+
+// ----------------------------------------------------------------------------
+// Note: this is largely based on assimp's SplitLargeMeshes_Vertex process.
+// it is refactored and the coding style is slightly improved, though.
+// ----------------------------------------------------------------------------
+
+#include <vector>
+
+struct aiScene;
+struct aiMesh;
+struct aiNode;
+
+// ---------------------------------------------------------------------------
+/** Splits meshes of unique vertices into meshes with no more vertices than
+ *  a given, configurable threshold value. 
+ */
+class MeshSplitter 
+{
+
+public:
+	
+	void SetLimit(unsigned int l) {
+		LIMIT = l;
+	}
+
+	unsigned int GetLimit() const {
+		return LIMIT;
+	}
+
+public:
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	 * At the moment a process is not supposed to fail.
+	 * @param pScene The imported data to work at.
+	 */
+	void Execute( aiScene* pScene);
+
+
+private:
+
+	void UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
+	void SplitMesh (unsigned int index, aiMesh* mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
+
+public:
+
+	unsigned int LIMIT;
+};
+
+#endif // INCLUDED_MESH_SPLITTER
+

+ 12 - 7
code/AssxmlExporter.cpp → code/Assxml/AssxmlExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -46,13 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
 
-#include <stdarg.h>
+#include "PostProcessing/ProcessHelper.h"
+
 #include <assimp/version.h>
-#include "ProcessHelper.h"
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
 #include <assimp/Exporter.hpp>
 
+#include <stdarg.h>
+
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
 #   include <zlib.h>
 #else
@@ -184,8 +186,13 @@ static std::string encodeXML(const std::string& data) {
 static
 void WriteDump(const aiScene* scene, IOStream* io, bool shortened) {
     time_t tt = ::time( NULL );
-    tm* p     = ::gmtime( &tt );
-    ai_assert( nullptr != p );
+#if _WIN32
+    tm* p = gmtime(&tt);
+#else
+    struct tm now;
+    tm* p = gmtime_r(&tt, &now);
+#endif
+    ai_assert(nullptr != p);
 
     // write header
     std::string header(
@@ -550,8 +557,6 @@ void WriteDump(const aiScene* scene, IOStream* io, bool shortened) {
                             mesh->mNormals[n].z);
                     }
                 }
-                else {
-                }
                 ioprintf(io,"\t\t</Normals>\n");
             }
 

+ 1 - 1
code/AssxmlExporter.h → code/Assxml/AssxmlExporter.h

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

+ 102 - 99
code/B3DImporter.cpp → code/B3D/B3DImporter.cpp

@@ -3,9 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
-
-
+Copyright (c) 2006-2019, assimp team
 
 All rights reserved.
 
@@ -49,17 +47,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
 
 // internal headers
-#include "B3DImporter.h"
-#include "TextureTransform.h"
-#include "ConvertToLHProcess.h"
+#include "B3D/B3DImporter.h"
+#include "PostProcessing/TextureTransform.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+
 #include <assimp/StringUtils.h>
-#include <memory>
 #include <assimp/IOSystem.hpp>
 #include <assimp/anim.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/importerdesc.h>
 
+#include <memory>
+
 using namespace Assimp;
 using namespace std;
 
@@ -76,7 +76,6 @@ static const aiImporterDesc desc = {
     "b3d"
 };
 
-// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings
 #ifdef _MSC_VER
 #	pragma warning (disable: 4018)
 #endif
@@ -84,10 +83,8 @@ static const aiImporterDesc desc = {
 //#define DEBUG_B3D
 
 template<typename T>
-void DeleteAllBarePointers(std::vector<T>& x)
-{
-    for(auto p : x)
-    {
+void DeleteAllBarePointers(std::vector<T>& x) {
+    for(auto p : x) {
         delete p;
     }
 }
@@ -100,10 +97,14 @@ B3DImporter::~B3DImporter()
 bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{
 
     size_t pos=pFile.find_last_of( '.' );
-    if( pos==string::npos ) return false;
+    if( pos==string::npos ) {
+        return false;
+    }
 
     string ext=pFile.substr( pos+1 );
-    if( ext.size()!=3 ) return false;
+    if( ext.size()!=3 ) {
+        return false;
+    }
 
     return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D');
 }
@@ -115,30 +116,21 @@ const aiImporterDesc* B3DImporter::GetInfo () const
     return &desc;
 }
 
-#ifdef DEBUG_B3D
-    extern "C"{ void _stdcall AllocConsole(); }
-#endif
 // ------------------------------------------------------------------------------------------------
 void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){
-
-#ifdef DEBUG_B3D
-    AllocConsole();
-    freopen( "conin$","r",stdin );
-    freopen( "conout$","w",stdout );
-    freopen( "conout$","w",stderr );
-    cout<<"Hello world from the B3DImporter!"<<endl;
-#endif
-
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
 
     // Check whether we can read from the file
-    if( file.get() == NULL)
+    if( file.get() == nullptr) {
         throw DeadlyImportError( "Failed to open B3D file " + pFile + ".");
+    }
 
     // check whether the .b3d file is large enough to contain
     // at least one chunk.
     size_t fileSize = file->FileSize();
-    if( fileSize<8 ) throw DeadlyImportError( "B3D File is too small.");
+    if( fileSize<8 ) {
+        throw DeadlyImportError( "B3D File is too small.");
+    }
 
     _pos=0;
     _buf.resize( fileSize );
@@ -156,14 +148,17 @@ AI_WONT_RETURN void B3DImporter::Oops(){
 // ------------------------------------------------------------------------------------------------
 AI_WONT_RETURN void B3DImporter::Fail( string str ){
 #ifdef DEBUG_B3D
-    cout<<"Error in B3D file data: "<<str<<endl;
+    ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
 #endif
     throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str );
 }
 
 // ------------------------------------------------------------------------------------------------
 int B3DImporter::ReadByte(){
-    if( _pos<_buf.size() ) return _buf[_pos++];
+    if( _pos<_buf.size() ) {
+        return _buf[_pos++];
+    }
+    
     Fail( "EOF" );
     return 0;
 }
@@ -222,7 +217,9 @@ string B3DImporter::ReadString(){
     string str;
     while( _pos<_buf.size() ){
         char c=(char)ReadByte();
-        if( !c ) return str;
+        if( !c ) {
+            return str;
+        }
         str+=c;
     }
     Fail( "EOF" );
@@ -236,7 +233,7 @@ string B3DImporter::ReadChunk(){
         tag+=char( ReadByte() );
     }
 #ifdef DEBUG_B3D
-//	cout<<"ReadChunk:"<<tag<<endl;
+    ASSIMP_LOG_DEBUG_F("ReadChunk: ", tag);
 #endif
     unsigned sz=(unsigned)ReadInt();
     _stack.push_back( _pos+sz );
@@ -267,7 +264,6 @@ T *B3DImporter::to_array( const vector<T> &v ){
     return p;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 template<class T>
 T **unique_to_array( vector<std::unique_ptr<T> > &v ){
@@ -281,7 +277,6 @@ T **unique_to_array( vector<std::unique_ptr<T> > &v ){
     return p;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 void B3DImporter::ReadTEXS(){
     while( ChunkSize() ){
@@ -374,9 +369,13 @@ void B3DImporter::ReadVRTS(){
 
         v.vertex=ReadVec3();
 
-        if( _vflags & 1 ) v.normal=ReadVec3();
+        if( _vflags & 1 ) {
+            v.normal=ReadVec3();
+        }
 
-        if( _vflags & 2 ) ReadQuat();	//skip v 4bytes...
+        if( _vflags & 2 ) {
+            ReadQuat();	//skip v 4bytes...
+        }
 
         for( int i=0;i<_tcsets;++i ){
             float t[4]={0,0,0,0};
@@ -384,53 +383,55 @@ void B3DImporter::ReadVRTS(){
                 t[j]=ReadFloat();
             }
             t[1]=1-t[1];
-            if( !i ) v.texcoords=aiVector3D( t[0],t[1],t[2] );
+            if( !i ) {
+                v.texcoords=aiVector3D( t[0],t[1],t[2] );
+            }
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void B3DImporter::ReadTRIS( int v0 ){
-    int matid=ReadInt();
-    if( matid==-1 ){
-        matid=0;
-    }else if( matid<0 || matid>=(int)_materials.size() ){
+void B3DImporter::ReadTRIS(int v0) {
+	int matid = ReadInt();
+	if (matid == -1) {
+		matid = 0;
+	} else if (matid < 0 || matid >= (int)_materials.size()) {
 #ifdef DEBUG_B3D
-        cout<<"material id="<<matid<<endl;
+		ASSIMP_LOG_ERROR_F("material id=", matid);
 #endif
-        Fail( "Bad material id" );
-    }
+		Fail("Bad material id");
+	}
 
-    std::unique_ptr<aiMesh> mesh(new aiMesh);
+	std::unique_ptr<aiMesh> mesh(new aiMesh);
 
-    mesh->mMaterialIndex=matid;
-    mesh->mNumFaces=0;
-    mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE;
+	mesh->mMaterialIndex = matid;
+	mesh->mNumFaces = 0;
+	mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
 
-    int n_tris=ChunkSize()/12;
-    aiFace *face=mesh->mFaces=new aiFace[n_tris];
+	int n_tris = ChunkSize() / 12;
+	aiFace *face = mesh->mFaces = new aiFace[n_tris];
 
-    for( int i=0;i<n_tris;++i ){
-        int i0=ReadInt()+v0;
-        int i1=ReadInt()+v0;
-        int i2=ReadInt()+v0;
-        if( i0<0 || i0>=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){
+	for (int i = 0; i < n_tris; ++i) {
+		int i0 = ReadInt() + v0;
+		int i1 = ReadInt() + v0;
+		int i2 = ReadInt() + v0;
+		if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
 #ifdef DEBUG_B3D
-            cout<<"Bad triangle index: i0="<<i0<<", i1="<<i1<<", i2="<<i2<<endl;
+			ASSIMP_LOG_ERROR_F("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
 #endif
-            Fail( "Bad triangle index" );
-            continue;
-        }
-        face->mNumIndices=3;
-        face->mIndices=new unsigned[3];
-        face->mIndices[0]=i0;
-        face->mIndices[1]=i1;
-        face->mIndices[2]=i2;
-        ++mesh->mNumFaces;
-        ++face;
-    }
-
-    _meshes.emplace_back( std::move(mesh) );
+			Fail("Bad triangle index");
+			continue;
+		}
+		face->mNumIndices = 3;
+		face->mIndices = new unsigned[3];
+		face->mIndices[0] = i0;
+		face->mIndices[1] = i1;
+		face->mIndices[2] = i2;
+		++mesh->mNumFaces;
+		++face;
+	}
+
+	_meshes.emplace_back(std::move(mesh));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -451,29 +452,23 @@ void B3DImporter::ReadMESH(){
 }
 
 // ------------------------------------------------------------------------------------------------
-void B3DImporter::ReadBONE( int id ){
-    while( ChunkSize() ){
-        int vertex=ReadInt();
-        float weight=ReadFloat();
-        if( vertex<0 || vertex>=(int)_vertices.size() ){
-            Fail( "Bad vertex index" );
-        }
-
-        Vertex &v=_vertices[vertex];
-        int i;
-        for( i=0;i<4;++i ){
-            if( !v.weights[i] ){
-                v.bones[i]=id;
-                v.weights[i]=weight;
-                break;
-            }
-        }
-#ifdef DEBUG_B3D
-        if( i==4 ){
-            cout<<"Too many bone weights"<<endl;
-        }
-#endif
-    }
+void B3DImporter::ReadBONE(int id) {
+	while (ChunkSize()) {
+		int vertex = ReadInt();
+		float weight = ReadFloat();
+		if (vertex < 0 || vertex >= (int)_vertices.size()) {
+			Fail("Bad vertex index");
+		}
+
+		Vertex &v = _vertices[vertex];
+		for (int i = 0; i < 4; ++i) {
+			if (!v.weights[i]) {
+				v.bones[i] = id;
+				v.weights[i] = weight;
+				break;
+			}
+		}
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -614,7 +609,7 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
         if (!DefaultLogger::isNullLogger()) {
             char dmp[128];
             ai_snprintf(dmp, 128, "B3D file format version: %i",version);
-            DefaultLogger::get()->info(dmp);
+            ASSIMP_LOG_INFO(dmp);
         }
 
         while( ChunkSize() ){
@@ -631,11 +626,15 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
     }
     ExitChunk();
 
-    if( !_nodes.size() ) Fail( "No nodes" );
+    if( !_nodes.size() ) {
+        Fail( "No nodes" );
+    }
 
-    if( !_meshes.size() ) Fail( "No meshes" );
+    if( !_meshes.size() ) {
+        Fail( "No meshes" );
+    }
 
-    //Fix nodes/meshes/bones
+    // Fix nodes/meshes/bones
     for(size_t i=0;i<_nodes.size();++i ){
         aiNode *node=_nodes[i];
 
@@ -646,8 +645,12 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
             int n_verts=mesh->mNumVertices=n_tris * 3;
 
             aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0;
-            if( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ];
-            if( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ];
+            if( _vflags & 1 ) {
+                mn=mesh->mNormals=new aiVector3D[ n_verts ];
+            }
+            if( _tcsets ) {
+                mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ];
+            }
 
             aiFace *face=mesh->mFaces;
 

+ 1 - 1
code/B3DImporter.h → code/B3D/B3DImporter.h

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

+ 61 - 30
code/BVHLoader.cpp → code/BVH/BVHLoader.cpp

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/IOSystem.hpp>
 #include <assimp/scene.h>
 #include <assimp/importerdesc.h>
+#include <map>
 
 using namespace Assimp;
 using namespace Assimp::Formatter;
@@ -461,6 +462,13 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
         aiNodeAnim* nodeAnim = new aiNodeAnim;
         anim->mChannels[a] = nodeAnim;
         nodeAnim->mNodeName.Set( nodeName);
+		std::map<BVHLoader::ChannelType, int> channelMap;
+
+		//Build map of channels 
+		for (unsigned int channel = 0; channel < node.mChannels.size(); ++channel)
+		{
+			channelMap[node.mChannels[channel]] = channel;
+		}
 
         // translational part, if given
         if( node.mChannels.size() == 6)
@@ -472,16 +480,32 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
             {
                 poskey->mTime = double( fr);
 
-                // Now compute all translations in the right order
-                for( unsigned int channel = 0; channel < 3; ++channel)
+                // Now compute all translations 
+                for(BVHLoader::ChannelType channel = Channel_PositionX; channel <= Channel_PositionZ; channel = (BVHLoader::ChannelType)(channel +1))
                 {
-                    switch( node.mChannels[channel])
-                    {
-                    case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
-                    case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
-                    case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
-                    default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
-                    }
+					//Find channel in node
+					std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
+
+					if (mapIter == channelMap.end())
+						throw DeadlyImportError("Missing position channel in node " + nodeName);
+					else {
+						int channelIdx = mapIter->second;
+						switch (channel) {
+						    case Channel_PositionX: 
+                                poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; 
+                                break;
+						    case Channel_PositionY: 
+                                poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; 
+                                break;
+						    case Channel_PositionZ: 
+                                poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channelIdx]; 
+                                break;
+                                
+                            default:
+                                break;
+						}
+
+					}
                 }
                 ++poskey;
             }
@@ -497,12 +521,6 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
 
         // rotation part. Always present. First find value offsets
         {
-            unsigned int rotOffset  = 0;
-            if( node.mChannels.size() == 6)
-            {
-                // Offset all further calculations
-                rotOffset = 3;
-            }
 
             // Then create the number of rotation keys
             nodeAnim->mNumRotationKeys = mAnimNumFrames;
@@ -512,20 +530,33 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
             {
                 aiMatrix4x4 temp;
                 aiMatrix3x3 rotMatrix;
-
-                for( unsigned int channel = 0; channel < 3; ++channel)
-                {
-                    // translate ZXY euler angels into a quaternion
-                    const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f;
-
-                    // Compute rotation transformations in the right order
-                    switch (node.mChannels[rotOffset+channel])
-                    {
-                    case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
-                    case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp);  break;
-                    case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
-                    default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
-                    }
+				for (BVHLoader::ChannelType channel = Channel_RotationX; channel <= Channel_RotationZ; channel = (BVHLoader::ChannelType)(channel + 1))
+				{
+					//Find channel in node
+					std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
+
+					if (mapIter == channelMap.end())
+						throw DeadlyImportError("Missing rotation channel in node " + nodeName);
+					else {
+						int channelIdx = mapIter->second;
+						// translate ZXY euler angels into a quaternion
+						const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+
+						// Compute rotation transformations in the right order
+						switch (channel)
+						{
+							case Channel_RotationX: 
+                                aiMatrix4x4::RotationX(angle, temp); rotMatrix *= aiMatrix3x3(temp); 
+                                break;
+							case Channel_RotationY: 
+                                aiMatrix4x4::RotationY(angle, temp); rotMatrix *= aiMatrix3x3(temp);  
+                                break;
+							case Channel_RotationZ: aiMatrix4x4::RotationZ(angle, temp); rotMatrix *= aiMatrix3x3(temp); 
+                                break;
+                            default:
+                                break;
+						}
+					}
                 }
 
                 rotkey->mTime = double( fr);

+ 1 - 1
code/BVHLoader.h → code/BVH/BVHLoader.h

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

+ 0 - 0
code/BlenderBMesh.cpp → code/Blender/BlenderBMesh.cpp


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


+ 189 - 0
code/Blender/BlenderCustomData.cpp

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

+ 89 - 0
code/Blender/BlenderCustomData.h

@@ -0,0 +1,89 @@
+#pragma once
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include <memory>
+
+namespace Assimp {
+    namespace Blender {
+        /* CustomData.type from Blender (2.79b) */
+        enum CustomDataType {
+            CD_AUTO_FROM_NAME = -1,
+            CD_MVERT = 0,
+#ifdef DNA_DEPRECATED
+            CD_MSTICKY = 1,  /* DEPRECATED */
+#endif
+            CD_MDEFORMVERT = 2,
+            CD_MEDGE = 3,
+            CD_MFACE = 4,
+            CD_MTFACE = 5,
+            CD_MCOL = 6,
+            CD_ORIGINDEX = 7,
+            CD_NORMAL = 8,
+            /*	CD_POLYINDEX        = 9, */
+            CD_PROP_FLT = 10,
+            CD_PROP_INT = 11,
+            CD_PROP_STR = 12,
+            CD_ORIGSPACE = 13,  /* for modifier stack face location mapping */
+            CD_ORCO = 14,
+            CD_MTEXPOLY = 15,
+            CD_MLOOPUV = 16,
+            CD_MLOOPCOL = 17,
+            CD_TANGENT = 18,
+            CD_MDISPS = 19,
+            CD_PREVIEW_MCOL = 20,  /* for displaying weightpaint colors */
+            /*	CD_ID_MCOL          = 21, */
+            CD_TEXTURE_MLOOPCOL = 22,
+            CD_CLOTH_ORCO = 23,
+            CD_RECAST = 24,
+
+            /* BMESH ONLY START */
+            CD_MPOLY = 25,
+            CD_MLOOP = 26,
+            CD_SHAPE_KEYINDEX = 27,
+            CD_SHAPEKEY = 28,
+            CD_BWEIGHT = 29,
+            CD_CREASE = 30,
+            CD_ORIGSPACE_MLOOP = 31,
+            CD_PREVIEW_MLOOPCOL = 32,
+            CD_BM_ELEM_PYPTR = 33,
+            /* BMESH ONLY END */
+
+            CD_PAINT_MASK = 34,
+            CD_GRID_PAINT_MASK = 35,
+            CD_MVERT_SKIN = 36,
+            CD_FREESTYLE_EDGE = 37,
+            CD_FREESTYLE_FACE = 38,
+            CD_MLOOPTANGENT = 39,
+            CD_TESSLOOPNORMAL = 40,
+            CD_CUSTOMLOOPNORMAL = 41,
+
+            CD_NUMTYPES = 42
+        };
+
+        /**
+        *   @brief  check if given cdtype is valid (ie >= 0 and < CD_NUMTYPES)
+        *   @param[in]  cdtype to check
+        *   @return true when valid
+        */
+        bool isValidCustomDataType(const int cdtype);
+
+        /**
+        *   @brief  returns CustomDataLayer ptr for given cdtype and name
+        *   @param[in]  customdata CustomData to search for wanted layer
+        *   @param[in]  cdtype to search for
+        *   @param[in]  name to search for
+        *   @return CustomDataLayer * or nullptr if not found
+        */
+        std::shared_ptr<CustomDataLayer> getCustomDataLayer(const CustomData &customdata, CustomDataType cdtype, const std::string &name);
+
+        /**
+        *   @brief  returns CustomDataLayer data ptr for given cdtype and name
+        *   @param[in]  customdata CustomData to search for wanted layer
+        *   @param[in]  cdtype to search for
+        *   @param[in]  name to search for
+        *   @return * to struct data or nullptr if not found
+        */
+        const ElemBase * getCustomDataLayerData(const CustomData &customdata, CustomDataType cdtype, const std::string &name);
+    }
+}

+ 6 - 7
code/BlenderDNA.cpp → code/Blender/BlenderDNA.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -210,8 +210,7 @@ void DNAParser::Parse ()
         s.size = offset;
     }
 
-    DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(),
-        " structures with totally ",fields," fields"));
+    ASSIMP_LOG_DEBUG_F( "BlenderDNA: Got ", dna.structures.size()," structures with totally ",fields," fields");
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
     dna.DumpToFile();
@@ -228,12 +227,12 @@ void DNAParser::Parse ()
 // ------------------------------------------------------------------------------------------------
 void DNA :: DumpToFile()
 {
-    // we dont't bother using the VFS here for this is only for debugging.
+    // we don't bother using the VFS here for this is only for debugging.
     // (and all your bases are belong to us).
 
     std::ofstream f("dna.txt");
     if (f.fail()) {
-        DefaultLogger::get()->error("Could not dump dna to dna.txt");
+        ASSIMP_LOG_ERROR("Could not dump dna to dna.txt");
         return;
     }
     f << "Field format: type name offset size" << "\n";
@@ -248,7 +247,7 @@ void DNA :: DumpToFile()
     }
     f << std::flush;
 
-    DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt");
+    ASSIMP_LOG_INFO("BlenderDNA: Dumped dna to dna.txt");
 }
 #endif
 
@@ -367,7 +366,7 @@ void SectionParser :: Next()
     }
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
-    DefaultLogger::get()->debug(current.id);
+    ASSIMP_LOG_DEBUG(current.id);
 #endif
 }
 

+ 37 - 4
code/BlenderDNA.h → code/Blender/BlenderDNA.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -309,6 +309,28 @@ public:
     void ReadField(T& out, const char* name,
         const FileDatabase& db) const;
 
+    // --------------------------------------------------------
+    /**
+    *   @brief  field parsing for dynamic vectors
+    *   @param[in]  out vector of struct to be filled
+    *   @param[in]  name of field
+    *   @param[in]  db to access the file, dna, ...
+    *   @return true when read was successful
+    */
+    template <int error_policy, template <typename> class TOUT, typename T>
+    bool ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const;
+
+    /**
+    *   @brief  parses raw customdata
+    *   @param[in]  out shared_ptr to be filled
+    *   @param[in]  cdtype customdata type to read
+    *   @param[in]  name of field ptr
+    *   @param[in]  db to access the file, dna, ...
+    *   @return true when read was successful
+    */
+    template <int error_policy>
+    bool ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const;
+
 private:
 
     // --------------------------------------------------------
@@ -381,7 +403,7 @@ template <>  struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
 
     template <typename T>
     void operator ()(T& out, const char* reason = "<add reason>") {
-        DefaultLogger::get()->warn(reason);
+        ASSIMP_LOG_WARN(reason);
 
         // ... and let the show go on
         _defaultInitializer<0 /*ErrorPolicy_Igno*/>()(out);
@@ -394,7 +416,7 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
     void operator ()(T& /*out*/,const char* = "") {
         // obviously, it is crucial that _DefaultInitializer is used
         // only from within a catch clause.
-        throw;
+        throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
     }
 };
 
@@ -663,7 +685,7 @@ public:
     /** Check whether a specific item is in the cache.
      *  @param s Data type of the item
      *  @param out Output pointer. Unchanged if the
-     *   cache doens't know the item yet.
+     *   cache doesn't know the item yet.
      *  @param ptr Item address to look for. */
     template <typename T> void get (
         const Structure& s,
@@ -803,6 +825,17 @@ private:
     FileDatabase& db;
 };
 
+/**
+*   @brief  read CustomData's data to ptr to mem
+*   @param[out] out memory ptr to set
+*   @param[in]  cdtype  to read
+*   @param[in]  cnt cnt of elements to read
+*   @param[in]  db to read elements from
+*   @return true when ok
+*/
+bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
+
+
     } // end Blend
 } // end Assimp
 

+ 104 - 4
code/BlenderDNA.inl → code/Blender/BlenderDNA.inl

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -307,6 +307,108 @@ void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) co
 }
 
 
+//--------------------------------------------------------------------------------
+// field parsing for raw untyped data (like CustomDataLayer.data)
+template <int error_policy>
+bool Structure::ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const {
+
+	const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+
+	Pointer ptrval;
+	const Field* f;
+	try	{
+		f = &(*this)[name];
+
+		// 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"));
+		}
+
+		db.reader->IncPtr(f->offset);
+		Convert(ptrval, db);
+		// actually it is meaningless on which Structure the Convert is called
+		// because the `Pointer` argument triggers a special implementation.
+	}
+	catch (const Error& e) {
+		_defaultInitializer<error_policy>()(out, e.what());
+		out.reset();
+	}
+
+	bool readOk = true;
+	if (ptrval.val)	{
+		// get block for ptr
+		const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
+		db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
+		// read block->num instances of given type to out
+		readOk = readCustomData(out, cdtype, block->num, db);
+	}
+
+	// and recover the previous stream position
+	db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+	++db.stats().fields_read;
+#endif
+
+	return readOk;
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T>
+bool Structure::ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const {
+	out.clear();
+
+	const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+
+	Pointer ptrval;
+	const Field* f;
+	try	{
+		f = &(*this)[name];
+
+		// 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"));
+		}
+
+		db.reader->IncPtr(f->offset);
+		Convert(ptrval, db);
+		// actually it is meaningless on which Structure the Convert is called
+		// because the `Pointer` argument triggers a special implementation.
+	}
+	catch (const Error& e) {
+		_defaultInitializer<error_policy>()(out, e.what());
+		out.clear();
+		return false;
+	}
+
+
+	if (ptrval.val)	{
+		// find the file block the pointer is pointing to
+		const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
+		db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
+		// FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+		// I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+		const Structure& s = db.dna[f->type];
+		for (size_t i = 0; i < block->num; ++i)	{
+			TOUT<T> p(new T);
+			s.Convert(*p, db);
+			out.push_back(p);
+		}
+	}
+
+	db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+	++db.stats().fields_read;
+#endif
+
+	return true;
+}
+
+
 //--------------------------------------------------------------------------------
 template <template <typename> class TOUT, typename T>
 bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db,
@@ -468,9 +570,7 @@ template <> bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shar
         // this might happen if DNA::RegisterConverters hasn't been called so far
         // or if the target type is not contained in `our` DNA.
         out.reset();
-        DefaultLogger::get()->warn((Formatter::format(),
-            "Failed to find a converter for the `",s.name,"` structure"
-            ));
+        ASSIMP_LOG_WARN_F( "Failed to find a converter for the `",s.name,"` structure" );
         return false;
     }
 

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

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

+ 98 - 12
code/BlenderLoader.cpp → code/Blender/BlenderLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BlenderIntermediate.h"
 #include "BlenderModifier.h"
 #include "BlenderBMesh.h"
+#include "BlenderCustomData.h"
 #include <assimp/StringUtils.h>
 #include <assimp/scene.h>
 #include <assimp/importerdesc.h>
@@ -327,12 +328,12 @@ void BlenderImporter::ExtractScene(Scene& out, const FileDatabase& file)
     ss.Convert(out,file);
 
 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
-    DefaultLogger::get()->info((format(),
+    ASSIMP_LOG_INFO_F(
         "(Stats) Fields read: " ,file.stats().fields_read,
         ", pointers resolved: " ,file.stats().pointers_resolved,
         ", cache hits: "        ,file.stats().cache_hits,
         ", cached objects: "    ,file.stats().cached_objects
-    ));
+    );
 #endif
 }
 
@@ -1021,6 +1022,34 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
         }
     }
 
+    // TODO should we create the TextureUVMapping map in Convert<Material> to prevent redundant processing?
+
+    // create texture <-> uvname mapping for all materials
+    // key is texture number, value is data *
+    typedef std::map<uint32_t, const MLoopUV *> TextureUVMapping;
+    // key is material number, value is the TextureUVMapping for the material
+    typedef std::map<uint32_t, TextureUVMapping> MaterialTextureUVMappings;
+    MaterialTextureUVMappings matTexUvMappings;
+    const uint32_t maxMat = static_cast<const uint32_t>(mesh->mat.size());
+    for (uint32_t m = 0; m < maxMat; ++m) {
+        // get material by index
+        const std::shared_ptr<Material> pMat = mesh->mat[m];
+        TextureUVMapping texuv;
+        const uint32_t maxTex = sizeof(pMat->mtex) / sizeof(pMat->mtex[0]);
+        for (uint32_t t = 0; t < maxTex; ++t) {
+            if (pMat->mtex[t] && pMat->mtex[t]->uvname[0]) {
+                // get the CustomData layer for given uvname and correct type
+                const ElemBase *pLoop = getCustomDataLayerData(mesh->ldata, CD_MLOOPUV, pMat->mtex[t]->uvname);
+                if (pLoop) {
+                    texuv.insert(std::make_pair(t, dynamic_cast<const MLoopUV *>(pLoop)));
+                }
+            }
+        }
+        if (texuv.size()) {
+            matTexUvMappings.insert(std::make_pair(m, texuv));
+        }
+    }
+
     // collect texture coordinates, they're stored in a separate per-face buffer
     if (mesh->mtface || mesh->mloopuv) {
         if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
@@ -1028,8 +1057,17 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
         }
         for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
             ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
-
-            (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+            const auto itMatTexUvMapping = matTexUvMappings.find((*it)->mMaterialIndex);
+            if (itMatTexUvMapping == matTexUvMappings.end()) {
+                // default behaviour like before
+                (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+            }
+            else {
+                // create texture coords for every mapped tex
+                for (uint32_t i = 0; i < itMatTexUvMapping->second.size(); ++i) {
+                    (*it)->mTextureCoords[i] = new aiVector3D[(*it)->mNumVertices];
+                }
+            }
             (*it)->mNumFaces = (*it)->mNumVertices = 0;
         }
 
@@ -1051,13 +1089,34 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
             aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
             const aiFace& f = out->mFaces[out->mNumFaces++];
 
-            aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
-            for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
-                const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
-                vo->x = uv.uv[0];
-                vo->y = uv.uv[1];
+            const auto itMatTexUvMapping = matTexUvMappings.find(v.mat_nr);
+            if (itMatTexUvMapping == matTexUvMappings.end()) {
+                // old behavior
+                aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+                for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
+                    const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
+                    vo->x = uv.uv[0];
+                    vo->y = uv.uv[1];
+                }
+            }
+            else {
+                // create textureCoords for every mapped tex
+                for (uint32_t m = 0; m < itMatTexUvMapping->second.size(); ++m) {
+                    const MLoopUV *tm = itMatTexUvMapping->second[m];
+                    aiVector3D* vo = &out->mTextureCoords[m][out->mNumVertices];
+                    uint32_t j = 0;
+                    for (; j < f.mNumIndices; ++j, ++vo) {
+                        const MLoopUV& uv = tm[v.loopstart + j];
+                        vo->x = uv.uv[0];
+                        vo->y = uv.uv[1];
+                    }
+                    // only update written mNumVertices in last loop
+                    // TODO why must the numVertices be incremented here?
+                    if (m == itMatTexUvMapping->second.size() - 1) {
+                        out->mNumVertices += j;
+                    }
+                }
             }
-
         }
     }
 
@@ -1147,7 +1206,7 @@ aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj,
     out->mUp = aiVector3D(0.f, 1.f, 0.f);
     out->mLookAt = aiVector3D(0.f, 0.f, -1.f);
     if (cam->sensor_x && cam->lens) {
-        out->mHorizontalFOV = std::atan2(cam->sensor_x,  2.f * cam->lens);
+        out->mHorizontalFOV = 2.f * std::atan2(cam->sensor_x,  2.f * cam->lens);
     }
     out->mClipPlaneNear = cam->clipsta;
     out->mClipPlaneFar = cam->clipend;
@@ -1166,6 +1225,16 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
         case Lamp::Type_Local:
             out->mType = aiLightSource_POINT;
             break;
+        case Lamp::Type_Spot:
+            out->mType = aiLightSource_SPOT;
+
+            // blender orients directional lights as facing toward -z
+            out->mDirection = aiVector3D(0.f, 0.f, -1.f);
+            out->mUp = aiVector3D(0.f, 1.f, 0.f);
+
+            out->mAngleInnerCone = lamp->spotsize * (1.0f - lamp->spotblend);
+            out->mAngleOuterCone = lamp->spotsize;
+            break;
         case Lamp::Type_Sun:
             out->mType = aiLightSource_DIRECTIONAL;
 
@@ -1196,6 +1265,23 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
     out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+
+    // If default values are supplied, compute the coefficients from light's max distance
+    // Read this: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
+    //
+    if (lamp->constant_coefficient == 1.0f && lamp->linear_coefficient == 0.0f && lamp->quadratic_coefficient == 0.0f && lamp->dist > 0.0f)
+    {
+        out->mAttenuationConstant = 1.0f;
+        out->mAttenuationLinear = 2.0f / lamp->dist;
+        out->mAttenuationQuadratic = 1.0f / (lamp->dist * lamp->dist);
+    }
+    else
+    {
+        out->mAttenuationConstant = lamp->constant_coefficient;
+        out->mAttenuationLinear = lamp->linear_coefficient;
+        out->mAttenuationQuadratic = lamp->quadratic_coefficient;
+    }
+
     return out.release();
 }
 

+ 1 - 1
code/BlenderLoader.h → code/Blender/BlenderLoader.h

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

+ 1 - 29
code/BlenderModifier.cpp → code/Blender/BlenderModifier.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -70,34 +70,6 @@ static const fpCreateModifier creators[] = {
         NULL // sentinel
 };
 
-// ------------------------------------------------------------------------------------------------
-// just testing out some new macros to simplify logging
-#define ASSIMP_LOG_WARN_F(string,...)\
-    DefaultLogger::get()->warn((Formatter::format(string),__VA_ARGS__))
-
-#define ASSIMP_LOG_ERROR_F(string,...)\
-    DefaultLogger::get()->error((Formatter::format(string),__VA_ARGS__))
-
-#define ASSIMP_LOG_DEBUG_F(string,...)\
-    DefaultLogger::get()->debug((Formatter::format(string),__VA_ARGS__))
-
-#define ASSIMP_LOG_INFO_F(string,...)\
-    DefaultLogger::get()->info((Formatter::format(string),__VA_ARGS__))
-
-
-#define ASSIMP_LOG_WARN(string)\
-    DefaultLogger::get()->warn(string)
-
-#define ASSIMP_LOG_ERROR(string)\
-    DefaultLogger::get()->error(string)
-
-#define ASSIMP_LOG_DEBUG(string)\
-    DefaultLogger::get()->debug(string)
-
-#define ASSIMP_LOG_INFO(string)\
-    DefaultLogger::get()->info(string)
-
-
 // ------------------------------------------------------------------------------------------------
 struct SharedModifierData : ElemBase
 {

+ 29 - 32
code/BlenderModifier.h → code/Blender/BlenderModifier.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -47,53 +47,57 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define INCLUDED_AI_BLEND_MODIFIER_H
 
 #include "BlenderIntermediate.h"
-#include <assimp/TinyFormatter.h>
 
 namespace Assimp {
 namespace Blender {
 
 // -------------------------------------------------------------------------------------------
-/** Dummy base class for all blender modifiers. Modifiers are reused between imports, so
- *  they should be stateless and not try to cache model data. */
+/** 
+ *  Dummy base class for all blender modifiers. Modifiers are reused between imports, so
+ *  they should be stateless and not try to cache model data. 
+ */
 // -------------------------------------------------------------------------------------------
-class BlenderModifier
-{
+class BlenderModifier {
 public:
+    /**
+     *  The class destructor, virtual.
+     */
     virtual ~BlenderModifier() {
         // empty
     }
 
-public:
-
     // --------------------
-    /** Check if *this* modifier is active, given a ModifierData& block.*/
+    /** 
+     *  Check if *this* modifier is active, given a ModifierData& block.
+     */
     virtual bool IsActive( const ModifierData& /*modin*/) {
         return false;
     }
 
     // --------------------
-    /** Apply the modifier to a given output node. The original data used
+    /** 
+     *  Apply the modifier to a given output node. The original data used
      *  to construct the node is given as well. Not called unless IsActive()
-     *  was called and gave positive response. */
+     *  was called and gave positive response. 
+     */
     virtual void DoIt(aiNode& /*out*/,
         ConversionData& /*conv_data*/,
         const ElemBase& orig_modifier,
         const Scene& /*in*/,
         const Object& /*orig_object*/
     ) {
-        DefaultLogger::get()->warn((Formatter::format("This modifier is not supported, skipping: "),orig_modifier.dna_type));
+        ASSIMP_LOG_INFO_F("This modifier is not supported, skipping: ",orig_modifier.dna_type );
         return;
     }
 };
 
-
 // -------------------------------------------------------------------------------------------
-/** Manage all known modifiers and instance and apply them if necessary */
+/** 
+ *  Manage all known modifiers and instance and apply them if necessary 
+ */
 // -------------------------------------------------------------------------------------------
-class BlenderModifierShowcase
-{
+class BlenderModifierShowcase {
 public:
-
     // --------------------
     /** Apply all requested modifiers provided we support them. */
     void ApplyModifiers(aiNode& out,
@@ -103,25 +107,18 @@ public:
     );
 
 private:
-
     TempArray< std::vector,BlenderModifier > cached_modifiers;
 };
 
-
-
-
-
-// MODIFIERS
-
-
+// MODIFIERS /////////////////////////////////////////////////////////////////////////////////
 
 // -------------------------------------------------------------------------------------------
-/** Mirror modifier. Status: implemented. */
+/** 
+ *  Mirror modifier. Status: implemented. 
+ */
 // -------------------------------------------------------------------------------------------
-class BlenderModifier_Mirror : public BlenderModifier
-{
+class BlenderModifier_Mirror : public BlenderModifier {
 public:
-
     // --------------------
     virtual bool IsActive( const ModifierData& modin);
 
@@ -137,8 +134,7 @@ public:
 // -------------------------------------------------------------------------------------------
 /** Subdivision modifier. Status: dummy. */
 // -------------------------------------------------------------------------------------------
-class BlenderModifier_Subdivision : public BlenderModifier
-{
+class BlenderModifier_Subdivision : public BlenderModifier {
 public:
 
     // --------------------
@@ -153,6 +149,7 @@ public:
     ) ;
 };
 
+}
+}
 
-}}
 #endif // !INCLUDED_AI_BLEND_MODIFIER_H

+ 49 - 2
code/BlenderScene.cpp → code/Blender/BlenderScene.cpp

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BlenderScene.h"
 #include "BlenderSceneGen.h"
 #include "BlenderDNA.h"
+#include "BlenderCustomData.h"
 
 using namespace Assimp;
 using namespace Assimp::Blender;
@@ -202,7 +203,7 @@ template <> void Structure :: Convert<Lamp> (
     int temp = 0;
     ReadField<ErrorPolicy_Fail>(temp,"type",db);
     dest.type = static_cast<Assimp::Blender::Lamp::Type>(temp);
-    ReadField<ErrorPolicy_Igno>(dest.flags,"flags",db);
+    ReadField<ErrorPolicy_Igno>(dest.flags,"flag",db);
     ReadField<ErrorPolicy_Igno>(dest.colormodel,"colormodel",db);
     ReadField<ErrorPolicy_Igno>(dest.totex,"totex",db);
     ReadField<ErrorPolicy_Warn>(dest.r,"r",db);
@@ -210,9 +211,12 @@ template <> void Structure :: Convert<Lamp> (
     ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
     ReadField<ErrorPolicy_Warn>(dest.k,"k",db);
     ReadField<ErrorPolicy_Igno>(dest.energy,"energy",db);
-    ReadField<ErrorPolicy_Igno>(dest.dist,"dist",db);
+    ReadField<ErrorPolicy_Warn>(dest.dist,"dist",db);
     ReadField<ErrorPolicy_Igno>(dest.spotsize,"spotsize",db);
     ReadField<ErrorPolicy_Igno>(dest.spotblend,"spotblend",db);
+    ReadField<ErrorPolicy_Warn>(dest.constant_coefficient, "coeff_const", db);
+    ReadField<ErrorPolicy_Warn>(dest.linear_coefficient, "coeff_lin", db);
+    ReadField<ErrorPolicy_Warn>(dest.quadratic_coefficient, "coeff_quad", db);
     ReadField<ErrorPolicy_Igno>(dest.att1,"att1",db);
     ReadField<ErrorPolicy_Igno>(dest.att2,"att2",db);
     ReadField<ErrorPolicy_Igno>(temp,"falloff_type",db);
@@ -481,6 +485,12 @@ template <> void Structure :: Convert<Mesh> (
     ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol,"*mcol",db);
     ReadFieldPtr<ErrorPolicy_Fail>(dest.mat,"**mat",db);
 
+    ReadField<ErrorPolicy_Igno>(dest.vdata, "vdata", db);
+    ReadField<ErrorPolicy_Igno>(dest.edata, "edata", db);
+    ReadField<ErrorPolicy_Igno>(dest.fdata, "fdata", db);
+    ReadField<ErrorPolicy_Igno>(dest.pdata, "pdata", db);
+    ReadField<ErrorPolicy_Warn>(dest.ldata, "ldata", db);
+
     db.reader->IncPtr(size);
 }
 
@@ -786,6 +796,41 @@ template <> void Structure :: Convert<Image> (
     db.reader->IncPtr(size);
 }
 
+//--------------------------------------------------------------------------------
+template <> void Structure::Convert<CustomData>(
+    CustomData& dest,
+    const FileDatabase& db
+    ) const
+{
+    ReadFieldArray<ErrorPolicy_Warn>(dest.typemap, "typemap", db);
+    ReadField<ErrorPolicy_Warn>(dest.totlayer, "totlayer", db);
+    ReadField<ErrorPolicy_Warn>(dest.maxlayer, "maxlayer", db);
+    ReadField<ErrorPolicy_Warn>(dest.totsize, "totsize", db);
+    ReadFieldPtrVector<ErrorPolicy_Warn>(dest.layers, "*layers", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure::Convert<CustomDataLayer>(
+    CustomDataLayer& dest,
+    const FileDatabase& db
+    ) const
+{
+    ReadField<ErrorPolicy_Fail>(dest.type, "type", db);
+    ReadField<ErrorPolicy_Fail>(dest.offset, "offset", db);
+    ReadField<ErrorPolicy_Fail>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Fail>(dest.active, "active", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_rnd, "active_rnd", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_clone, "active_clone", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_mask, "active_mask", db);
+    ReadField<ErrorPolicy_Fail>(dest.uid, "uid", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadCustomDataPtr<ErrorPolicy_Fail>(dest.data, dest.type, "*data", db);
+
+    db.reader->IncPtr(size);
+}
+
 //--------------------------------------------------------------------------------
 void DNA::RegisterConverters() {
 
@@ -822,6 +867,8 @@ void DNA::RegisterConverters() {
     converters["Camera"] = DNA::FactoryPair( &Structure::Allocate<Camera>, &Structure::Convert<Camera> );
     converters["MirrorModifierData"] = DNA::FactoryPair( &Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData> );
     converters["Image"] = DNA::FactoryPair( &Structure::Allocate<Image>, &Structure::Convert<Image> );
+    converters["CustomData"] = DNA::FactoryPair(&Structure::Allocate<CustomData>, &Structure::Convert<CustomData>);
+    converters["CustomDataLayer"] = DNA::FactoryPair(&Structure::Allocate<CustomDataLayer>, &Structure::Convert<CustomDataLayer>);
 }
 
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 84 - 2
code/BlenderScene.h → code/Blender/BlenderScene.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -157,10 +157,16 @@ struct World : ElemBase {
 // -------------------------------------------------------------------------------
 struct MVert : ElemBase {
     float co[3] FAIL;
-    float no[3] FAIL;
+    float no[3] FAIL;       // readed as short and divided through / 32767.f
     char flag;
     int mat_nr WARN;
     int bweight;
+
+    MVert() : ElemBase()
+        , flag(0)
+        , mat_nr(0)
+        , bweight(0)
+    {}
 };
 
 // -------------------------------------------------------------------------------
@@ -369,6 +375,72 @@ struct Material : ElemBase {
     std::shared_ptr<MTex> mtex[18];
 };
 
+/*
+CustomDataLayer 104
+
+    int type 0 4
+    int offset 4 4
+    int flag 8 4
+    int active 12 4
+    int active_rnd 16 4
+    int active_clone 20 4
+    int active_mask 24 4
+    int uid 28 4
+    char name 32 64
+    void *data 96 8
+*/
+struct CustomDataLayer : ElemBase {
+    int type;
+    int offset;
+    int flag;
+    int active;
+    int active_rnd;
+    int active_clone;
+    int active_mask;
+    int uid;
+    char name[64];
+    std::shared_ptr<ElemBase> data;     // must be converted to real type according type member
+
+    CustomDataLayer()
+        : ElemBase()
+        , type(0)
+        , offset(0)
+        , flag(0)
+        , active(0)
+        , active_rnd(0)
+        , active_clone(0)
+        , active_mask(0)
+        , uid(0)
+        , data(nullptr)
+    {
+        memset(name, 0, sizeof name);
+    }
+};
+
+/*
+CustomData 208
+
+    CustomDataLayer *layers 0 8
+    int typemap 8 168
+    int pad_i1 176 4
+    int totlayer 180 4
+    int maxlayer 184 4
+    int totsize 188 4
+    BLI_mempool *pool 192 8
+    CustomDataExternal *external 200 8
+*/
+struct CustomData : ElemBase {
+    vector<std::shared_ptr<struct CustomDataLayer> > layers;
+    int typemap[42];    // CD_NUMTYPES
+    int totlayer;
+    int maxlayer;
+    int totsize;
+    /*
+    std::shared_ptr<BLI_mempool> pool;
+    std::shared_ptr<CustomDataExternal> external;
+    */
+};
+
 // -------------------------------------------------------------------------------
 struct Mesh : ElemBase {
     ID id FAIL;
@@ -398,6 +470,12 @@ struct Mesh : ElemBase {
     vector<MCol> mcol;
 
     vector< std::shared_ptr<Material> > mat FAIL;
+
+    struct CustomData vdata;
+    struct CustomData edata;
+    struct CustomData fdata;
+    struct CustomData pdata;
+    struct CustomData ldata;
 };
 
 // -------------------------------------------------------------------------------
@@ -460,6 +538,10 @@ struct Lamp : ElemBase {
       float energy, dist, spotsize, spotblend;
       //float haint;
 
+      float constant_coefficient;
+      float linear_coefficient;
+      float quadratic_coefficient;
+
       float att1, att2;
       //struct CurveMapping *curfalloff;
       FalloffType falloff_type;

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

@@ -248,6 +248,17 @@ template <> void Structure :: Convert<Image> (
     ) const
 ;
 
+template <> void Structure::Convert<CustomData>(
+    CustomData& dest,
+    const FileDatabase& db
+    ) const
+    ;
+
+template <> void Structure::Convert<CustomDataLayer>(
+    CustomDataLayer& dest,
+    const FileDatabase& db
+    ) const
+    ;
 
     }
 }

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

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

+ 7 - 3
code/BlenderTessellator.h → code/Blender/BlenderTessellator.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define INCLUDED_AI_BLEND_TESSELLATOR_H
 
 // Use these to toggle between GLU Tessellate or poly2tri
-// Note (acg) keep GLU Tesselate disabled by default - if it is turned on,
+// Note (acg) keep GLU Tessellate disabled by default - if it is turned on,
 // assimp needs to be linked against GLU, which is currently not yet
 // made configurable in CMake and potentially not wanted by most users
 // as it requires a Gl environment.
@@ -144,7 +144,11 @@ namespace Assimp
 
 #if ASSIMP_BLEND_WITH_POLY_2_TRI
 
-#include "../contrib/poly2tri/poly2tri/poly2tri.h"
+#ifdef ASSIMP_USE_HUNTER
+#  include <poly2tri/poly2tri.h>
+#else
+#  include "../contrib/poly2tri/poly2tri/poly2tri.h"
+#endif
 
 namespace Assimp
 {

+ 63 - 92
code/C4DImporter.cpp → code/C4D/C4DImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2019, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -68,8 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace melange;
 
 // overload this function and fill in your own unique data
-void GetWriterInfo(int &id, String &appname)
-{
+void GetWriterInfo(int &id, String &appname) {
     id = 2424226;
     appname = "Open Asset Import Library";
 }
@@ -78,7 +77,10 @@ using namespace Assimp;
 using namespace Assimp::Formatter;
 
 namespace Assimp {
-    template<> const std::string LogFunctions<C4DImporter>::log_prefix = "C4D: ";
+    template<> const char* LogFunctions<C4DImporter>::Prefix() {
+        static auto prefix = "C4D: ";
+        return prefix;
+    }
 }
 
 static const aiImporterDesc desc = {
@@ -97,47 +99,44 @@ static const aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 C4DImporter::C4DImporter()
-{}
+: BaseImporter() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
-C4DImporter::~C4DImporter()
-{}
+C4DImporter::~C4DImporter() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
+bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     const std::string& extension = GetExtension(pFile);
     if (extension == "c4d") {
         return true;
-    }
-
-    else if ((!extension.length() || checkSig) && pIOHandler)   {
+    } else if ((!extension.length() || checkSig) && pIOHandler)   {
         // TODO
     }
+
     return false;
 }
 
 // ------------------------------------------------------------------------------------------------
-const aiImporterDesc* C4DImporter::GetInfo () const
-{
+const aiImporterDesc* C4DImporter::GetInfo () const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::SetupProperties(const Importer* /*pImp*/)
-{
+void C4DImporter::SetupProperties(const Importer* /*pImp*/) {
     // nothing to be done for the moment
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void C4DImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
 
-    if( file.get() == NULL) {
+    if( file.get() == nullptr ) {
         ThrowException("failed to open file " + pFile);
     }
 
@@ -151,7 +150,7 @@ void C4DImporter::InternReadFile( const std::string& pFile,
 
     // open document first
     BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
-    if(doc == NULL) {
+    if(doc == nullptr ) {
         ThrowException("failed to read document " + pFile);
     }
 
@@ -160,11 +159,10 @@ void C4DImporter::InternReadFile( const std::string& pFile,
     // first convert all materials
     ReadMaterials(doc->GetFirstMaterial());
 
-    // process C4D scenegraph recursively
+    // process C4D scene-graph recursively
     try {
         RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
-    }
-    catch(...) {
+    } catch(...) {
         for(aiMesh* mesh : meshes) {
             delete mesh;
         }
@@ -201,8 +199,7 @@ void C4DImporter::InternReadFile( const std::string& pFile,
 
 
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
-{
+bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) {
     // based on Melange sample code (C4DImportExport.cpp)
     while(shader) {
         if(shader->GetType() == Xlayer) {
@@ -220,15 +217,12 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
             // Ignore the actual layer blending - models for real-time rendering should not
             // use them in a non-trivial way. Just try to find textures that we can apply
             // to the model.
-            while (lsl)
-            {
-                if (lsl->GetType() == TypeFolder)
-                {
+            while (lsl) {
+                if (lsl->GetType() == TypeFolder) {
                     BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl);
                     LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
 
-                    while (subLsl)
-                    {
+                    while (subLsl) {
                         if (subLsl->GetType() == TypeShader) {
                             BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl);
                             if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
@@ -238,8 +232,7 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
 
                         subLsl = subLsl->GetNext();
                     }
-                }
-                else if (lsl->GetType() == TypeShader) {
+                } else if (lsl->GetType() == TypeShader) {
                     BlendShader* const shader = dynamic_cast<BlendShader*>(lsl);
                     if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
                         return true;
@@ -248,33 +241,27 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
 
                 lsl = lsl->GetNext();
             }
-        }
-        else if ( shader->GetType() == Xbitmap )
-        {
+        } else if ( shader->GetType() == Xbitmap ) {
             aiString path;
             shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
             path.length = ::strlen(path.data);
             out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
             return true;
-        }
-        else {
+        } else {
             LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
         }
         shader = shader->GetNext();
     }
+
     return false;
 }
 
-
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
-{
+void C4DImporter::ReadMaterials(melange::BaseMaterial* mat) {
     // based on Melange sample code
-    while (mat)
-    {
+    while (mat) {
         const String& name = mat->GetName();
-        if (mat->GetType() == Mmaterial)
-        {
+        if (mat->GetType() == Mmaterial) {
             aiMaterial* out = new aiMaterial();
             material_mapping[mat] = static_cast<unsigned int>(materials.size());
             materials.push_back(out);
@@ -286,8 +273,7 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
 
             Material& m = dynamic_cast<Material&>(*mat);
 
-            if (m.GetChannelState(CHANNEL_COLOR))
-            {
+            if (m.GetChannelState(CHANNEL_COLOR)) {
                 GeData data;
                 mat->GetParameter(MATERIAL_COLOR_COLOR, data);
                 Vector color = data.GetVector();
@@ -307,9 +293,7 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
             if(shader) {
                 ReadShader(out, shader);
             }
-        }
-        else
-        {
+        } else {
             LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
         }
         mat = mat->GetNext();
@@ -317,14 +301,12 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
 }
 
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
-{
-    ai_assert(parent != NULL);
+void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) {
+    ai_assert(parent != nullptr );
     std::vector<aiNode*> nodes;
 
     // based on Melange sample code
-    while (object)
-    {
+    while (object) {
         const String& name = object->GetName();
         const LONG type = object->GetType();
         const Matrix& ml = object->GetMl();
@@ -356,26 +338,20 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
         nodes.push_back(nd);
 
         GeData data;
-        if (type == Ocamera)
-        {
+        if (type == Ocamera) {
             object->GetParameter(CAMERAOBJECT_FOV, data);
             // TODO: read camera
-        }
-        else if (type == Olight)
-        {
+        } else if (type == Olight) {
             // TODO: read light
-        }
-        else if (type == Opolygon)
-        {
+        } else if (type == Opolygon) {
             aiMesh* const mesh = ReadMesh(object);
-            if(mesh != NULL) {
+            if(mesh != nullptr) {
                 nd->mNumMeshes = 1;
                 nd->mMeshes = new unsigned int[1];
                 nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
                 meshes.push_back(mesh);
             }
-        }
-        else {
+        } else {
             LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
         }
 
@@ -389,28 +365,27 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
     std::copy(nodes.begin(), nodes.end(), parent->mChildren);
 }
 
-
 // ------------------------------------------------------------------------------------------------
-aiMesh* C4DImporter::ReadMesh(BaseObject* object)
-{
-    ai_assert(object != NULL && object->GetType() == Opolygon);
+aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
+    ai_assert(object != nullptr);
+    ai_assert( object->GetType() == Opolygon );
 
     // based on Melange sample code
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
-    ai_assert(polyObject != NULL);
+    ai_assert(polyObject != nullptr);
 
     const LONG pointCount = polyObject->GetPointCount();
     const LONG polyCount = polyObject->GetPolygonCount();
     if(!polyObject || !pointCount) {
         LogWarn("ignoring mesh with zero vertices or faces");
-        return NULL;
+        return nullptr;
     }
 
     const Vector* points = polyObject->GetPointR();
-    ai_assert(points != NULL);
+    ai_assert(points != nullptr);
 
     const CPolygon* polys = polyObject->GetPolygonR();
-    ai_assert(polys != NULL);
+    ai_assert(polys != nullptr);
 
     std::unique_ptr<aiMesh> mesh(new aiMesh());
     mesh->mNumFaces = static_cast<unsigned int>(polyCount);
@@ -443,14 +418,14 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
 
     // check if there are normals, tangents or UVW coordinates
     BaseTag* tag = object->GetTag(Tnormal);
-    NormalTag* normals_src = NULL;
+    NormalTag* normals_src = nullptr;
     if(tag) {
         normals_src = dynamic_cast<NormalTag*>(tag);
         normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
     }
 
     tag = object->GetTag(Ttangent);
-    TangentTag* tangents_src = NULL;
+    TangentTag* tangents_src = nullptr;
     if(tag) {
         tangents_src = dynamic_cast<TangentTag*>(tag);
         tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
@@ -458,15 +433,14 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     }
 
     tag = object->GetTag(Tuvw);
-    UVWTag* uvs_src = NULL;
+    UVWTag* uvs_src = nullptr;
     if(tag) {
         uvs_src = dynamic_cast<UVWTag*>(tag);
         uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
     }
 
     // copy vertices and extra channels over and populate faces
-    for (LONG i = 0; i < polyCount; ++i, ++face)
-    {
+    for (LONG i = 0; i < polyCount; ++i, ++face) {
         ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
         const Vector& pointA = points[polys[i].a];
         verts->x = pointA.x;
@@ -489,8 +463,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
         ++verts;
 
         // TODO: do we also need to handle lines or points with similar checks?
-        if (polys[i].c != polys[i].d)
-        {
+        if (polys[i].c != polys[i].d) {
             ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
 
             face->mNumIndices = 4;
@@ -500,8 +473,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
             verts->y = pointD.y;
             verts->z = pointD.z;
             ++verts;
-        }
-        else {
+        } else {
             face->mNumIndices = 3;
         }
         face->mIndices = new unsigned int[face->mNumIndices];
@@ -513,8 +485,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
         if (normals_src) {
             if(i >= normals_src->GetDataCount()) {
                 LogError("unexpected number of normals, ignoring");
-            }
-            else {
+            } else {
                 ConstNormalHandle normal_handle = normals_src->GetDataAddressR();
                 NormalStruct nor;
                 NormalTag::Get(normal_handle, i, nor);
@@ -616,26 +587,25 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     }
 
     mesh->mMaterialIndex = ResolveMaterial(polyObject);
+
     return mesh.release();
 }
 
-
 // ------------------------------------------------------------------------------------------------
-unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
-{
-    ai_assert(obj != NULL);
+unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj) {
+    ai_assert(obj != nullptr);
 
     const unsigned int mat_count = static_cast<unsigned int>(materials.size());
 
     BaseTag* tag = obj->GetTag(Ttexture);
-    if(tag == NULL) {
+    if(tag == nullptr) {
         return mat_count;
     }
 
     TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
 
     BaseMaterial* const mat = ttag.GetMaterial();
-    ai_assert(mat != NULL);
+    ai_assert(mat != nullptr);
 
     const MaterialMap::const_iterator it = material_mapping.find(mat);
     if(it == material_mapping.end()) {
@@ -643,6 +613,7 @@ unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
     }
 
     ai_assert((*it).second < mat_count);
+
     return (*it).second;
 }
 

+ 6 - 12
code/C4DImporter.h → code/C4D/C4DImporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2019, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/LogAux.h>
 
 #include <map>
+
+// Forward declarations
 struct aiNode;
 struct aiMesh;
 struct aiMaterial;
@@ -61,8 +63,7 @@ namespace melange {
     class BaseShader;
 }
 
-namespace Assimp    {
-
+namespace Assimp  {
     // TinyFormatter.h
     namespace Formatter {
         template <typename T,typename TR, typename A> class basic_formatter;
@@ -75,17 +76,10 @@ namespace Assimp    {
  *
  *  Note that Melange is not free software. */
 // -------------------------------------------------------------------------------------------
-class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter>
-{
+class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter> {
 public:
-
     C4DImporter();
     ~C4DImporter();
-
-
-public:
-
-    // --------------------
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
         bool checkSig) const;
 
@@ -119,5 +113,5 @@ private:
 }; // !class C4DImporter
 
 } // end of namespace Assimp
-#endif // INCLUDED_AI_CINEMA_4D_LOADER_H
 
+#endif // INCLUDED_AI_CINEMA_4D_LOADER_H

+ 2 - 3
code/AssimpCExport.cpp → code/CApi/AssimpCExport.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -49,7 +49,7 @@ Assimp C export interface. See Exporter.cpp for some notes.
 
 #include "CInterfaceIOWrapper.h"
 #include <assimp/SceneCombiner.h>
-#include "ScenePrivate.h"
+#include "Common/ScenePrivate.h"
 #include <assimp/Exporter.hpp>
 
 using namespace Assimp;
@@ -60,7 +60,6 @@ ASSIMP_API size_t aiGetExportFormatCount(void)
     return Exporter().GetExportFormatCount();
 }
 
-
 // ------------------------------------------------------------------------------------------------
 ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t index)
 {

+ 1 - 1
code/CInterfaceIOWrapper.cpp → code/CApi/CInterfaceIOWrapper.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 

+ 1 - 1
code/CInterfaceIOWrapper.h → code/CApi/CInterfaceIOWrapper.h

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 677 - 467
code/CMakeLists.txt


+ 45 - 93
code/COBLoader.cpp → code/COB/COBLoader.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 All rights reserved.
 
@@ -45,21 +45,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
-#include "COBLoader.h"
-#include "COBScene.h"
+#include "COB/COBLoader.h"
+#include "COB/COBScene.h"
+#include "PostProcessing/ConvertToLHProcess.h"
 
 #include <assimp/StreamReader.h>
 #include <assimp/ParsingUtils.h>
 #include <assimp/fast_atof.h>
-
 #include <assimp/LineSplitter.h>
 #include <assimp/TinyFormatter.h>
-#include <memory>
 #include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/scene.h>
 #include <assimp/importerdesc.h>
 
+#include <memory>
+
 using namespace Assimp;
 using namespace Assimp::COB;
 using namespace Assimp::Formatter;
@@ -105,7 +106,7 @@ COBImporter::~COBImporter()
 bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
 {
     const std::string& extension = GetExtension(pFile);
-    if (extension == "cob" || extension == "scn") {
+    if (extension == "cob" || extension == "scn" || extension == "COB" || extension == "SCN") {
         return true;
     }
 
@@ -138,20 +139,18 @@ void COBImporter::SetupProperties(const Importer* /*pImp*/)
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void COBImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+void COBImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
     COB::Scene scene;
     std::unique_ptr<StreamReaderLE> stream(new StreamReaderLE( pIOHandler->Open(pFile,"rb")) );
 
     // check header
     char head[32];
     stream->CopyAndAdvance(head,32);
-    if (strncmp(head,"Caligari ",9)) {
+    if (strncmp(head,"Caligari ",9) != 0) {
         ThrowException("Could not found magic id: `Caligari`");
     }
 
-    DefaultLogger::get()->info("File format tag: "+std::string(head+9,6));
+    ASSIMP_LOG_INFO_F("File format tag: ",std::string(head+9,6));
     if (head[16]!='L') {
         ThrowException("File is big-endian, which is not supported");
     }
@@ -225,6 +224,9 @@ void COBImporter::InternReadFile( const std::string& pFile,
     }
 
     pScene->mRootNode = BuildNodes(*root.get(),scene,pScene);
+	//flip normals after import
+    FlipWindingOrderProcess flip;
+    flip.Execute( pScene );
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -301,7 +303,7 @@ aiNode* COBImporter::BuildNodes(const Node& root,const Scene& scin,aiScene* fill
                     }
                     std::unique_ptr<const Material> defmat;
                     if(!min) {
-                        DefaultLogger::get()->debug(format()<<"Could not resolve material index "
+                        ASSIMP_LOG_DEBUG(format()<<"Could not resolve material index "
                             <<reflist.first<<" - creating default material for this slot");
 
                         defmat.reset(min=new Material());
@@ -473,7 +475,7 @@ void COBImporter::UnsupportedChunk_Ascii(LineSplitter& splitter, const ChunkInfo
 
     // we can recover if the chunk size was specified.
     if(nfo.size != static_cast<unsigned int>(-1)) {
-        DefaultLogger::get()->error(error);
+        ASSIMP_LOG_ERROR(error);
 
         // (HACK) - our current position in the stream is the beginning of the
         // head line of the next chunk. That's fine, but the caller is going
@@ -485,46 +487,6 @@ void COBImporter::UnsupportedChunk_Ascii(LineSplitter& splitter, const ChunkInfo
     else ThrowException(error);
 }
 
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogWarn_Ascii(const LineSplitter& splitter, const format& message)    {
-    LogWarn_Ascii(message << " [at line "<< splitter.get_index()<<"]");
-}
-
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogError_Ascii(const LineSplitter& splitter, const format& message)   {
-    LogError_Ascii(message << " [at line "<< splitter.get_index()<<"]");
-}
-
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogInfo_Ascii(const LineSplitter& splitter, const format& message)    {
-    LogInfo_Ascii(message << " [at line "<< splitter.get_index()<<"]");
-}
-
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogDebug_Ascii(const LineSplitter& splitter, const format& message)   {
-    LogDebug_Ascii(message << " [at line "<< splitter.get_index()<<"]");
-}
-
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogWarn_Ascii(const Formatter::format& message)   {
-    DefaultLogger::get()->warn(std::string("COB: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogError_Ascii(const Formatter::format& message)  {
-    DefaultLogger::get()->error(std::string("COB: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogInfo_Ascii(const Formatter::format& message)   {
-    DefaultLogger::get()->info(std::string("COB: ")+=message);
-}
-
-// ------------------------------------------------------------------------------------------------
-void COBImporter::LogDebug_Ascii(const Formatter::format& message)  {
-    DefaultLogger::get()->debug(std::string("COB: ")+=message);
-}
-
 // ------------------------------------------------------------------------------------------------
 void COBImporter::ReadBasicNodeInfo_Ascii(Node& msh, LineSplitter& splitter, const ChunkInfo& /*nfo*/)
 {
@@ -574,8 +536,7 @@ void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const Chunk
 
     ++splitter;
     if (!splitter.match_start("mat# ")) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
+        ASSIMP_LOG_WARN_F( "Expected `mat#` line in `Mat1` chunk ", nfo.id );
         return;
     }
 
@@ -587,8 +548,7 @@ void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const Chunk
     ++splitter;
 
     if (!splitter.match_start("shader: ")) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
+        ASSIMP_LOG_WARN_F( "Expected `mat#` line in `Mat1` chunk ", nfo.id);
         return;
     }
     std::string shader = std::string(splitter[1]);
@@ -601,14 +561,12 @@ void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const Chunk
         mat.shader = Material::PHONG;
     }
     else if (shader != "flat") {
-        LogWarn_Ascii(splitter,format()<<
-            "Unknown value for `shader` in `Mat1` chunk "<<nfo.id);
+        ASSIMP_LOG_WARN_F( "Unknown value for `shader` in `Mat1` chunk ", nfo.id );
     }
 
     ++splitter;
     if (!splitter.match_start("rgb ")) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `rgb` line in `Mat1` chunk "<<nfo.id);
+        ASSIMP_LOG_WARN_F( "Expected `rgb` line in `Mat1` chunk ", nfo.id);
     }
 
     const char* rgb = splitter[1];
@@ -616,8 +574,7 @@ void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const Chunk
 
     ++splitter;
     if (!splitter.match_start("alpha ")) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `alpha` line in `Mat1` chunk "<<nfo.id);
+        ASSIMP_LOG_WARN_F( "Expected `alpha` line in `Mat1` chunk ", nfo.id);
     }
 
     const char* tokens[10];
@@ -638,8 +595,7 @@ void COBImporter::ReadUnit_Ascii(Scene& out, LineSplitter& splitter, const Chunk
     }
     ++splitter;
     if (!splitter.match_start("Units ")) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `Units` line in `Unit` chunk "<<nfo.id);
+        ASSIMP_LOG_WARN_F( "Expected `Units` line in `Unit` chunk ", nfo.id);
         return;
     }
 
@@ -650,13 +606,12 @@ void COBImporter::ReadUnit_Ascii(Scene& out, LineSplitter& splitter, const Chunk
             const unsigned int t=strtoul10(splitter[1]);
 
             nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
-                LogWarn_Ascii(splitter,format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
+                ASSIMP_LOG_WARN_F(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id)
                 ,1.f):units[t];
             return;
         }
     }
-    LogWarn_Ascii(splitter,format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
-        <<nfo.parent_id<<" which does not exist");
+    ASSIMP_LOG_WARN_F( "`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist");
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -690,32 +645,28 @@ void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const Chunk
         msh.ltype = Light::SPOT;
     }
     else {
-        LogWarn_Ascii(splitter,format()<<
-            "Unknown kind of light source in `Lght` chunk "<<nfo.id<<" : "<<*splitter);
+        ASSIMP_LOG_WARN_F( "Unknown kind of light source in `Lght` chunk ", nfo.id, " : ", *splitter );
         msh.ltype = Light::SPOT;
     }
 
     ++splitter;
     if (!splitter.match_start("color ")) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `color` line in `Lght` chunk "<<nfo.id);
+        ASSIMP_LOG_WARN_F( "Expected `color` line in `Lght` chunk ", nfo.id );
     }
 
     const char* rgb = splitter[1];
     ReadFloat3Tuple_Ascii(msh.color ,&rgb);
 
     SkipSpaces(&rgb);
-    if (strncmp(rgb,"cone angle",10)) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `cone angle` entity in `color` line in `Lght` chunk "<<nfo.id);
+    if (strncmp(rgb,"cone angle",10) != 0) {
+        ASSIMP_LOG_WARN_F( "Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id );
     }
     SkipSpaces(rgb+10,&rgb);
     msh.angle = fast_atof(&rgb);
 
     SkipSpaces(&rgb);
-    if (strncmp(rgb,"inner angle",11)) {
-        LogWarn_Ascii(splitter,format()<<
-            "Expected `inner angle` entity in `color` line in `Lght` chunk "<<nfo.id);
+    if (strncmp(rgb,"inner angle",11) != 0) {
+        ASSIMP_LOG_WARN_F( "Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
     }
     SkipSpaces(rgb+11,&rgb);
     msh.inner_angle = fast_atof(&rgb);
@@ -826,7 +777,7 @@ void COBImporter::ReadPolH_Ascii(Scene& out, LineSplitter& splitter, const Chunk
 
             for(unsigned int cur = 0; cur < cnt && ++splitter ;++cur) {
                 if (splitter.match_start("Hole")) {
-                    LogWarn_Ascii(splitter,"Skipping unsupported `Hole` line");
+                    ASSIMP_LOG_WARN( "Skipping unsupported `Hole` line" );
                     continue;
                 }
 
@@ -886,7 +837,7 @@ void COBImporter::ReadBitM_Ascii(Scene& /*out*/, LineSplitter& splitter, const C
 
     const unsigned int head = strtoul10((++splitter)[1]);
     if (head != sizeof(Bitmap::BitmapHeader)) {
-        LogWarn_Ascii(splitter,"Unexpected ThumbNailHdrSize, skipping this chunk");
+        ASSIMP_LOG_WARN("Unexpected ThumbNailHdrSize, skipping this chunk");
         return;
     }
 
@@ -933,7 +884,7 @@ void COBImporter::UnsupportedChunk_Binary( StreamReaderLE& reader, const ChunkIn
 
     // we can recover if the chunk size was specified.
     if(nfo.size != static_cast<unsigned int>(-1)) {
-        DefaultLogger::get()->error(error);
+        ASSIMP_LOG_ERROR(error);
         reader.IncPtr(nfo.size);
     }
     else ThrowException(error);
@@ -947,6 +898,7 @@ public:
     : nfo(nfo)
     , reader(reader)
     , cur(reader.GetCurrentPos()) {
+        // empty
     }
 
     ~chunk_guard() {
@@ -954,7 +906,7 @@ public:
         if(nfo.size != static_cast<unsigned int>(-1)) {
             try {
                 reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
-            } catch ( DeadlyImportError e ) {
+            } catch (const DeadlyImportError& ) {
                 // out of limit so correct the value
                 reader.IncPtr( reader.GetReadLimit() );
             }
@@ -962,15 +914,17 @@ public:
     }
 
 private:
-
     const COB::ChunkInfo& nfo;
     StreamReaderLE& reader;
     long cur;
 };
 
 // ------------------------------------------------------------------------------------------------
-void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader)
-{
+void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader) {
+    if (nullptr == reader) {
+        return;
+    }
+
     while(1) {
         std::string type;
          type += reader -> GetI1()
@@ -1140,7 +1094,7 @@ void COBImporter::ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const
             mat.type = Material::METAL;
             break;
         default:
-            LogError_Ascii(format("Unrecognized shader type in `Mat1` chunk with id ")<<nfo.id);
+            ASSIMP_LOG_ERROR_F( "Unrecognized shader type in `Mat1` chunk with id ", nfo.id );
             mat.type = Material::FLAT;
     }
 
@@ -1155,7 +1109,7 @@ void COBImporter::ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const
             mat.autofacet = Material::SMOOTH;
             break;
         default:
-            LogError_Ascii(format("Unrecognized faceting mode in `Mat1` chunk with id ")<<nfo.id);
+            ASSIMP_LOG_ERROR_F( "Unrecognized faceting mode in `Mat1` chunk with id ", nfo.id );
             mat.autofacet = Material::FACETED;
     }
     mat.autofacet_angle = static_cast<float>(reader.GetI1());
@@ -1265,7 +1219,7 @@ void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const
 
     const chunk_guard cn(nfo,reader);
 
-    out.nodes.push_back(std::shared_ptr<Group>(new Group()));
+    out.nodes.push_back(std::make_shared<Group>());
     Group& msh = (Group&)(*out.nodes.back().get());
     msh = nfo;
 
@@ -1287,15 +1241,13 @@ void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const
         if (nd->id == nfo.parent_id) {
             const unsigned int t=reader.GetI2();
             nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
-                LogWarn_Ascii(format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
+                ASSIMP_LOG_WARN_F(t," is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id)
                 ,1.f):units[t];
 
             return;
         }
     }
-    LogWarn_Ascii(format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
-        <<nfo.parent_id<<" which does not exist");
+    ASSIMP_LOG_WARN_F( "`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist");
 }
 
-
-#endif
+#endif // ASSIMP_BUILD_NO_COB_IMPORTER

+ 2 - 22
code/COBLoader.h → code/COB/COBLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -77,10 +77,7 @@ class COBImporter : public BaseImporter
 public:
     COBImporter();
     ~COBImporter();
-
-
-public:
-
+    
     // --------------------
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
         bool checkSig) const;
@@ -115,15 +112,11 @@ private:
      *  @param stream Stream to read from.  */
     void ReadBinaryFile(COB::Scene& out, StreamReaderLE* stream);
 
-
-private:
-
     // Conversion to Assimp output format
 
     aiNode* BuildNodes(const COB::Node& root,const COB::Scene& scin,aiScene* fill);
 
 private:
-
     // ASCII file support
 
     void UnsupportedChunk_Ascii(LineSplitter& splitter, const COB::ChunkInfo& nfo, const char* name);
@@ -142,19 +135,6 @@ private:
     void ReadChan_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
 
 
-    // ASCII file logging stuff to add proper line numbers to messages
-
-    static void LogWarn_Ascii (const LineSplitter& splitter, const Formatter::format& message);
-    static void LogError_Ascii(const LineSplitter& splitter, const Formatter::format& message);
-    static void LogInfo_Ascii (const LineSplitter& splitter, const Formatter::format& message);
-    static void LogDebug_Ascii(const LineSplitter& splitter, const Formatter::format& message);
-
-    static void LogWarn_Ascii  (const Formatter::format& message);
-    static void LogError_Ascii (const Formatter::format& message);
-    static void LogInfo_Ascii  (const Formatter::format& message);
-    static void LogDebug_Ascii (const Formatter::format& message);
-
-
     // Binary file support
 
     void UnsupportedChunk_Binary(StreamReaderLE& reader, const COB::ChunkInfo& nfo, const char* name);

+ 1 - 1
code/COBScene.h → code/COB/COBScene.h

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

+ 2 - 2
code/CSMLoader.cpp → code/CSM/CSMLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 
@@ -233,7 +233,7 @@ void CSMImporter::InternReadFile( const std::string& pFile,
 
                         if (TokenMatchI(buffer, "DROPOUT", 7))  {
                             // seems this is invalid marker data; at least the doc says it's possible
-                            DefaultLogger::get()->warn("CSM: Encountered invalid marker data (DROPOUT)");
+                            ASSIMP_LOG_WARN("CSM: Encountered invalid marker data (DROPOUT)");
                         }
                         else    {
                             aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;

+ 1 - 1
code/CSMLoader.h → code/CSM/CSMLoader.h

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

+ 190 - 115
code/ColladaExporter.cpp → code/Collada/ColladaExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "ColladaExporter.h"
 #include <assimp/Bitmap.h>
+#include <assimp/MathFunctions.h>
 #include <assimp/fast_atof.h>
 #include <assimp/SceneCombiner.h>
 #include <assimp/StringUtils.h>
@@ -64,13 +65,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
-namespace Assimp
-{
+namespace Assimp {
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
-void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
-{
+void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
     std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
     std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
 
@@ -93,15 +92,45 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
 
 } // end of namespace Assimp
 
+// ------------------------------------------------------------------------------------------------
+// Encodes a string into a valid XML ID using the xsd:ID schema qualifications.
+static const std::string XMLIDEncode(const std::string& name) {
+    const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
+    const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char);
+
+    if (name.length() == 0) {
+        return name;
+    }
+
+    std::stringstream idEncoded;
+
+    // xsd:ID must start with letter or underscore
+    if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) {
+        idEncoded << '_';
+    }
+
+    for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) {
+        // xsd:ID can only contain letters, digits, underscores, hyphens and periods
+        if (strchr(XML_ID_CHARS, *it) != nullptr) {
+            idEncoded << *it;
+        } else {
+            // Select placeholder character based on invalid character to prevent name collisions 
+            idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT];
+        }
+    }
 
+    return idEncoded.str();
+}
 
 // ------------------------------------------------------------------------------------------------
 // Constructor for a specific scene to export
-ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file)
-{
+ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) 
+: mIOSystem(pIOSystem)
+, mPath(path)
+, mFile(file) {
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     mOutput.imbue( std::locale("C") );
-    mOutput.precision(16);
+    mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
 
     mScene = pScene;
     mSceneOwned = false;
@@ -115,17 +144,15 @@ ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, co
 
 // ------------------------------------------------------------------------------------------------
 // Destructor
-ColladaExporter::~ColladaExporter()
-{
-    if(mSceneOwned) {
+ColladaExporter::~ColladaExporter() {
+    if ( mSceneOwned ) {
         delete mScene;
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Starts writing the contents
-void ColladaExporter::WriteFile()
-{
+void ColladaExporter::WriteFile() {
     // write the DTD
     mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
     // COLLADA element start
@@ -149,7 +176,7 @@ void ColladaExporter::WriteFile()
     // useless Collada fu at the end, just in case we haven't had enough indirections, yet.
     mOutput << startstr << "<scene>" << endstr;
     PushTag();
-    mOutput << startstr << "<instance_visual_scene url=\"#" + XMLEscape(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
+    mOutput << startstr << "<instance_visual_scene url=\"#" + XMLIDEncode(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
     PopTag();
     mOutput << startstr << "</scene>" << endstr;
     PopTag();
@@ -158,9 +185,8 @@ void ColladaExporter::WriteFile()
 
 // ------------------------------------------------------------------------------------------------
 // Writes the asset header
-void ColladaExporter::WriteHeader()
-{
-    static const ai_real epsilon = ai_real( 0.00001 );
+void ColladaExporter::WriteHeader() {
+    static const ai_real epsilon = Math::getEpsilon<ai_real>();
     static const aiQuaternion x_rot(aiMatrix3x3(
         0, -1,  0,
         1,  0,  0,
@@ -238,25 +264,64 @@ void ColladaExporter::WriteHeader()
     mOutput << startstr << "<contributor>" << endstr;
     PushTag();
 
-    aiMetadata* meta = mScene->mRootNode->mMetaData;
+    // If no Scene metadata, use root node metadata
+    aiMetadata* meta = mScene->mMetaData;
+    if (nullptr == meta) {
+        meta = mScene->mRootNode->mMetaData;
+    }
+
     aiString value;
-    if (!meta || !meta->Get("Author", value))
+    if (!meta || !meta->Get("Author", value)) {
         mOutput << startstr << "<author>" << "Assimp" << "</author>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
+    }
 
-    if (!meta || !meta->Get("AuthoringTool", value))
+    if (nullptr == meta || !meta->Get("AuthoringTool", value)) {
         mOutput << startstr << "<authoring_tool>" << "Assimp Exporter" << "</authoring_tool>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
+    }
 
-    //mOutput << startstr << "<author>" << mScene->author.C_Str() << "</author>" << endstr;
-    //mOutput << startstr << "<authoring_tool>" << mScene->authoringTool.C_Str() << "</authoring_tool>" << endstr;
+    if (meta) {
+        if (meta->Get("Comments", value)) {
+            mOutput << startstr << "<comments>" << XMLEscape(value.C_Str()) << "</comments>" << endstr;
+        }
+        if (meta->Get("Copyright", value)) {
+            mOutput << startstr << "<copyright>" << XMLEscape(value.C_Str()) << "</copyright>" << endstr;
+        }
+        if (meta->Get("SourceData", value)) {
+            mOutput << startstr << "<source_data>" << XMLEscape(value.C_Str()) << "</source_data>" << endstr;
+        }
+    }
 
     PopTag();
     mOutput << startstr << "</contributor>" << endstr;
-    mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+
+    if (nullptr == meta || !meta->Get("Created", value)) {
+        mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+    } else {
+        mOutput << startstr << "<created>" << XMLEscape(value.C_Str()) << "</created>" << endstr;
+    }
+
+    // Modified date is always the date saved
     mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
+
+    if (meta) {
+        if (meta->Get("Keywords", value)) {
+            mOutput << startstr << "<keywords>" << XMLEscape(value.C_Str()) << "</keywords>" << endstr;
+        }
+        if (meta->Get("Revision", value)) {
+            mOutput << startstr << "<revision>" << XMLEscape(value.C_Str()) << "</revision>" << endstr;
+        }
+        if (meta->Get("Subject", value)) {
+            mOutput << startstr << "<subject>" << XMLEscape(value.C_Str()) << "</subject>" << endstr;
+        }
+        if (meta->Get("Title", value)) {
+            mOutput << startstr << "<title>" << XMLEscape(value.C_Str()) << "</title>" << endstr;
+        }
+    }
+
     mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
     mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
     PopTag();
@@ -269,18 +334,21 @@ void ColladaExporter::WriteTextures() {
     static const unsigned int buffer_size = 1024;
     char str[buffer_size];
 
-    if(mScene->HasTextures()) {
+    if (mScene->HasTextures()) {
         for(unsigned int i = 0; i < mScene->mNumTextures; i++) {
             // It would be great to be able to create a directory in portable standard C++, but it's not the case,
             // so we just write the textures in the current directory.
 
             aiTexture* texture = mScene->mTextures[i];
+            if ( nullptr == texture ) {
+                continue;
+            }
 
             ASSIMP_itoa10(str, buffer_size, i + 1);
 
             std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char*) texture->achFormatHint);
 
-            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + name, "wb"));
+            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
             if(outfile == NULL) {
                 throw DeadlyExportError("could not open output texture file: " + mPath + name);
             }
@@ -318,9 +386,10 @@ void ColladaExporter::WriteCamerasLibrary() {
 void ColladaExporter::WriteCamera(size_t pIndex){
 
     const aiCamera *cam = mScene->mCameras[pIndex];
-    const std::string idstrEscaped = XMLEscape(cam->mName.C_Str());
+    const std::string cameraName = XMLEscape(cam->mName.C_Str());
+    const std::string cameraId = XMLIDEncode(cam->mName.C_Str());
 
-    mOutput << startstr << "<camera id=\"" << idstrEscaped << "-camera\" name=\"" << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<camera id=\"" << cameraId << "-camera\" name=\"" << cameraName << "\" >" << endstr;
     PushTag();
     mOutput << startstr << "<optics>" << endstr;
     PushTag();
@@ -374,10 +443,11 @@ void ColladaExporter::WriteLightsLibrary() {
 void ColladaExporter::WriteLight(size_t pIndex){
 
     const aiLight *light = mScene->mLights[pIndex];
-    const std::string idstrEscaped = XMLEscape(light->mName.C_Str());
+    const std::string lightName = XMLEscape(light->mName.C_Str());
+    const std::string lightId = XMLIDEncode(light->mName.C_Str());
 
-    mOutput << startstr << "<light id=\"" << idstrEscaped << "-light\" name=\""
-            << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<light id=\"" << lightId << "-light\" name=\""
+            << lightName << "\" >" << endstr;
     PushTag();
     mOutput << startstr << "<technique_common>" << endstr;
     PushTag();
@@ -428,6 +498,7 @@ void ColladaExporter::WritePointLight(const aiLight *const light){
     mOutput << startstr << "</point>" << endstr;
 
 }
+
 void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     const aiColor3D &color=  light->mColorDiffuse;
     mOutput << startstr << "<directional>" << endstr;
@@ -440,6 +511,7 @@ void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     mOutput << startstr << "</directional>" << endstr;
 
 }
+
 void ColladaExporter::WriteSpotLight(const aiLight *const light){
 
     const aiColor3D &color=  light->mColorDiffuse;
@@ -496,18 +568,16 @@ void ColladaExporter::WriteAmbienttLight(const aiLight *const light){
 
 // ------------------------------------------------------------------------------------------------
 // Reads a single surface entry from the given material keys
-void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex)
-{
-  if( pSrcMat->GetTextureCount( pTexture) > 0 )
-  {
+void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, 
+                                          aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex) {
+  if( pSrcMat->GetTextureCount( pTexture) > 0 ) {
     aiString texfile;
     unsigned int uvChannel = 0;
     pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel);
 
     std::string index_str(texfile.C_Str());
 
-    if(index_str.size() != 0 && index_str[0] == '*')
-    {
+    if(index_str.size() != 0 && index_str[0] == '*') {
         unsigned int index;
 
         index_str = index_str.substr(1, std::string::npos);
@@ -525,15 +595,13 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
         } else {
             throw DeadlyExportError("could not find embedded texture at index " + index_str);
         }
-    } else
-    {
+    } else {
         poSurface.texture = texfile.C_Str();
     }
 
     poSurface.channel = uvChannel;
     poSurface.exist = true;
-  } else
-  {
+  } else {
     if( pKey )
       poSurface.exist = pSrcMat->Get( pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
   }
@@ -541,18 +609,16 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
 
 // ------------------------------------------------------------------------------------------------
 // Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
-static bool isalnum_C(char c)
-{
+static bool isalnum_C(char c) {
   return ( nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",c) );
 }
 
 // ------------------------------------------------------------------------------------------------
 // Writes an image entry for the given surface
-void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd)
-{
+void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) {
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
+    mOutput << startstr << "<image id=\"" << XMLIDEncode(pNameAdd) << "\">" << endstr;
     PushTag();
     mOutput << startstr << "<init_from>";
 
@@ -585,7 +651,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std
     }
     else
     {
-      mOutput << startstr << "<texture texture=\"" << XMLEscape(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+      mOutput << startstr << "<texture texture=\"" << XMLIDEncode(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
     }
     PopTag();
     mOutput << startstr << "</" << pTypeName << ">" << endstr;
@@ -599,21 +665,21 @@ void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std
   // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
     PushTag();
     mOutput << startstr << "<surface type=\"2D\">" << endstr;
     PushTag();
-    mOutput << startstr << "<init_from>" << XMLEscape(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
+    mOutput << startstr << "<init_from>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
     PopTag();
     mOutput << startstr << "</surface>" << endstr;
     PopTag();
     mOutput << startstr << "</newparam>" << endstr;
 
-    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
     PushTag();
     mOutput << startstr << "<sampler2D>" << endstr;
     PushTag();
-    mOutput << startstr << "<source>" << XMLEscape(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
+    mOutput << startstr << "<source>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
     PopTag();
     mOutput << startstr << "</sampler2D>" << endstr;
     PopTag();
@@ -665,11 +731,6 @@ void ColladaExporter::WriteMaterials()
         materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName);
       }
     }
-    for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) {
-      if( !isalnum_C( *it ) ) {
-        *it = '_';
-      }
-    }
 
     aiShadingMode shading = aiShadingMode_Flat;
     materials[a].shading_model = "phong";
@@ -734,7 +795,7 @@ void ColladaExporter::WriteMaterials()
     {
       const Material& mat = *it;
       // this is so ridiculous it must be right
-      mOutput << startstr << "<effect id=\"" << XMLEscape(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
+      mOutput << startstr << "<effect id=\"" << XMLIDEncode(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
       PushTag();
       mOutput << startstr << "<profile_COMMON>" << endstr;
       PushTag();
@@ -785,9 +846,9 @@ void ColladaExporter::WriteMaterials()
     for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
     {
       const Material& mat = *it;
-      mOutput << startstr << "<material id=\"" << XMLEscape(mat.name) << "\" name=\"" << mat.name << "\">" << endstr;
+      mOutput << startstr << "<material id=\"" << XMLIDEncode(mat.name) << "\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
       PushTag();
-      mOutput << startstr << "<instance_effect url=\"#" << XMLEscape(mat.name) << "-fx\"/>" << endstr;
+      mOutput << startstr << "<instance_effect url=\"#" << XMLIDEncode(mat.name) << "-fx\"/>" << endstr;
       PopTag();
       mOutput << startstr << "</material>" << endstr;
     }
@@ -803,8 +864,9 @@ void ColladaExporter::WriteControllerLibrary()
     mOutput << startstr << "<library_controllers>" << endstr;
     PushTag();
     
-    for( size_t a = 0; a < mScene->mNumMeshes; ++a)
+    for( size_t a = 0; a < mScene->mNumMeshes; ++a) {
         WriteController( a);
+    }
 
     PopTag();
     mOutput << startstr << "</library_controllers>" << endstr;
@@ -815,8 +877,8 @@ void ColladaExporter::WriteControllerLibrary()
 void ColladaExporter::WriteController( size_t pIndex)
 {
     const aiMesh* mesh = mScene->mMeshes[pIndex];
-    const std::string idstr = GetMeshId( pIndex);
-    const std::string idstrEscaped = XMLEscape(idstr);
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string idstrEscaped = XMLIDEncode(idstr);
 
     if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
         return;
@@ -851,7 +913,7 @@ void ColladaExporter::WriteController( size_t pIndex)
     mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
 
     for( size_t i = 0; i < mesh->mNumBones; ++i )
-        mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " ";
+        mOutput << XMLIDEncode(mesh->mBones[i]->mName.C_Str()) << " ";
 
     mOutput << "</Name_array>" << endstr;
 
@@ -986,14 +1048,15 @@ void ColladaExporter::WriteGeometryLibrary()
 void ColladaExporter::WriteGeometry( size_t pIndex)
 {
     const aiMesh* mesh = mScene->mMeshes[pIndex];
-    const std::string idstr = GetMeshId( pIndex);
-    const std::string idstrEscaped = XMLEscape(idstr);
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string geometryName = XMLEscape(idstr);
+    const std::string geometryId = XMLIDEncode(idstr);
 
     if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
         return;
 
     // opening tag
-    mOutput << startstr << "<geometry id=\"" << idstrEscaped << "\" name=\"" << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
     PushTag();
 
     mOutput << startstr << "<mesh>" << endstr;
@@ -1024,9 +1087,9 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
 
     // assemble vertex structure
     // Only write input for POSITION since we will write other as shared inputs in polygon definition
-    mOutput << startstr << "<vertices id=\"" << idstrEscaped << "-vertices" << "\">" << endstr;
+    mOutput << startstr << "<vertices id=\"" << geometryId << "-vertices" << "\">" << endstr;
     PushTag();
-    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstrEscaped << "-positions\" />" << endstr;
+    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr;
     PopTag();
     mOutput << startstr << "</vertices>" << endstr;
 
@@ -1044,18 +1107,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
     {
         mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
-        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         if( mesh->HasNormals() )
-            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr;
+            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {
             if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
-                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
         {
             if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
-                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
 
         mOutput << startstr << "<p>";
@@ -1078,18 +1141,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
     {
         mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
-        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         if( mesh->HasNormals() )
-            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr;
+            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {
             if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
-                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
         {
             if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
-                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
 
         mOutput << startstr << "<vcount>";
@@ -1138,13 +1201,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
             return;
     }
 
-    std::string arrayId = pIdString + "-array";
+    std::string arrayId = XMLIDEncode(pIdString) + "-array";
 
-    mOutput << startstr << "<source id=\"" << XMLEscape(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
+    mOutput << startstr << "<source id=\"" << XMLIDEncode(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
     PushTag();
 
     // source array
-    mOutput << startstr << "<float_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
+    mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
     PushTag();
 
     if( pType == FloatType_TexCoord2 )
@@ -1230,11 +1293,12 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
 // Writes the scene library
 void ColladaExporter::WriteSceneLibrary()
 {
-    const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
+    const std::string sceneName = XMLEscape(mScene->mRootNode->mName.C_Str());
+    const std::string sceneId = XMLIDEncode(mScene->mRootNode->mName.C_Str());
 
     mOutput << startstr << "<library_visual_scenes>" << endstr;
     PushTag();
-    mOutput << startstr << "<visual_scene id=\"" + scene_name_escaped + "\" name=\"" + scene_name_escaped + "\">" << endstr;
+    mOutput << startstr << "<visual_scene id=\"" + sceneId + "\" name=\"" + sceneName + "\">" << endstr;
     PushTag();
 
     // start recursive write at the root node
@@ -1265,7 +1329,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		idstr = idstr + ending;
 	}
 
-	const std::string idstrEscaped = XMLEscape(idstr);
+	const std::string idstrEscaped = XMLIDEncode(idstr);
 	
 	mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << endstr;
 	PushTag();
@@ -1337,13 +1401,13 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 			}
 			
 			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation");
-			std::string arrayId = node_idstr + "-array";
+            std::string arrayId = XMLIDEncode(node_idstr) + "-array";
 			
-			mOutput << startstr << "<source id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			mOutput << startstr << "<source id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
 			PushTag();
 			
 			// source array
-			mOutput << startstr << "<Name_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\"> ";
+			mOutput << startstr << "<Name_array id=\"" << arrayId << "\" count=\"" << names.size() << "\"> ";
 			for( size_t a = 0; a < names.size(); ++a ) {
 				mOutput << names[a] << " ";
             }
@@ -1352,7 +1416,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 			mOutput << startstr << "<technique_common>" << endstr;
 			PushTag();
 
-			mOutput << startstr << "<accessor source=\"#" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
+			mOutput << startstr << "<accessor source=\"#" << arrayId << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
 			PushTag();
 			
 			mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
@@ -1374,12 +1438,12 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		{
 		// samplers
 			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler");
-			mOutput << startstr << "<sampler id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			mOutput << startstr << "<sampler id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
 			PushTag();
 			
-			mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
-			mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
-			mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
 			
 			PopTag();
 			mOutput << startstr << "</sampler>" << endstr;
@@ -1391,7 +1455,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		
 		{
 		// channels
-			mOutput << startstr << "<channel source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+			mOutput << startstr << "<channel source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
 		}
 	}
 	
@@ -1402,8 +1466,6 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 // ------------------------------------------------------------------------------------------------
 void ColladaExporter::WriteAnimationsLibrary()
 {
-	const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
-	
 	if ( mScene->mNumAnimations > 0 ) {
 		mOutput << startstr << "<library_animations>" << endstr;
 		PushTag();
@@ -1500,40 +1562,51 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
     // otherwise it is a normal node (NODE)
     const char * node_type;
     bool is_joint, is_skeleton_root = false;
-    if (NULL == findBone(pScene, pNode->mName.C_Str())) {
+    if (nullptr == findBone(pScene, pNode->mName.C_Str())) {
         node_type = "NODE";
         is_joint = false;
     } else {
         node_type = "JOINT";
         is_joint = true;
-        if(!pNode->mParent || NULL == findBone(pScene, pNode->mParent->mName.C_Str()))
+        if (!pNode->mParent || nullptr == findBone(pScene, pNode->mParent->mName.C_Str())) {
             is_skeleton_root = true;
+        }
     }
 
-    const std::string node_name_escaped = XMLEscape(pNode->mName.data);
-	/* // customized, Note! the id field is crucial for inter-xml look up, it cannot be replaced with sid ?!
-    mOutput << startstr
-            << "<node ";
-    if(is_skeleton_root)
-        mOutput << "id=\"" << "skeleton_root" << "\" "; // For now, only support one skeleton in a scene.
-    mOutput << (is_joint ? "s" : "") << "id=\"" << node_name_escaped;
-	 */
+    const std::string node_id = XMLIDEncode(pNode->mName.data);
+    const std::string node_name = XMLEscape(pNode->mName.data);
 	mOutput << startstr << "<node ";
 	if(is_skeleton_root) {
-		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"" : "") ; // For now, only support one skeleton in a scene.
-		mFoundSkeletonRootNodeID = node_name_escaped;
+		mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"" : "") ; // For now, only support one skeleton in a scene.
+		mFoundSkeletonRootNodeID = node_id;
 	} else {
-		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"": "") ;
+		mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"": "") ;
 	}
 	
-    mOutput << " name=\"" << node_name_escaped
+    mOutput << " name=\"" << node_name
             << "\" type=\"" << node_type
             << "\">" << endstr;
     PushTag();
 
     // write transformation - we can directly put the matrix there
     // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards
-    const aiMatrix4x4& mat = pNode->mTransformation;
+    aiMatrix4x4 mat = pNode->mTransformation;
+
+    // If this node is a Camera node, the camera coordinate system needs to be multiplied in.
+    // When importing from Collada, the mLookAt is set to 0, 0, -1, and the node transform is unchanged.
+    // When importing from a different format, mLookAt is set to 0, 0, 1. Therefore, the local camera
+    // coordinate system must be changed to matche the Collada specification.
+    for (size_t i = 0; i<mScene->mNumCameras; i++){
+        if (mScene->mCameras[i]->mName == pNode->mName){
+            aiMatrix4x4 sourceView;
+            mScene->mCameras[i]->GetCameraMatrix(sourceView);
+
+            aiMatrix4x4 colladaView;
+            colladaView.a1 = colladaView.c3 = -1; // move into -z space.
+            mat *= (sourceView * colladaView);
+            break;
+        }
+    }
 	
 	// customized, sid should be 'matrix' to match with loader code.
     //mOutput << startstr << "<matrix sid=\"transform\">";
@@ -1549,14 +1622,14 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         //check if it is a camera node
         for(size_t i=0; i<mScene->mNumCameras; i++){
             if(mScene->mCameras[i]->mName == pNode->mName){
-                mOutput << startstr <<"<instance_camera url=\"#" << node_name_escaped << "-camera\"/>" << endstr;
+                mOutput << startstr <<"<instance_camera url=\"#" << node_id << "-camera\"/>" << endstr;
                 break;
             }
         }
         //check if it is a light node
         for(size_t i=0; i<mScene->mNumLights; i++){
             if(mScene->mLights[i]->mName == pNode->mName){
-                mOutput << startstr <<"<instance_light url=\"#" << node_name_escaped << "-light\"/>" << endstr;
+                mOutput << startstr <<"<instance_light url=\"#" << node_id << "-light\"/>" << endstr;
                 break;
             }
         }
@@ -1570,15 +1643,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
             continue;
 
+        const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str();
+
         if( mesh->mNumBones == 0 )
         {
-            mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
+            mOutput << startstr << "<instance_geometry url=\"#" << XMLIDEncode(meshName) << "\">" << endstr;
             PushTag();
         }
         else
         {
             mOutput << startstr
-                    << "<instance_controller url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "-skin\">"
+                    << "<instance_controller url=\"#" << XMLIDEncode(meshName) << "-skin\">"
                     << endstr;
             PushTag();
 
@@ -1586,7 +1661,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
 			// use the first bone to find skeleton root
 			const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh );
 			if ( skeletonRootBoneNode ) {
-				mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() );
+				mFoundSkeletonRootNodeID = XMLIDEncode( skeletonRootBoneNode->mName.C_Str() );
 			}
             mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
         }
@@ -1594,7 +1669,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         PushTag();
         mOutput << startstr << "<technique_common>" << endstr;
         PushTag();
-        mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLEscape(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
+        mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLIDEncode(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
         PushTag();
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {

+ 3 - 4
code/ColladaExporter.h → code/Collada/ColladaExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -150,7 +150,6 @@ public:
     /// Stringstream to write all output into
     std::stringstream mOutput;
 
-protected:
     /// The IOSystem for output
     IOSystem* mIOSystem;
 
@@ -189,7 +188,7 @@ protected:
      {}
   };
 
-  // summarize a material in an convinient way.
+  // summarize a material in an convenient way.
   struct Material
   {
     std::string name;
@@ -204,7 +203,7 @@ protected:
 
   std::map<unsigned int, std::string> textures;
 
-protected:
+public:
   /// Dammit C++ - y u no compile two-pass? No I have to add all methods below the struct definitions
   /// Reads a single surface entry from the given material keys
   void ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex);

+ 7 - 10
code/ColladaHelper.h → code/Collada/ColladaHelper.h

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -272,12 +272,13 @@ struct Node
     /** Node instances at this node */
     std::vector<NodeInstance> mNodeInstances;
 
-    /** Rootnodes: Name of primary camera, if any */
+    /** Root-nodes: Name of primary camera, if any */
     std::string mPrimaryCamera;
 
     //! Constructor. Begin with a zero parent
-    Node() {
-        mParent = NULL;
+    Node()
+    : mParent( nullptr ){
+        // empty
     }
 
     //! Destructor: delete all children subsequently
@@ -579,15 +580,11 @@ struct Image
 {
     std::string mFileName;
 
-    /** If image file name is zero, embedded image data
-     */
+    /** Embedded image data */
     std::vector<uint8_t> mImageData;
 
-    /** If image file name is zero, file format of
-     *  embedded image data.
-     */
+    /** File format hint of embedded image data */
     std::string mEmbeddedFormat;
-
 };
 
 /** An animation channel. */

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 286 - 282
code/Collada/ColladaLoader.cpp


+ 6 - 9
code/ColladaLoader.h → code/Collada/ColladaLoader.h

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, assimp team
+Copyright (c) 2006-2019, assimp team
 
 
 All rights reserved.
@@ -94,20 +94,20 @@ public:
 public:
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details. */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+    bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
 
 protected:
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
      */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer* pImp) override;
 
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      */
-    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
 
     /** Recursively constructs a scene node for the given parser node and returns it. */
     aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode);
@@ -120,7 +120,7 @@ protected:
     void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
         aiNode* pTarget);
 		
-    aiMesh *findMesh(std::string meshid);
+    aiMesh *findMesh(const std::string& meshid);
 
     /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
     aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
@@ -184,9 +184,6 @@ protected:
     aiString FindFilenameForEffectTexture( const ColladaParser& pParser,
         const Collada::Effect& pEffect, const std::string& pName);
 
-    /** Converts a path read from a collada file to the usual representation */
-    void ConvertPath( aiString& ss);
-
     /** Reads a float value from an accessor and its data array.
      * @param pAccessor The accessor to use for reading
      * @param pData The data array to read from

+ 3484 - 0
code/Collada/ColladaParser.cpp

@@ -0,0 +1,3484 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file ColladaParser.cpp
+ *  @brief Implementation of the Collada parser helper
+ */
+
+#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
+
+#include <sstream>
+#include <stdarg.h>
+#include "ColladaParser.h"
+#include <assimp/fast_atof.h>
+#include <assimp/ParsingUtils.h>
+#include <assimp/StringUtils.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/light.h>
+#include <assimp/TinyFormatter.h>
+#include <assimp/ZipArchiveIOSystem.h>
+
+#include <memory>
+
+using namespace Assimp;
+using namespace Assimp::Collada;
+using namespace Assimp::Formatter;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaParser::ColladaParser(IOSystem* pIOHandler, const std::string& pFile)
+    : mFileName(pFile)
+    , mReader(nullptr)
+    , mDataLibrary()
+    , mAccessorLibrary()
+    , mMeshLibrary()
+    , mNodeLibrary()
+    , mImageLibrary()
+    , mEffectLibrary()
+    , mMaterialLibrary()
+    , mLightLibrary()
+    , mCameraLibrary()
+    , mControllerLibrary()
+    , mRootNode(nullptr)
+    , mAnims()
+    , mUnitSize(1.0f)
+    , mUpDirection(UP_Y)
+    , mFormat(FV_1_5_n)    // We assume the newest file format by default
+{
+    // validate io-handler instance
+    if (nullptr == pIOHandler) {
+        throw DeadlyImportError("IOSystem is NULL.");
+    }
+
+    std::unique_ptr<IOStream> daefile;
+    std::unique_ptr<ZipArchiveIOSystem> zip_archive;
+
+    // Determine type
+    std::string extension = BaseImporter::GetExtension(pFile);
+    if (extension != "dae") {
+        zip_archive.reset(new ZipArchiveIOSystem(pIOHandler, pFile));
+    }
+
+    if (zip_archive && zip_archive->isOpen()) {
+        std::string dae_filename = ReadZaeManifest(*zip_archive);
+
+        if (dae_filename.empty()) {
+            ThrowException(std::string("Invalid ZAE"));
+        }
+
+        daefile.reset(zip_archive->Open(dae_filename.c_str()));
+        if (daefile == nullptr) {
+            ThrowException(std::string("Invalid ZAE manifest: '") + std::string(dae_filename) + std::string("' is missing"));
+        }
+    }
+    else {
+        // attempt to open the file directly
+        daefile.reset(pIOHandler->Open(pFile));
+        if (daefile.get() == nullptr) {
+            throw DeadlyImportError("Failed to open file '" + pFile + "'.");
+        }
+    }
+
+    // generate a XML reader for it
+    std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(daefile.get()));
+    mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+    if (!mReader) {
+        ThrowException("Unable to read file, malformed XML");
+    }
+
+    // start reading
+    ReadContents();
+
+    // read embedded textures
+    if (zip_archive && zip_archive->isOpen()) {
+        ReadEmbeddedTextures(*zip_archive);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ColladaParser::~ColladaParser()
+{
+    delete mReader;
+    for (NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
+        delete it->second;
+    for (MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
+        delete it->second;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a ZAE manifest and return the filename to attempt to open
+std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
+    // Open the manifest
+    std::unique_ptr<IOStream> manifestfile(zip_archive.Open("manifest.xml"));
+    if (manifestfile == nullptr)
+    {
+        // No manifest, hope there is only one .DAE inside
+        std::vector<std::string> file_list;
+        zip_archive.getFileListExtension(file_list, "dae");
+
+        if (file_list.empty())
+            return std::string();
+
+        return file_list.front();
+    }
+
+    std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(manifestfile.get()));
+    std::unique_ptr<irr::io::IrrXMLReader> manifest_reader(irr::io::createIrrXMLReader(mIOWrapper.get()));
+
+    while (manifest_reader->read())
+    {
+        // find the manifest "dae_root" element
+        if (manifest_reader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (::strcmp(manifest_reader->getNodeName(), "dae_root") == 0)
+            {
+                if (!manifest_reader->read())
+                    return std::string();
+                if (manifest_reader->getNodeType() != irr::io::EXN_TEXT && manifest_reader->getNodeType() != irr::io::EXN_CDATA)
+                    return std::string();
+
+                const char* filepath = manifest_reader->getNodeData();
+                if (filepath == nullptr)
+                    return std::string();
+
+                aiString ai_str(filepath);
+                UriDecodePath(ai_str);
+
+                return std::string(ai_str.C_Str());
+            }
+        }
+    }
+    return std::string();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a path read from a collada file to the usual representation
+void ColladaParser::UriDecodePath(aiString& ss)
+{
+    // TODO: collada spec, p 22. Handle URI correctly.
+    // For the moment we're just stripping the file:// away to make it work.
+    // Windows doesn't seem to be able to find stuff like
+    // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
+    if (0 == strncmp(ss.data, "file://", 7))
+    {
+        ss.length -= 7;
+        memmove(ss.data, ss.data + 7, ss.length);
+        ss.data[ss.length] = '\0';
+    }
+
+    // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
+    // I need to filter it without destroying linux paths starting with "/somewhere"
+#if defined( _MSC_VER )
+    if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') {
+#else
+    if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') {
+#endif
+        --ss.length;
+        ::memmove(ss.data, ss.data + 1, ss.length);
+        ss.data[ss.length] = 0;
+    }
+
+    // find and convert all %xy special chars
+    char* out = ss.data;
+    for (const char* it = ss.data; it != ss.data + ss.length; /**/)
+    {
+        if (*it == '%' && (it + 3) < ss.data + ss.length)
+        {
+            // separate the number to avoid dragging in chars from behind into the parsing
+            char mychar[3] = { it[1], it[2], 0 };
+            size_t nbr = strtoul16(mychar);
+            it += 3;
+            *out++ = (char)(nbr & 0xFF);
+        }
+        else
+        {
+            *out++ = *it++;
+        }
+    }
+
+    // adjust length and terminator of the shortened string
+    *out = 0;
+    ai_assert(out > ss.data);
+    ss.length = static_cast<ai_uint32>(out - ss.data);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read bool from text contents of current element
+bool ColladaParser::ReadBoolFromTextContent()
+{
+    const char* cur = GetTextContent();
+    return (!ASSIMP_strincmp(cur, "true", 4) || '0' != *cur);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read float from text contents of current element
+ai_real ColladaParser::ReadFloatFromTextContent()
+{
+    const char* cur = GetTextContent();
+    return fast_atof(cur);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the contents of the file
+void ColladaParser::ReadContents()
+{
+    while (mReader->read())
+    {
+        // handle the root element "COLLADA"
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("COLLADA"))
+            {
+                // check for 'version' attribute
+                const int attrib = TestAttribute("version");
+                if (attrib != -1) {
+                    const char* version = mReader->getAttributeValue(attrib);
+
+                    if (!::strncmp(version, "1.5", 3)) {
+                        mFormat = FV_1_5_n;
+                        ASSIMP_LOG_DEBUG("Collada schema version is 1.5.n");
+                    }
+                    else if (!::strncmp(version, "1.4", 3)) {
+                        mFormat = FV_1_4_n;
+                        ASSIMP_LOG_DEBUG("Collada schema version is 1.4.n");
+                    }
+                    else if (!::strncmp(version, "1.3", 3)) {
+                        mFormat = FV_1_3_n;
+                        ASSIMP_LOG_DEBUG("Collada schema version is 1.3.n");
+                    }
+                }
+
+                ReadStructure();
+            }
+            else
+            {
+                ASSIMP_LOG_DEBUG_F("Ignoring global element <", mReader->getNodeName(), ">.");
+                SkipElement();
+            }
+        }
+        else
+        {
+            // skip everything else silently
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the structure of the file
+void ColladaParser::ReadStructure()
+{
+    while (mReader->read())
+    {
+        // beginning of elements
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("asset"))
+                ReadAssetInfo();
+            else if (IsElement("library_animations"))
+                ReadAnimationLibrary();
+            else if (IsElement("library_animation_clips"))
+                ReadAnimationClipLibrary();
+            else if (IsElement("library_controllers"))
+                ReadControllerLibrary();
+            else if (IsElement("library_images"))
+                ReadImageLibrary();
+            else if (IsElement("library_materials"))
+                ReadMaterialLibrary();
+            else if (IsElement("library_effects"))
+                ReadEffectLibrary();
+            else if (IsElement("library_geometries"))
+                ReadGeometryLibrary();
+            else if (IsElement("library_visual_scenes"))
+                ReadSceneLibrary();
+            else if (IsElement("library_lights"))
+                ReadLightLibrary();
+            else if (IsElement("library_cameras"))
+                ReadCameraLibrary();
+            else if (IsElement("library_nodes"))
+                ReadSceneNode(NULL); /* some hacking to reuse this piece of code */
+            else if (IsElement("scene"))
+                ReadScene();
+            else
+                SkipElement();
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            break;
+        }
+    }
+
+    PostProcessRootAnimations();
+    PostProcessControllers();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads asset information such as coordinate system information and legal blah
+void ColladaParser::ReadAssetInfo()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("unit"))
+            {
+                // read unit data from the element's attributes
+                const int attrIndex = TestAttribute("meter");
+                if (attrIndex == -1) {
+                    mUnitSize = 1.f;
+                }
+                else {
+                    mUnitSize = mReader->getAttributeValueAsFloat(attrIndex);
+                }
+
+                // consume the trailing stuff
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else if (IsElement("up_axis"))
+            {
+                // read content, strip whitespace, compare
+                const char* content = GetTextContent();
+                if (strncmp(content, "X_UP", 4) == 0)
+                    mUpDirection = UP_X;
+                else if (strncmp(content, "Z_UP", 4) == 0)
+                    mUpDirection = UP_Z;
+                else
+                    mUpDirection = UP_Y;
+
+                // check element end
+                TestClosing("up_axis");
+            }
+            else if (IsElement("contributor"))
+            {
+                ReadContributorInfo();
+            }
+            else
+            {
+                ReadMetaDataItem(mAssetMetaData);
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "asset") != 0)
+                ThrowException("Expected end of <asset> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the contributor info
+void ColladaParser::ReadContributorInfo()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            ReadMetaDataItem(mAssetMetaData);
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "contributor") != 0)
+                ThrowException("Expected end of <contributor> element.");
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single string metadata item
+void ColladaParser::ReadMetaDataItem(StringMetaData &metadata)
+{
+    // Metadata such as created, keywords, subject etc
+    const char* key_char = mReader->getNodeName();
+    if (key_char != nullptr)
+    {
+        const std::string key_str(key_char);
+        const char* value_char = TestTextContent();
+        if (value_char != nullptr)
+        {
+            std::string camel_key_str = key_str;
+            ToCamelCase(camel_key_str);
+            aiString aistr;
+            aistr.Set(value_char);
+            metadata.emplace(camel_key_str, aistr);
+        }
+        TestClosing(key_str.c_str());
+    }
+    else
+        SkipElement();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert underscore_seperated to CamelCase: "authoring_tool" becomes "AuthoringTool"
+void ColladaParser::ToCamelCase(std::string &text)
+{
+    if (text.empty())
+        return;
+    // Capitalise first character
+    text[0] = ToUpper(text[0]);
+    for (auto it = text.begin(); it != text.end(); /*iterated below*/)
+    {
+        if ((*it) == '_')
+        {
+            it = text.erase(it);
+            if (it != text.end())
+                (*it) = ToUpper(*it);
+        }
+        else
+            ++it;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation clips
+void ColladaParser::ReadAnimationClipLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("animation_clip"))
+            {
+                // optional name given as an attribute
+                std::string animName;
+                int indexName = TestAttribute("name");
+                int indexID = TestAttribute("id");
+                if (indexName >= 0)
+                    animName = mReader->getAttributeValue(indexName);
+                else if (indexID >= 0)
+                    animName = mReader->getAttributeValue(indexID);
+                else
+                    animName = std::string("animation_") + to_string(mAnimationClipLibrary.size());
+
+                std::pair<std::string, std::vector<std::string> > clip;
+
+                clip.first = animName;
+
+                while (mReader->read())
+                {
+                    if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+                    {
+                        if (IsElement("instance_animation"))
+                        {
+                            int indexUrl = TestAttribute("url");
+                            if (indexUrl >= 0)
+                            {
+                                const char* url = mReader->getAttributeValue(indexUrl);
+                                if (url[0] != '#')
+                                    ThrowException("Unknown reference format");
+
+                                url++;
+
+                                clip.second.push_back(url);
+                            }
+                        }
+                        else
+                        {
+                            // ignore the rest
+                            SkipElement();
+                        }
+                    }
+                    else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+                    {
+                        if (strcmp(mReader->getNodeName(), "animation_clip") != 0)
+                            ThrowException("Expected end of <animation_clip> element.");
+
+                        break;
+                    }
+                }
+
+                if (clip.second.size() > 0)
+                {
+                    mAnimationClipLibrary.push_back(clip);
+                }
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_animation_clips") != 0)
+                ThrowException("Expected end of <library_animation_clips> element.");
+
+            break;
+        }
+    }
+}
+
+void ColladaParser::PostProcessControllers()
+{
+    std::string meshId;
+    for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) {
+        meshId = it->second.mMeshId;
+        ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
+        while (findItr != mControllerLibrary.end()) {
+            meshId = findItr->second.mMeshId;
+            findItr = mControllerLibrary.find(meshId);
+        }
+
+        it->second.mMeshId = meshId;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Re-build animations from animation clip library, if present, otherwise combine single-channel animations
+void ColladaParser::PostProcessRootAnimations()
+{
+    if (mAnimationClipLibrary.size() > 0)
+    {
+        Animation temp;
+
+        for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it)
+        {
+            std::string clipName = it->first;
+
+            Animation *clip = new Animation();
+            clip->mName = clipName;
+
+            temp.mSubAnims.push_back(clip);
+
+            for (std::vector<std::string>::iterator a = it->second.begin(); a != it->second.end(); ++a)
+            {
+                std::string animationID = *a;
+
+                AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
+
+                if (animation != mAnimationLibrary.end())
+                {
+                    Animation *pSourceAnimation = animation->second;
+
+                    pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
+                }
+            }
+        }
+
+        mAnims = temp;
+
+        // Ensure no double deletes.
+        temp.mSubAnims.clear();
+    }
+    else
+    {
+        mAnims.CombineSingleChannelAnimations();
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation library
+void ColladaParser::ReadAnimationLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("animation"))
+            {
+                // delegate the reading. Depending on the inner elements it will be a container or a anim channel
+                ReadAnimation(&mAnims);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_animations") != 0)
+                ThrowException("Expected end of <library_animations> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation into the given parent structure
+void ColladaParser::ReadAnimation(Collada::Animation* pParent)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    // an <animation> element may be a container for grouping sub-elements or an animation channel
+    // this is the channel collection by ID, in case it has channels
+    typedef std::map<std::string, AnimationChannel> ChannelMap;
+    ChannelMap channels;
+    // this is the anim container in case we're a container
+    Animation* anim = NULL;
+
+    // optional name given as an attribute
+    std::string animName;
+    std::string animID;
+    int indexName = TestAttribute("name");
+    int indexID = TestAttribute("id");
+
+    if (indexID >= 0)
+        animID = mReader->getAttributeValue(indexID);
+
+    if (indexName >= 0)
+        animName = mReader->getAttributeValue(indexName);
+    else if (indexID >= 0)
+        animName = animID;
+    else
+        animName = "animation";
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // we have subanimations
+            if (IsElement("animation"))
+            {
+                // create container from our element
+                if (!anim)
+                {
+                    anim = new Animation;
+                    anim->mName = animName;
+                    pParent->mSubAnims.push_back(anim);
+                }
+
+                // recurse into the subelement
+                ReadAnimation(anim);
+            }
+            else if (IsElement("source"))
+            {
+                // possible animation data - we'll never know. Better store it
+                ReadSource();
+            }
+            else if (IsElement("sampler"))
+            {
+                // read the ID to assign the corresponding collada channel afterwards.
+                int indexID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(indexID);
+                ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first;
+
+                // have it read into a channel
+                ReadAnimationSampler(newChannel->second);
+            }
+            else if (IsElement("channel"))
+            {
+                // the binding element whose whole purpose is to provide the target to animate
+                // Thanks, Collada! A directly posted information would have been too simple, I guess.
+                // Better add another indirection to that! Can't have enough of those.
+                int indexTarget = GetAttribute("target");
+                int indexSource = GetAttribute("source");
+                const char* sourceId = mReader->getAttributeValue(indexSource);
+                if (sourceId[0] == '#')
+                    sourceId++;
+                ChannelMap::iterator cit = channels.find(sourceId);
+                if (cit != channels.end())
+                    cit->second.mTarget = mReader->getAttributeValue(indexTarget);
+
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "animation") != 0)
+                ThrowException("Expected end of <animation> element.");
+
+            break;
+        }
+    }
+
+    // it turned out to have channels - add them
+    if (!channels.empty())
+    {
+        // FIXME: Is this essentially doing the same as "single-anim-node" codepath in
+        //        ColladaLoader::StoreAnimations? For now, this has been deferred to after
+        //        all animations and all clips have been read. Due to handling of
+        //        <library_animation_clips> this cannot be done here, as the channel owner
+        //        is lost, and some exporters make up animations by referring to multiple
+        //        single-channel animations from an <instance_animation>.
+/*
+        // special filtering for stupid exporters packing each channel into a separate animation
+        if( channels.size() == 1)
+        {
+            pParent->mChannels.push_back( channels.begin()->second);
+        } else
+*/
+        {
+            // else create the animation, if not done yet, and store the channels
+            if (!anim)
+            {
+                anim = new Animation;
+                anim->mName = animName;
+                pParent->mSubAnims.push_back(anim);
+            }
+            for (ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it)
+                anim->mChannels.push_back(it->second);
+
+            if (indexID >= 0)
+            {
+                mAnimationLibrary[animID] = anim;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation sampler into the given anim channel
+void ColladaParser::ReadAnimationSampler(Collada::AnimationChannel& pChannel)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("input"))
+            {
+                int indexSemantic = GetAttribute("semantic");
+                const char* semantic = mReader->getAttributeValue(indexSemantic);
+                int indexSource = GetAttribute("source");
+                const char* source = mReader->getAttributeValue(indexSource);
+                if (source[0] != '#')
+                    ThrowException("Unsupported URL format");
+                source++;
+
+                if (strcmp(semantic, "INPUT") == 0)
+                    pChannel.mSourceTimes = source;
+                else if (strcmp(semantic, "OUTPUT") == 0)
+                    pChannel.mSourceValues = source;
+                else if (strcmp(semantic, "IN_TANGENT") == 0)
+                    pChannel.mInTanValues = source;
+                else if (strcmp(semantic, "OUT_TANGENT") == 0)
+                    pChannel.mOutTanValues = source;
+                else if (strcmp(semantic, "INTERPOLATION") == 0)
+                    pChannel.mInterpolationValues = source;
+
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "sampler") != 0)
+                ThrowException("Expected end of <sampler> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the skeleton controller library
+void ColladaParser::ReadControllerLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("controller"))
+            {
+                // read ID. Ask the spec if it's necessary or optional... you might be surprised.
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mControllerLibrary[id] = Controller();
+
+                // read on from there
+                ReadController(mControllerLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_controllers") != 0)
+                ThrowException("Expected end of <library_controllers> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a controller into the given mesh structure
+void ColladaParser::ReadController(Collada::Controller& pController)
+{
+    // initial values
+    pController.mType = Skin;
+    pController.mMethod = Normalized;
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
+            if (IsElement("morph"))
+            {
+                pController.mType = Morph;
+                int baseIndex = GetAttribute("source");
+                pController.mMeshId = mReader->getAttributeValue(baseIndex) + 1;
+                int methodIndex = GetAttribute("method");
+                if (methodIndex > 0) {
+                    const char *method = mReader->getAttributeValue(methodIndex);
+                    if (strcmp(method, "RELATIVE") == 0)
+                        pController.mMethod = Relative;
+                }
+            }
+            else if (IsElement("skin"))
+            {
+                // read the mesh it refers to. According to the spec this could also be another
+                // controller, but I refuse to implement every single idea they've come up with
+                int sourceIndex = GetAttribute("source");
+                pController.mMeshId = mReader->getAttributeValue(sourceIndex) + 1;
+            }
+            else if (IsElement("bind_shape_matrix"))
+            {
+                // content is 16 floats to define a matrix... it seems to be important for some models
+                const char* content = GetTextContent();
+
+                // read the 16 floats
+                for (unsigned int a = 0; a < 16; a++)
+                {
+                    // read a number
+                    content = fast_atoreal_move<ai_real>(content, pController.mBindShapeMatrix[a]);
+                    // skip whitespace after it
+                    SkipSpacesAndLineEnd(&content);
+                }
+
+                TestClosing("bind_shape_matrix");
+            }
+            else if (IsElement("source"))
+            {
+                // data array - we have specialists to handle this
+                ReadSource();
+            }
+            else if (IsElement("joints"))
+            {
+                ReadControllerJoints(pController);
+            }
+            else if (IsElement("vertex_weights"))
+            {
+                ReadControllerWeights(pController);
+            }
+            else if (IsElement("targets"))
+            {
+                while (mReader->read()) {
+                    if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+                        if (IsElement("input")) {
+                            int semanticsIndex = GetAttribute("semantic");
+                            int sourceIndex = GetAttribute("source");
+
+                            const char *semantics = mReader->getAttributeValue(semanticsIndex);
+                            const char *source = mReader->getAttributeValue(sourceIndex);
+                            if (strcmp(semantics, "MORPH_TARGET") == 0) {
+                                pController.mMorphTarget = source + 1;
+                            }
+                            else if (strcmp(semantics, "MORPH_WEIGHT") == 0)
+                            {
+                                pController.mMorphWeight = source + 1;
+                            }
+                        }
+                    }
+                    else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+                        if (strcmp(mReader->getNodeName(), "targets") == 0)
+                            break;
+                        else
+                            ThrowException("Expected end of <targets> element.");
+                    }
+                }
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "controller") == 0)
+                break;
+            else if (strcmp(mReader->getNodeName(), "skin") != 0 && strcmp(mReader->getNodeName(), "morph") != 0)
+                ThrowException("Expected end of <controller> element.");
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint definitions for the given controller
+void ColladaParser::ReadControllerJoints(Collada::Controller& pController)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
+            if (IsElement("input"))
+            {
+                int indexSemantic = GetAttribute("semantic");
+                const char* attrSemantic = mReader->getAttributeValue(indexSemantic);
+                int indexSource = GetAttribute("source");
+                const char* attrSource = mReader->getAttributeValue(indexSource);
+
+                // local URLS always start with a '#'. We don't support global URLs
+                if (attrSource[0] != '#')
+                    ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <joints> data <input> element");
+                attrSource++;
+
+                // parse source URL to corresponding source
+                if (strcmp(attrSemantic, "JOINT") == 0)
+                    pController.mJointNameSource = attrSource;
+                else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0)
+                    pController.mJointOffsetMatrixSource = attrSource;
+                else
+                    ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <joints> data <input> element");
+
+                // skip inner data, if present
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "joints") != 0)
+                ThrowException("Expected end of <joints> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint weights for the given controller
+void ColladaParser::ReadControllerWeights(Collada::Controller& pController)
+{
+    // read vertex count from attributes and resize the array accordingly
+    int indexCount = GetAttribute("count");
+    size_t vertexCount = (size_t)mReader->getAttributeValueAsInt(indexCount);
+    pController.mWeightCounts.resize(vertexCount);
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
+            if (IsElement("input") && vertexCount > 0)
+            {
+                InputChannel channel;
+
+                int indexSemantic = GetAttribute("semantic");
+                const char* attrSemantic = mReader->getAttributeValue(indexSemantic);
+                int indexSource = GetAttribute("source");
+                const char* attrSource = mReader->getAttributeValue(indexSource);
+                int indexOffset = TestAttribute("offset");
+                if (indexOffset >= 0)
+                    channel.mOffset = mReader->getAttributeValueAsInt(indexOffset);
+
+                // local URLS always start with a '#'. We don't support global URLs
+                if (attrSource[0] != '#')
+                    ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <vertex_weights> data <input> element");
+                channel.mAccessor = attrSource + 1;
+
+                // parse source URL to corresponding source
+                if (strcmp(attrSemantic, "JOINT") == 0)
+                    pController.mWeightInputJoints = channel;
+                else if (strcmp(attrSemantic, "WEIGHT") == 0)
+                    pController.mWeightInputWeights = channel;
+                else
+                    ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <vertex_weights> data <input> element");
+
+                // skip inner data, if present
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else if (IsElement("vcount") && vertexCount > 0)
+            {
+                // read weight count per vertex
+                const char* text = GetTextContent();
+                size_t numWeights = 0;
+                for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
+                {
+                    if (*text == 0)
+                        ThrowException("Out of data while reading <vcount>");
+
+                    *it = strtoul10(text, &text);
+                    numWeights += *it;
+                    SkipSpacesAndLineEnd(&text);
+                }
+
+                TestClosing("vcount");
+
+                // reserve weight count
+                pController.mWeights.resize(numWeights);
+            }
+            else if (IsElement("v") && vertexCount > 0)
+            {
+                // read JointIndex - WeightIndex pairs
+                const char* text = GetTextContent();
+
+                for (std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
+                {
+                    if (*text == 0)
+                        ThrowException("Out of data while reading <vertex_weights>");
+                    it->first = strtoul10(text, &text);
+                    SkipSpacesAndLineEnd(&text);
+                    if (*text == 0)
+                        ThrowException("Out of data while reading <vertex_weights>");
+                    it->second = strtoul10(text, &text);
+                    SkipSpacesAndLineEnd(&text);
+                }
+
+                TestClosing("v");
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "vertex_weights") != 0)
+                ThrowException("Expected end of <vertex_weights> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the image library contents
+void ColladaParser::ReadImageLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("image"))
+            {
+                // read ID. Another entry which is "optional" by design but obligatory in reality
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mImageLibrary[id] = Image();
+
+                // read on from there
+                ReadImage(mImageLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_images") != 0)
+                ThrowException("Expected end of <library_images> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an image entry into the given image
+void ColladaParser::ReadImage(Collada::Image& pImage)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            // Need to run different code paths here, depending on the Collada XSD version
+            if (IsElement("image")) {
+                SkipElement();
+            }
+            else if (IsElement("init_from"))
+            {
+                if (mFormat == FV_1_4_n)
+                {
+                    // FIX: C4D exporter writes empty <init_from/> tags
+                    if (!mReader->isEmptyElement()) {
+                        // element content is filename - hopefully
+                        const char* sz = TestTextContent();
+                        if (sz)
+                        {
+                            aiString filepath(sz);
+                            UriDecodePath(filepath);
+                            pImage.mFileName = filepath.C_Str();
+                        }
+                        TestClosing("init_from");
+                    }
+                    if (!pImage.mFileName.length()) {
+                        pImage.mFileName = "unknown_texture";
+                    }
+                }
+                else if (mFormat == FV_1_5_n)
+                {
+                    // make sure we skip over mip and array initializations, which
+                    // we don't support, but which could confuse the loader if
+                    // they're not skipped.
+                    int attrib = TestAttribute("array_index");
+                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+                        ASSIMP_LOG_WARN("Collada: Ignoring texture array index");
+                        continue;
+                    }
+
+                    attrib = TestAttribute("mip_index");
+                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+                        ASSIMP_LOG_WARN("Collada: Ignoring MIP map layer");
+                        continue;
+                    }
+
+                    // TODO: correctly jump over cube and volume maps?
+                }
+            }
+            else if (mFormat == FV_1_5_n)
+            {
+                if (IsElement("ref"))
+                {
+                    // element content is filename - hopefully
+                    const char* sz = TestTextContent();
+                    if (sz)
+                    {
+                        aiString filepath(sz);
+                        UriDecodePath(filepath);
+                        pImage.mFileName = filepath.C_Str();
+                    }
+                    TestClosing("ref");
+                }
+                else if (IsElement("hex") && !pImage.mFileName.length())
+                {
+                    // embedded image. get format
+                    const int attrib = TestAttribute("format");
+                    if (-1 == attrib)
+                        ASSIMP_LOG_WARN("Collada: Unknown image file format");
+                    else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib);
+
+                    const char* data = GetTextContent();
+
+                    // hexadecimal-encoded binary octets. First of all, find the
+                    // required buffer size to reserve enough storage.
+                    const char* cur = data;
+                    while (!IsSpaceOrNewLine(*cur)) cur++;
+
+                    const unsigned int size = (unsigned int)(cur - data) * 2;
+                    pImage.mImageData.resize(size);
+                    for (unsigned int i = 0; i < size; ++i)
+                        pImage.mImageData[i] = HexOctetToDecimal(data + (i << 1));
+
+                    TestClosing("hex");
+                }
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "image") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the material library
+void ColladaParser::ReadMaterialLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    std::map<std::string, int> names;
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("material"))
+            {
+                // read ID. By now you probably know my opinion about this "specification"
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                std::string name;
+                int attrName = TestAttribute("name");
+                if (attrName >= 0)
+                    name = mReader->getAttributeValue(attrName);
+
+                // create an entry and store it in the library under its ID
+                mMaterialLibrary[id] = Material();
+
+                if (!name.empty())
+                {
+                    std::map<std::string, int>::iterator it = names.find(name);
+                    if (it != names.end())
+                    {
+                        std::ostringstream strStream;
+                        strStream << ++it->second;
+                        name.append(" " + strStream.str());
+                    }
+                    else
+                    {
+                        names[name] = 0;
+                    }
+
+                    mMaterialLibrary[id].mName = name;
+                }
+
+                ReadMaterial(mMaterialLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_materials") != 0)
+                ThrowException("Expected end of <library_materials> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the light library
+void ColladaParser::ReadLightLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("light"))
+            {
+                // read ID. By now you probably know my opinion about this "specification"
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                ReadLight(mLightLibrary[id] = Light());
+
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_lights") != 0)
+                ThrowException("Expected end of <library_lights> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the camera library
+void ColladaParser::ReadCameraLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("camera"))
+            {
+                // read ID. By now you probably know my opinion about this "specification"
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                Camera& cam = mCameraLibrary[id];
+                attrID = TestAttribute("name");
+                if (attrID != -1)
+                    cam.mName = mReader->getAttributeValue(attrID);
+
+                ReadCamera(cam);
+
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_cameras") != 0)
+                ThrowException("Expected end of <library_cameras> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a material entry into the given material
+void ColladaParser::ReadMaterial(Collada::Material& pMaterial)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("material")) {
+                SkipElement();
+            }
+            else if (IsElement("instance_effect"))
+            {
+                // referred effect by URL
+                int attrUrl = GetAttribute("url");
+                const char* url = mReader->getAttributeValue(attrUrl);
+                if (url[0] != '#')
+                    ThrowException("Unknown reference format");
+
+                pMaterial.mEffect = url + 1;
+
+                SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "material") != 0)
+                ThrowException("Expected end of <material> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a light entry into the given light
+void ColladaParser::ReadLight(Collada::Light& pLight)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("light")) {
+                SkipElement();
+            }
+            else if (IsElement("spot")) {
+                pLight.mType = aiLightSource_SPOT;
+            }
+            else if (IsElement("ambient")) {
+                pLight.mType = aiLightSource_AMBIENT;
+            }
+            else if (IsElement("directional")) {
+                pLight.mType = aiLightSource_DIRECTIONAL;
+            }
+            else if (IsElement("point")) {
+                pLight.mType = aiLightSource_POINT;
+            }
+            else if (IsElement("color")) {
+                // text content contains 3 floats
+                const char* content = GetTextContent();
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pLight.mColor.r);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pLight.mColor.g);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pLight.mColor.b);
+                SkipSpacesAndLineEnd(&content);
+
+                TestClosing("color");
+            }
+            else if (IsElement("constant_attenuation")) {
+                pLight.mAttConstant = ReadFloatFromTextContent();
+                TestClosing("constant_attenuation");
+            }
+            else if (IsElement("linear_attenuation")) {
+                pLight.mAttLinear = ReadFloatFromTextContent();
+                TestClosing("linear_attenuation");
+            }
+            else if (IsElement("quadratic_attenuation")) {
+                pLight.mAttQuadratic = ReadFloatFromTextContent();
+                TestClosing("quadratic_attenuation");
+            }
+            else if (IsElement("falloff_angle")) {
+                pLight.mFalloffAngle = ReadFloatFromTextContent();
+                TestClosing("falloff_angle");
+            }
+            else if (IsElement("falloff_exponent")) {
+                pLight.mFalloffExponent = ReadFloatFromTextContent();
+                TestClosing("falloff_exponent");
+            }
+            // FCOLLADA extensions
+            // -------------------------------------------------------
+            else if (IsElement("outer_cone")) {
+                pLight.mOuterAngle = ReadFloatFromTextContent();
+                TestClosing("outer_cone");
+            }
+            // ... and this one is even deprecated
+            else if (IsElement("penumbra_angle")) {
+                pLight.mPenumbraAngle = ReadFloatFromTextContent();
+                TestClosing("penumbra_angle");
+            }
+            else if (IsElement("intensity")) {
+                pLight.mIntensity = ReadFloatFromTextContent();
+                TestClosing("intensity");
+            }
+            else if (IsElement("falloff")) {
+                pLight.mOuterAngle = ReadFloatFromTextContent();
+                TestClosing("falloff");
+            }
+            else if (IsElement("hotspot_beam")) {
+                pLight.mFalloffAngle = ReadFloatFromTextContent();
+                TestClosing("hotspot_beam");
+            }
+            // OpenCOLLADA extensions
+            // -------------------------------------------------------
+            else if (IsElement("decay_falloff")) {
+                pLight.mOuterAngle = ReadFloatFromTextContent();
+                TestClosing("decay_falloff");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "light") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a camera entry into the given light
+void ColladaParser::ReadCamera(Collada::Camera& pCamera)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("camera")) {
+                SkipElement();
+            }
+            else if (IsElement("orthographic")) {
+                pCamera.mOrtho = true;
+            }
+            else if (IsElement("xfov") || IsElement("xmag")) {
+                pCamera.mHorFov = ReadFloatFromTextContent();
+                TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
+            }
+            else if (IsElement("yfov") || IsElement("ymag")) {
+                pCamera.mVerFov = ReadFloatFromTextContent();
+                TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
+            }
+            else if (IsElement("aspect_ratio")) {
+                pCamera.mAspect = ReadFloatFromTextContent();
+                TestClosing("aspect_ratio");
+            }
+            else if (IsElement("znear")) {
+                pCamera.mZNear = ReadFloatFromTextContent();
+                TestClosing("znear");
+            }
+            else if (IsElement("zfar")) {
+                pCamera.mZFar = ReadFloatFromTextContent();
+                TestClosing("zfar");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "camera") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the effect library
+void ColladaParser::ReadEffectLibrary()
+{
+    if (mReader->isEmptyElement()) {
+        return;
+    }
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("effect"))
+            {
+                // read ID. Do I have to repeat my ranting about "optional" attributes?
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mEffectLibrary[id] = Effect();
+                // read on from there
+                ReadEffect(mEffectLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_effects") != 0)
+                ThrowException("Expected end of <library_effects> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry into the given effect
+void ColladaParser::ReadEffect(Collada::Effect& pEffect)
+{
+    // for the moment we don't support any other type of effect.
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("profile_COMMON"))
+                ReadEffectProfileCommon(pEffect);
+            else
+                SkipElement();
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "effect") != 0)
+                ThrowException("Expected end of <effect> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an COMMON effect profile
+void ColladaParser::ReadEffectProfileCommon(Collada::Effect& pEffect)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("newparam")) {
+                // save ID
+                int attrSID = GetAttribute("sid");
+                std::string sid = mReader->getAttributeValue(attrSID);
+                pEffect.mParams[sid] = EffectParam();
+                ReadEffectParam(pEffect.mParams[sid]);
+            }
+            else if (IsElement("technique") || IsElement("extra"))
+            {
+                // just syntactic sugar
+            }
+
+            else if (mFormat == FV_1_4_n && IsElement("image"))
+            {
+                // read ID. Another entry which is "optional" by design but obligatory in reality
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mImageLibrary[id] = Image();
+
+                // read on from there
+                ReadImage(mImageLibrary[id]);
+            }
+
+            /* Shading modes */
+            else if (IsElement("phong"))
+                pEffect.mShadeType = Shade_Phong;
+            else if (IsElement("constant"))
+                pEffect.mShadeType = Shade_Constant;
+            else if (IsElement("lambert"))
+                pEffect.mShadeType = Shade_Lambert;
+            else if (IsElement("blinn"))
+                pEffect.mShadeType = Shade_Blinn;
+
+            /* Color + texture properties */
+            else if (IsElement("emission"))
+                ReadEffectColor(pEffect.mEmissive, pEffect.mTexEmissive);
+            else if (IsElement("ambient"))
+                ReadEffectColor(pEffect.mAmbient, pEffect.mTexAmbient);
+            else if (IsElement("diffuse"))
+                ReadEffectColor(pEffect.mDiffuse, pEffect.mTexDiffuse);
+            else if (IsElement("specular"))
+                ReadEffectColor(pEffect.mSpecular, pEffect.mTexSpecular);
+            else if (IsElement("reflective")) {
+                ReadEffectColor(pEffect.mReflective, pEffect.mTexReflective);
+            }
+            else if (IsElement("transparent")) {
+                pEffect.mHasTransparency = true;
+
+                const char* opaque = mReader->getAttributeValueSafe("opaque");
+
+                if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) {
+                    pEffect.mRGBTransparency = true;
+                }
+
+                // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure...
+                if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) {
+                    pEffect.mInvertTransparency = true;
+                }
+
+                ReadEffectColor(pEffect.mTransparent, pEffect.mTexTransparent);
+            }
+            else if (IsElement("shininess"))
+                ReadEffectFloat(pEffect.mShininess);
+            else if (IsElement("reflectivity"))
+                ReadEffectFloat(pEffect.mReflectivity);
+
+            /* Single scalar properties */
+            else if (IsElement("transparency"))
+                ReadEffectFloat(pEffect.mTransparency);
+            else if (IsElement("index_of_refraction"))
+                ReadEffectFloat(pEffect.mRefractIndex);
+
+            // GOOGLEEARTH/OKINO extensions
+            // -------------------------------------------------------
+            else if (IsElement("double_sided"))
+                pEffect.mDoubleSided = ReadBoolFromTextContent();
+
+            // FCOLLADA extensions
+            // -------------------------------------------------------
+            else if (IsElement("bump")) {
+                aiColor4D dummy;
+                ReadEffectColor(dummy, pEffect.mTexBump);
+            }
+
+            // MAX3D extensions
+            // -------------------------------------------------------
+            else if (IsElement("wireframe")) {
+                pEffect.mWireframe = ReadBoolFromTextContent();
+                TestClosing("wireframe");
+            }
+            else if (IsElement("faceted")) {
+                pEffect.mFaceted = ReadBoolFromTextContent();
+                TestClosing("faceted");
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "profile_COMMON") == 0)
+            {
+                break;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read texture wrapping + UV transform settings from a profile==Maya chunk
+void ColladaParser::ReadSamplerProperties(Sampler& out)
+{
+    if (mReader->isEmptyElement()) {
+        return;
+    }
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+
+            // MAYA extensions
+            // -------------------------------------------------------
+            if (IsElement("wrapU")) {
+                out.mWrapU = ReadBoolFromTextContent();
+                TestClosing("wrapU");
+            }
+            else if (IsElement("wrapV")) {
+                out.mWrapV = ReadBoolFromTextContent();
+                TestClosing("wrapV");
+            }
+            else if (IsElement("mirrorU")) {
+                out.mMirrorU = ReadBoolFromTextContent();
+                TestClosing("mirrorU");
+            }
+            else if (IsElement("mirrorV")) {
+                out.mMirrorV = ReadBoolFromTextContent();
+                TestClosing("mirrorV");
+            }
+            else if (IsElement("repeatU")) {
+                out.mTransform.mScaling.x = ReadFloatFromTextContent();
+                TestClosing("repeatU");
+            }
+            else if (IsElement("repeatV")) {
+                out.mTransform.mScaling.y = ReadFloatFromTextContent();
+                TestClosing("repeatV");
+            }
+            else if (IsElement("offsetU")) {
+                out.mTransform.mTranslation.x = ReadFloatFromTextContent();
+                TestClosing("offsetU");
+            }
+            else if (IsElement("offsetV")) {
+                out.mTransform.mTranslation.y = ReadFloatFromTextContent();
+                TestClosing("offsetV");
+            }
+            else if (IsElement("rotateUV")) {
+                out.mTransform.mRotation = ReadFloatFromTextContent();
+                TestClosing("rotateUV");
+            }
+            else if (IsElement("blend_mode")) {
+
+                const char* sz = GetTextContent();
+                // http://www.feelingsoftware.com/content/view/55/72/lang,en/
+                // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
+                if (0 == ASSIMP_strincmp(sz, "ADD", 3))
+                    out.mOp = aiTextureOp_Add;
+
+                else if (0 == ASSIMP_strincmp(sz, "SUBTRACT", 8))
+                    out.mOp = aiTextureOp_Subtract;
+
+                else if (0 == ASSIMP_strincmp(sz, "MULTIPLY", 8))
+                    out.mOp = aiTextureOp_Multiply;
+
+                else {
+                    ASSIMP_LOG_WARN("Collada: Unsupported MAYA texture blend mode");
+                }
+                TestClosing("blend_mode");
+            }
+            // OKINO extensions
+            // -------------------------------------------------------
+            else if (IsElement("weighting")) {
+                out.mWeighting = ReadFloatFromTextContent();
+                TestClosing("weighting");
+            }
+            else if (IsElement("mix_with_previous_layer")) {
+                out.mMixWithPrevious = ReadFloatFromTextContent();
+                TestClosing("mix_with_previous_layer");
+            }
+            // MAX3D extensions
+            // -------------------------------------------------------
+            else if (IsElement("amount")) {
+                out.mWeighting = ReadFloatFromTextContent();
+                TestClosing("amount");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "technique") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a color or a texture defining that color
+void ColladaParser::ReadEffectColor(aiColor4D& pColor, Sampler& pSampler)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    // Save current element name
+    const std::string curElem = mReader->getNodeName();
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("color"))
+            {
+                // text content contains 4 floats
+                const char* content = GetTextContent();
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.r);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.g);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.b);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.a);
+                SkipSpacesAndLineEnd(&content);
+                TestClosing("color");
+            }
+            else if (IsElement("texture"))
+            {
+                // get name of source texture/sampler
+                int attrTex = GetAttribute("texture");
+                pSampler.mName = mReader->getAttributeValue(attrTex);
+
+                // get name of UV source channel. Specification demands it to be there, but some exporters
+                // don't write it. It will be the default UV channel in case it's missing.
+                attrTex = TestAttribute("texcoord");
+                if (attrTex >= 0)
+                    pSampler.mUVChannel = mReader->getAttributeValue(attrTex);
+                //SkipElement();
+
+                // as we've read texture, the color needs to be 1,1,1,1
+                pColor = aiColor4D(1.f, 1.f, 1.f, 1.f);
+            }
+            else if (IsElement("technique"))
+            {
+                const int _profile = GetAttribute("profile");
+                const char* profile = mReader->getAttributeValue(_profile);
+
+                // Some extensions are quite useful ... ReadSamplerProperties processes
+                // several extensions in MAYA, OKINO and MAX3D profiles.
+                if (!::strcmp(profile, "MAYA") || !::strcmp(profile, "MAX3D") || !::strcmp(profile, "OKINO"))
+                {
+                    // get more information on this sampler
+                    ReadSamplerProperties(pSampler);
+                }
+                else SkipElement();
+            }
+            else if (!IsElement("extra"))
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (mReader->getNodeName() == curElem)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a float
+void ColladaParser::ReadEffectFloat(ai_real& pFloat)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("float"))
+            {
+                // text content contains a single floats
+                const char* content = GetTextContent();
+                content = fast_atoreal_move<ai_real>(content, pFloat);
+                SkipSpacesAndLineEnd(&content);
+
+                TestClosing("float");
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect parameter specification of any kind
+void ColladaParser::ReadEffectParam(Collada::EffectParam& pParam)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("surface"))
+            {
+                // image ID given inside <init_from> tags
+                TestOpening("init_from");
+                const char* content = GetTextContent();
+                pParam.mType = Param_Surface;
+                pParam.mReference = content;
+                TestClosing("init_from");
+
+                // don't care for remaining stuff
+                SkipElement("surface");
+            }
+            else if (IsElement("sampler2D") && (FV_1_4_n == mFormat || FV_1_3_n == mFormat))
+            {
+                // surface ID is given inside <source> tags
+                TestOpening("source");
+                const char* content = GetTextContent();
+                pParam.mType = Param_Sampler;
+                pParam.mReference = content;
+                TestClosing("source");
+
+                // don't care for remaining stuff
+                SkipElement("sampler2D");
+            }
+            else if (IsElement("sampler2D"))
+            {
+                // surface ID is given inside <instance_image> tags
+                TestOpening("instance_image");
+                int attrURL = GetAttribute("url");
+                const char* url = mReader->getAttributeValue(attrURL);
+                if (url[0] != '#')
+                    ThrowException("Unsupported URL format in instance_image");
+                url++;
+                pParam.mType = Param_Sampler;
+                pParam.mReference = url;
+                SkipElement("sampler2D");
+            }
+            else
+            {
+                // ignore unknown element
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the geometry library contents
+void ColladaParser::ReadGeometryLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("geometry"))
+            {
+                // read ID. Another entry which is "optional" by design but obligatory in reality
+                int indexID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(indexID);
+
+                // TODO: (thom) support SIDs
+                // ai_assert( TestAttribute( "sid") == -1);
+
+                // create a mesh and store it in the library under its ID
+                Mesh* mesh = new Mesh;
+                mMeshLibrary[id] = mesh;
+
+                // read the mesh name if it exists
+                const int nameIndex = TestAttribute("name");
+                if (nameIndex != -1)
+                {
+                    mesh->mName = mReader->getAttributeValue(nameIndex);
+                }
+
+                // read on from there
+                ReadGeometry(mesh);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_geometries") != 0)
+                ThrowException("Expected end of <library_geometries> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a geometry from the geometry library.
+void ColladaParser::ReadGeometry(Collada::Mesh* pMesh)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("mesh"))
+            {
+                // read on from there
+                ReadMesh(pMesh);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "geometry") != 0)
+                ThrowException("Expected end of <geometry> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh from the geometry library
+void ColladaParser::ReadMesh(Mesh* pMesh)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("source"))
+            {
+                // we have professionals dealing with this
+                ReadSource();
+            }
+            else if (IsElement("vertices"))
+            {
+                // read per-vertex mesh data
+                ReadVertexData(pMesh);
+            }
+            else if (IsElement("triangles") || IsElement("lines") || IsElement("linestrips")
+                || IsElement("polygons") || IsElement("polylist") || IsElement("trifans") || IsElement("tristrips"))
+            {
+                // read per-index mesh data and faces setup
+                ReadIndexData(pMesh);
+            }
+            else
+            {
+                // ignore the restf
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "technique_common") == 0)
+            {
+                // end of another meaningless element - read over it
+            }
+            else if (strcmp(mReader->getNodeName(), "mesh") == 0)
+            {
+                // end of <mesh> element - we're done here
+                break;
+            }
+            else
+            {
+                // everything else should be punished
+                ThrowException("Expected end of <mesh> element.");
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a source element
+void ColladaParser::ReadSource()
+{
+    int indexID = GetAttribute("id");
+    std::string sourceID = mReader->getAttributeValue(indexID);
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("float_array") || IsElement("IDREF_array") || IsElement("Name_array"))
+            {
+                ReadDataArray();
+            }
+            else if (IsElement("technique_common"))
+            {
+                // I don't care for your profiles
+            }
+            else if (IsElement("accessor"))
+            {
+                ReadAccessor(sourceID);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "source") == 0)
+            {
+                // end of <source> - we're done
+                break;
+            }
+            else if (strcmp(mReader->getNodeName(), "technique_common") == 0)
+            {
+                // end of another meaningless element - read over it
+            }
+            else
+            {
+                // everything else should be punished
+                ThrowException("Expected end of <source> element.");
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a data array holding a number of floats, and stores it in the global library
+void ColladaParser::ReadDataArray()
+{
+    std::string elmName = mReader->getNodeName();
+    bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
+    bool isEmptyElement = mReader->isEmptyElement();
+
+    // read attributes
+    int indexID = GetAttribute("id");
+    std::string id = mReader->getAttributeValue(indexID);
+    int indexCount = GetAttribute("count");
+    unsigned int count = (unsigned int)mReader->getAttributeValueAsInt(indexCount);
+    const char* content = TestTextContent();
+
+    // read values and store inside an array in the data library
+    mDataLibrary[id] = Data();
+    Data& data = mDataLibrary[id];
+    data.mIsStringArray = isStringArray;
+
+    // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
+    if (content)
+    {
+        if (isStringArray)
+        {
+            data.mStrings.reserve(count);
+            std::string s;
+
+            for (unsigned int a = 0; a < count; a++)
+            {
+                if (*content == 0)
+                    ThrowException("Expected more values while reading IDREF_array contents.");
+
+                s.clear();
+                while (!IsSpaceOrNewLine(*content))
+                    s += *content++;
+                data.mStrings.push_back(s);
+
+                SkipSpacesAndLineEnd(&content);
+            }
+        }
+        else
+        {
+            data.mValues.reserve(count);
+
+            for (unsigned int a = 0; a < count; a++)
+            {
+                if (*content == 0)
+                    ThrowException("Expected more values while reading float_array contents.");
+
+                ai_real value;
+                // read a number
+                content = fast_atoreal_move<ai_real>(content, value);
+                data.mValues.push_back(value);
+                // skip whitespace after it
+                SkipSpacesAndLineEnd(&content);
+            }
+        }
+    }
+
+    // test for closing tag
+    if (!isEmptyElement)
+        TestClosing(elmName.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an accessor and stores it in the global library
+void ColladaParser::ReadAccessor(const std::string& pID)
+{
+    // read accessor attributes
+    int attrSource = GetAttribute("source");
+    const char* source = mReader->getAttributeValue(attrSource);
+    if (source[0] != '#')
+        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <accessor> element.");
+    int attrCount = GetAttribute("count");
+    unsigned int count = (unsigned int)mReader->getAttributeValueAsInt(attrCount);
+    int attrOffset = TestAttribute("offset");
+    unsigned int offset = 0;
+    if (attrOffset > -1)
+        offset = (unsigned int)mReader->getAttributeValueAsInt(attrOffset);
+    int attrStride = TestAttribute("stride");
+    unsigned int stride = 1;
+    if (attrStride > -1)
+        stride = (unsigned int)mReader->getAttributeValueAsInt(attrStride);
+
+    // store in the library under the given ID
+    mAccessorLibrary[pID] = Accessor();
+    Accessor& acc = mAccessorLibrary[pID];
+    acc.mCount = count;
+    acc.mOffset = offset;
+    acc.mStride = stride;
+    acc.mSource = source + 1; // ignore the leading '#'
+    acc.mSize = 0; // gets incremented with every param
+
+    // and read the components
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("param"))
+            {
+                // read data param
+                int attrName = TestAttribute("name");
+                std::string name;
+                if (attrName > -1)
+                {
+                    name = mReader->getAttributeValue(attrName);
+
+                    // analyse for common type components and store it's sub-offset in the corresponding field
+
+                    /* Cartesian coordinates */
+                    if (name == "X") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "Y") acc.mSubOffset[1] = acc.mParams.size();
+                    else if (name == "Z") acc.mSubOffset[2] = acc.mParams.size();
+
+                    /* RGBA colors */
+                    else if (name == "R") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "G") acc.mSubOffset[1] = acc.mParams.size();
+                    else if (name == "B") acc.mSubOffset[2] = acc.mParams.size();
+                    else if (name == "A") acc.mSubOffset[3] = acc.mParams.size();
+
+                    /* UVWQ (STPQ) texture coordinates */
+                    else if (name == "S") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "T") acc.mSubOffset[1] = acc.mParams.size();
+                    else if (name == "P") acc.mSubOffset[2] = acc.mParams.size();
+                    //  else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size();
+                        /* 4D uv coordinates are not supported in Assimp */
+
+                        /* Generic extra data, interpreted as UV data, too*/
+                    else if (name == "U") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "V") acc.mSubOffset[1] = acc.mParams.size();
+                    //else
+                    //  DefaultLogger::get()->warn( format() << "Unknown accessor parameter \"" << name << "\". Ignoring data channel." );
+                }
+
+                // read data type
+                int attrType = TestAttribute("type");
+                if (attrType > -1)
+                {
+                    // for the moment we only distinguish between a 4x4 matrix and anything else.
+                    // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
+                    // which should be tested for here.
+                    std::string type = mReader->getAttributeValue(attrType);
+                    if (type == "float4x4")
+                        acc.mSize += 16;
+                    else
+                        acc.mSize += 1;
+                }
+
+                acc.mParams.push_back(name);
+
+                // skip remaining stuff of this element, if any
+                SkipElement();
+            }
+            else
+            {
+                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <accessor>");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "accessor") != 0)
+                ThrowException("Expected end of <accessor> element.");
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-vertex mesh data into the given mesh
+void ColladaParser::ReadVertexData(Mesh* pMesh)
+{
+    // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
+    int attrID = GetAttribute("id");
+    pMesh->mVertexID = mReader->getAttributeValue(attrID);
+
+    // a number of <input> elements
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("input"))
+            {
+                ReadInputChannel(pMesh->mPerVertexData);
+            }
+            else
+            {
+                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <vertices>");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "vertices") != 0)
+                ThrowException("Expected end of <vertices> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-index mesh data into the given mesh
+void ColladaParser::ReadIndexData(Mesh* pMesh)
+{
+    std::vector<size_t> vcount;
+    std::vector<InputChannel> perIndexData;
+
+    // read primitive count from the attribute
+    int attrCount = GetAttribute("count");
+    size_t numPrimitives = (size_t)mReader->getAttributeValueAsInt(attrCount);
+    // some mesh types (e.g. tristrips) don't specify primitive count upfront,
+    // so we need to sum up the actual number of primitives while we read the <p>-tags
+    size_t actualPrimitives = 0;
+
+    // material subgroup
+    int attrMaterial = TestAttribute("material");
+    SubMesh subgroup;
+    if (attrMaterial > -1)
+        subgroup.mMaterial = mReader->getAttributeValue(attrMaterial);
+
+    // distinguish between polys and triangles
+    std::string elementName = mReader->getNodeName();
+    PrimitiveType primType = Prim_Invalid;
+    if (IsElement("lines"))
+        primType = Prim_Lines;
+    else if (IsElement("linestrips"))
+        primType = Prim_LineStrip;
+    else if (IsElement("polygons"))
+        primType = Prim_Polygon;
+    else if (IsElement("polylist"))
+        primType = Prim_Polylist;
+    else if (IsElement("triangles"))
+        primType = Prim_Triangles;
+    else if (IsElement("trifans"))
+        primType = Prim_TriFans;
+    else if (IsElement("tristrips"))
+        primType = Prim_TriStrips;
+
+    ai_assert(primType != Prim_Invalid);
+
+    // also a number of <input> elements, but in addition a <p> primitive collection and probably index counts for all primitives
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("input"))
+            {
+                ReadInputChannel(perIndexData);
+            }
+            else if (IsElement("vcount"))
+            {
+                if (!mReader->isEmptyElement())
+                {
+                    if (numPrimitives)  // It is possible to define a mesh without any primitives
+                    {
+                        // case <polylist> - specifies the number of indices for each polygon
+                        const char* content = GetTextContent();
+                        vcount.reserve(numPrimitives);
+                        for (unsigned int a = 0; a < numPrimitives; a++)
+                        {
+                            if (*content == 0)
+                                ThrowException("Expected more values while reading <vcount> contents.");
+                            // read a number
+                            vcount.push_back((size_t)strtoul10(content, &content));
+                            // skip whitespace after it
+                            SkipSpacesAndLineEnd(&content);
+                        }
+                    }
+
+                    TestClosing("vcount");
+                }
+            }
+            else if (IsElement("p"))
+            {
+                if (!mReader->isEmptyElement())
+                {
+                    // now here the actual fun starts - these are the indices to construct the mesh data from
+                    actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
+                }
+            }
+            else if (IsElement("extra"))
+            {
+                SkipElement("extra");
+            }
+            else if (IsElement("ph")) {
+                SkipElement("ph");
+            }
+            else {
+                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (mReader->getNodeName() != elementName)
+                ThrowException(format() << "Expected end of <" << elementName << "> element.");
+
+            break;
+        }
+    }
+
+#ifdef ASSIMP_BUILD_DEBUG
+    if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip &&
+        primType != Prim_Lines) { // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'.
+        ai_assert(actualPrimitives == numPrimitives);
+    }
+#endif
+
+    // only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
+    subgroup.mNumFaces = actualPrimitives;
+    pMesh->mSubMeshes.push_back(subgroup);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single input channel element and stores it in the given array, if valid
+void ColladaParser::ReadInputChannel(std::vector<InputChannel>& poChannels)
+{
+    InputChannel channel;
+
+    // read semantic
+    int attrSemantic = GetAttribute("semantic");
+    std::string semantic = mReader->getAttributeValue(attrSemantic);
+    channel.mType = GetTypeForSemantic(semantic);
+
+    // read source
+    int attrSource = GetAttribute("source");
+    const char* source = mReader->getAttributeValue(attrSource);
+    if (source[0] != '#')
+        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <input> element.");
+    channel.mAccessor = source + 1; // skipping the leading #, hopefully the remaining text is the accessor ID only
+
+    // read index offset, if per-index <input>
+    int attrOffset = TestAttribute("offset");
+    if (attrOffset > -1)
+        channel.mOffset = mReader->getAttributeValueAsInt(attrOffset);
+
+    // read set if texture coordinates
+    if (channel.mType == IT_Texcoord || channel.mType == IT_Color) {
+        int attrSet = TestAttribute("set");
+        if (attrSet > -1) {
+            attrSet = mReader->getAttributeValueAsInt(attrSet);
+            if (attrSet < 0)
+                ThrowException(format() << "Invalid index \"" << (attrSet) << "\" in set attribute of <input> element");
+
+            channel.mIndex = attrSet;
+        }
+    }
+
+    // store, if valid type
+    if (channel.mType != IT_Invalid)
+        poChannels.push_back(channel);
+
+    // skip remaining stuff of this element, if any
+    SkipElement();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a <p> primitive index list and assembles the mesh data into the given mesh
+size_t ColladaParser::ReadPrimitives(Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
+    size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
+{
+    // determine number of indices coming per vertex
+    // find the offset index for all per-vertex channels
+    size_t numOffsets = 1;
+    size_t perVertexOffset = SIZE_MAX; // invalid value
+    for (const InputChannel& channel : pPerIndexChannels)
+    {
+        numOffsets = std::max(numOffsets, channel.mOffset + 1);
+        if (channel.mType == IT_Vertex)
+            perVertexOffset = channel.mOffset;
+    }
+
+    // determine the expected number of indices
+    size_t expectedPointCount = 0;
+    switch (pPrimType)
+    {
+    case Prim_Polylist:
+    {
+        for (size_t i : pVCount)
+            expectedPointCount += i;
+        break;
+    }
+    case Prim_Lines:
+        expectedPointCount = 2 * pNumPrimitives;
+        break;
+    case Prim_Triangles:
+        expectedPointCount = 3 * pNumPrimitives;
+        break;
+    default:
+        // other primitive types don't state the index count upfront... we need to guess
+        break;
+    }
+
+    // and read all indices into a temporary array
+    std::vector<size_t> indices;
+    if (expectedPointCount > 0)
+        indices.reserve(expectedPointCount * numOffsets);
+
+    if (pNumPrimitives > 0) // It is possible to not contain any indices
+    {
+        const char* content = GetTextContent();
+        while (*content != 0)
+        {
+            // read a value.
+            // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
+            int value = std::max(0, strtol10(content, &content));
+            indices.push_back(size_t(value));
+            // skip whitespace after it
+            SkipSpacesAndLineEnd(&content);
+        }
+    }
+
+    // complain if the index count doesn't fit
+    if (expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) {
+        if (pPrimType == Prim_Lines) {
+            // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
+            ReportWarning("Expected different index count in <p> element, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets);
+            pNumPrimitives = (indices.size() / numOffsets) / 2;
+        }
+        else
+            ThrowException("Expected different index count in <p> element.");
+
+    }
+    else if (expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
+        ThrowException("Expected different index count in <p> element.");
+
+    // find the data for all sources
+    for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+    {
+        InputChannel& input = *it;
+        if (input.mResolved)
+            continue;
+
+        // find accessor
+        input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
+        // resolve accessor's data pointer as well, if necessary
+        const Accessor* acc = input.mResolved;
+        if (!acc->mData)
+            acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
+    }
+    // and the same for the per-index channels
+    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+    {
+        InputChannel& input = *it;
+        if (input.mResolved)
+            continue;
+
+        // ignore vertex pointer, it doesn't refer to an accessor
+        if (input.mType == IT_Vertex)
+        {
+            // warn if the vertex channel does not refer to the <vertices> element in the same mesh
+            if (input.mAccessor != pMesh->mVertexID)
+                ThrowException("Unsupported vertex referencing scheme.");
+            continue;
+        }
+
+        // find accessor
+        input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
+        // resolve accessor's data pointer as well, if necessary
+        const Accessor* acc = input.mResolved;
+        if (!acc->mData)
+            acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
+    }
+
+    // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
+    size_t numPrimitives = pNumPrimitives;
+    if (pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
+        numPrimitives = 1;
+    // For continued primitives, the given count is actually the number of <p>'s inside the parent tag
+    if (pPrimType == Prim_TriStrips) {
+        size_t numberOfVertices = indices.size() / numOffsets;
+        numPrimitives = numberOfVertices - 2;
+    }
+    if (pPrimType == Prim_LineStrip) {
+        size_t numberOfVertices = indices.size() / numOffsets;
+        numPrimitives = numberOfVertices - 1;
+    }
+
+    pMesh->mFaceSize.reserve(numPrimitives);
+    pMesh->mFacePosIndices.reserve(indices.size() / numOffsets);
+
+    size_t polylistStartVertex = 0;
+    for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++)
+    {
+        // determine number of points for this primitive
+        size_t numPoints = 0;
+        switch (pPrimType)
+        {
+        case Prim_Lines:
+            numPoints = 2;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_LineStrip:
+            numPoints = 2;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_Triangles:
+            numPoints = 3;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_TriStrips:
+            numPoints = 3;
+            ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_Polylist:
+            numPoints = pVCount[currentPrimitive];
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
+            polylistStartVertex += numPoints;
+            break;
+        case Prim_TriFans:
+        case Prim_Polygon:
+            numPoints = indices.size() / numOffsets;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        default:
+            // LineStrip is not supported due to expected index unmangling
+            ThrowException("Unsupported primitive type.");
+            break;
+        }
+
+        // store the face size to later reconstruct the face from
+        pMesh->mFaceSize.push_back(numPoints);
+    }
+
+    // if I ever get my hands on that guy who invented this steaming pile of indirection...
+    TestClosing("p");
+    return numPrimitives;
+}
+
+///@note This function willn't work correctly if both PerIndex and PerVertex channels have same channels.
+///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
+///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior
+void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices) {
+    // calculate the base offset of the vertex whose attributes we ant to copy
+    size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
+
+    // don't overrun the boundaries of the index list
+    ai_assert((baseOffset + numOffsets - 1) < indices.size());
+
+    // extract per-vertex channels using the global per-vertex offset
+    for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+        ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
+    // and extract per-index channels using there specified offset
+    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+        ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
+
+    // store the vertex-data index for later assignment of bone vertex weights
+    pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
+}
+
+void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices) {
+    if (currentPrimitive % 2 != 0) {
+        //odd tristrip triangles need their indices mangled, to preserve winding direction
+        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+    }
+    else {//for non tristrips or even tristrip triangles
+        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extracts a single object from an input channel and stores it in the appropriate mesh data array
+void ColladaParser::ExtractDataObjectFromChannel(const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
+{
+    // ignore vertex referrer - we handle them that separate
+    if (pInput.mType == IT_Vertex)
+        return;
+
+    const Accessor& acc = *pInput.mResolved;
+    if (pLocalIndex >= acc.mCount)
+        ThrowException(format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification");
+
+    // get a pointer to the start of the data object referred to by the accessor and the local index
+    const ai_real* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex * acc.mStride;
+
+    // assemble according to the accessors component sub-offset list. We don't care, yet,
+    // what kind of object exactly we're extracting here
+    ai_real obj[4];
+    for (size_t c = 0; c < 4; ++c)
+        obj[c] = dataObject[acc.mSubOffset[c]];
+
+    // now we reinterpret it according to the type we're reading here
+    switch (pInput.mType)
+    {
+    case IT_Position: // ignore all position streams except 0 - there can be only one position
+        if (pInput.mIndex == 0)
+            pMesh->mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported");
+        break;
+    case IT_Normal:
+        // pad to current vertex count if necessary
+        if (pMesh->mNormals.size() < pMesh->mPositions.size() - 1)
+            pMesh->mNormals.insert(pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D(0, 1, 0));
+
+        // ignore all normal streams except 0 - there can be only one normal
+        if (pInput.mIndex == 0)
+            pMesh->mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported");
+        break;
+    case IT_Tangent:
+        // pad to current vertex count if necessary
+        if (pMesh->mTangents.size() < pMesh->mPositions.size() - 1)
+            pMesh->mTangents.insert(pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D(1, 0, 0));
+
+        // ignore all tangent streams except 0 - there can be only one tangent
+        if (pInput.mIndex == 0)
+            pMesh->mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported");
+        break;
+    case IT_Bitangent:
+        // pad to current vertex count if necessary
+        if (pMesh->mBitangents.size() < pMesh->mPositions.size() - 1)
+            pMesh->mBitangents.insert(pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D(0, 0, 1));
+
+        // ignore all bitangent streams except 0 - there can be only one bitangent
+        if (pInput.mIndex == 0)
+            pMesh->mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported");
+        break;
+    case IT_Texcoord:
+        // up to 4 texture coord sets are fine, ignore the others
+        if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
+        {
+            // pad to current vertex count if necessary
+            if (pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size() - 1)
+                pMesh->mTexCoords[pInput.mIndex].insert(pMesh->mTexCoords[pInput.mIndex].end(),
+                    pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0));
+
+            pMesh->mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2]));
+            if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
+                pMesh->mNumUVComponents[pInput.mIndex] = 3;
+        }
+        else
+        {
+            ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping.");
+        }
+        break;
+    case IT_Color:
+        // up to 4 color sets are fine, ignore the others
+        if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
+        {
+            // pad to current vertex count if necessary
+            if (pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size() - 1)
+                pMesh->mColors[pInput.mIndex].insert(pMesh->mColors[pInput.mIndex].end(),
+                    pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1));
+
+            aiColor4D result(0, 0, 0, 1);
+            for (size_t i = 0; i < pInput.mResolved->mSize; ++i)
+            {
+                result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
+            }
+            pMesh->mColors[pInput.mIndex].push_back(result);
+        }
+        else
+        {
+            ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping.");
+        }
+
+        break;
+    default:
+        // IT_Invalid and IT_Vertex
+        ai_assert(false && "shouldn't ever get here");
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the library of node hierarchies and scene parts
+void ColladaParser::ReadSceneLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // a visual scene - generate root node under its ID and let ReadNode() do the recursive work
+            if (IsElement("visual_scene"))
+            {
+                // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
+                int indexID = GetAttribute("id");
+                const char* attrID = mReader->getAttributeValue(indexID);
+
+                // read name if given.
+                int indexName = TestAttribute("name");
+                const char* attrName = "unnamed";
+                if (indexName > -1)
+                    attrName = mReader->getAttributeValue(indexName);
+
+                // create a node and store it in the library under its ID
+                Node* node = new Node;
+                node->mID = attrID;
+                node->mName = attrName;
+                mNodeLibrary[node->mID] = node;
+
+                ReadSceneNode(node);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_visual_scenes") == 0)
+                //ThrowException( "Expected end of \"library_visual_scenes\" element.");
+
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a scene node's contents including children and stores it in the given node
+void ColladaParser::ReadSceneNode(Node* pNode)
+{
+    // quit immediately on <bla/> elements
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("node"))
+            {
+                Node* child = new Node;
+                int attrID = TestAttribute("id");
+                if (attrID > -1)
+                    child->mID = mReader->getAttributeValue(attrID);
+                int attrSID = TestAttribute("sid");
+                if (attrSID > -1)
+                    child->mSID = mReader->getAttributeValue(attrSID);
+
+                int attrName = TestAttribute("name");
+                if (attrName > -1)
+                    child->mName = mReader->getAttributeValue(attrName);
+
+                // TODO: (thom) support SIDs
+                // ai_assert( TestAttribute( "sid") == -1);
+
+                if (pNode)
+                {
+                    pNode->mChildren.push_back(child);
+                    child->mParent = pNode;
+                }
+                else
+                {
+                    // no parent node given, probably called from <library_nodes> element.
+                    // create new node in node library
+                    mNodeLibrary[child->mID] = child;
+                }
+
+                // read on recursively from there
+                ReadSceneNode(child);
+                continue;
+            }
+            // For any further stuff we need a valid node to work on
+            else if (!pNode)
+                continue;
+
+            if (IsElement("lookat"))
+                ReadNodeTransformation(pNode, TF_LOOKAT);
+            else if (IsElement("matrix"))
+                ReadNodeTransformation(pNode, TF_MATRIX);
+            else if (IsElement("rotate"))
+                ReadNodeTransformation(pNode, TF_ROTATE);
+            else if (IsElement("scale"))
+                ReadNodeTransformation(pNode, TF_SCALE);
+            else if (IsElement("skew"))
+                ReadNodeTransformation(pNode, TF_SKEW);
+            else if (IsElement("translate"))
+                ReadNodeTransformation(pNode, TF_TRANSLATE);
+            else if (IsElement("render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
+            {
+                // ... scene evaluation or, in other words, postprocessing pipeline,
+                // or, again in other words, a turing-complete description how to
+                // render a Collada scene. The only thing that is interesting for
+                // us is the primary camera.
+                int attrId = TestAttribute("camera_node");
+                if (-1 != attrId)
+                {
+                    const char* s = mReader->getAttributeValue(attrId);
+                    if (s[0] != '#')
+                        ASSIMP_LOG_ERROR("Collada: Unresolved reference format of camera");
+                    else
+                        pNode->mPrimaryCamera = s + 1;
+                }
+            }
+            else if (IsElement("instance_node"))
+            {
+                // find the node in the library
+                int attrID = TestAttribute("url");
+                if (attrID != -1)
+                {
+                    const char* s = mReader->getAttributeValue(attrID);
+                    if (s[0] != '#')
+                        ASSIMP_LOG_ERROR("Collada: Unresolved reference format of node");
+                    else
+                    {
+                        pNode->mNodeInstances.push_back(NodeInstance());
+                        pNode->mNodeInstances.back().mNode = s + 1;
+                    }
+                }
+            }
+            else if (IsElement("instance_geometry") || IsElement("instance_controller"))
+            {
+                // Reference to a mesh or controller, with possible material associations
+                ReadNodeGeometry(pNode);
+            }
+            else if (IsElement("instance_light"))
+            {
+                // Reference to a light, name given in 'url' attribute
+                int attrID = TestAttribute("url");
+                if (-1 == attrID)
+                    ASSIMP_LOG_WARN("Collada: Expected url attribute in <instance_light> element");
+                else
+                {
+                    const char* url = mReader->getAttributeValue(attrID);
+                    if (url[0] != '#')
+                        ThrowException("Unknown reference format in <instance_light> element");
+
+                    pNode->mLights.push_back(LightInstance());
+                    pNode->mLights.back().mLight = url + 1;
+                }
+            }
+            else if (IsElement("instance_camera"))
+            {
+                // Reference to a camera, name given in 'url' attribute
+                int attrID = TestAttribute("url");
+                if (-1 == attrID)
+                    ASSIMP_LOG_WARN("Collada: Expected url attribute in <instance_camera> element");
+                else
+                {
+                    const char* url = mReader->getAttributeValue(attrID);
+                    if (url[0] != '#')
+                        ThrowException("Unknown reference format in <instance_camera> element");
+
+                    pNode->mCameras.push_back(CameraInstance());
+                    pNode->mCameras.back().mCamera = url + 1;
+                }
+            }
+            else
+            {
+                // skip everything else for the moment
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
+void ColladaParser::ReadNodeTransformation(Node* pNode, TransformType pType)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    std::string tagName = mReader->getNodeName();
+
+    Transform tf;
+    tf.mType = pType;
+
+    // read SID
+    int indexSID = TestAttribute("sid");
+    if (indexSID >= 0)
+        tf.mID = mReader->getAttributeValue(indexSID);
+
+    // how many parameters to read per transformation type
+    static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
+    const char* content = GetTextContent();
+
+    // read as many parameters and store in the transformation
+    for (unsigned int a = 0; a < sNumParameters[pType]; a++)
+    {
+        // read a number
+        content = fast_atoreal_move<ai_real>(content, tf.f[a]);
+        // skip whitespace after it
+        SkipSpacesAndLineEnd(&content);
+    }
+
+    // place the transformation at the queue of the node
+    pNode->mTransforms.push_back(tf);
+
+    // and consume the closing tag
+    TestClosing(tagName.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Processes bind_vertex_input and bind elements
+void ColladaParser::ReadMaterialVertexInputBinding(Collada::SemanticMappingTable& tbl)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("bind_vertex_input"))
+            {
+                Collada::InputSemanticMapEntry vn;
+
+                // effect semantic
+                int n = GetAttribute("semantic");
+                std::string s = mReader->getAttributeValue(n);
+
+                // input semantic
+                n = GetAttribute("input_semantic");
+                vn.mType = GetTypeForSemantic(mReader->getAttributeValue(n));
+
+                // index of input set
+                n = TestAttribute("input_set");
+                if (-1 != n)
+                    vn.mSet = mReader->getAttributeValueAsInt(n);
+
+                tbl.mMap[s] = vn;
+            }
+            else if (IsElement("bind")) {
+                ASSIMP_LOG_WARN("Collada: Found unsupported <bind> element");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "instance_material") == 0)
+                break;
+        }
+    }
+}
+
+void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem& zip_archive)
+{
+    // Attempt to load any undefined Collada::Image in ImageLibrary
+    for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) {
+        Collada::Image &image = (*it).second;
+
+        if (image.mImageData.empty()) {
+            std::unique_ptr<IOStream> image_file(zip_archive.Open(image.mFileName.c_str()));
+            if (image_file) {
+                image.mImageData.resize(image_file->FileSize());
+                image_file->Read(image.mImageData.data(), image_file->FileSize(), 1);
+                image.mEmbeddedFormat = BaseImporter::GetExtension(image.mFileName);
+                if (image.mEmbeddedFormat == "jpeg") {
+                    image.mEmbeddedFormat = "jpg";
+                }
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh reference in a node and adds it to the node's mesh list
+void ColladaParser::ReadNodeGeometry(Node* pNode)
+{
+    // referred mesh is given as an attribute of the <instance_geometry> element
+    int attrUrl = GetAttribute("url");
+    const char* url = mReader->getAttributeValue(attrUrl);
+    if (url[0] != '#')
+        ThrowException("Unknown reference format");
+
+    Collada::MeshInstance instance;
+    instance.mMeshOrController = url + 1; // skipping the leading #
+
+    if (!mReader->isEmptyElement())
+    {
+        // read material associations. Ignore additional elements in between
+        while (mReader->read())
+        {
+            if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+            {
+                if (IsElement("instance_material"))
+                {
+                    // read ID of the geometry subgroup and the target material
+                    int attrGroup = GetAttribute("symbol");
+                    std::string group = mReader->getAttributeValue(attrGroup);
+                    int attrMaterial = GetAttribute("target");
+                    const char* urlMat = mReader->getAttributeValue(attrMaterial);
+                    Collada::SemanticMappingTable s;
+                    if (urlMat[0] == '#')
+                        urlMat++;
+
+                    s.mMatName = urlMat;
+
+                    // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff
+                    if (!mReader->isEmptyElement())
+                        ReadMaterialVertexInputBinding(s);
+
+                    // store the association
+                    instance.mMaterials[group] = s;
+                }
+            }
+            else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+            {
+                if (strcmp(mReader->getNodeName(), "instance_geometry") == 0
+                    || strcmp(mReader->getNodeName(), "instance_controller") == 0)
+                    break;
+            }
+        }
+    }
+
+    // store it
+    pNode->mMeshes.push_back(instance);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the collada scene
+void ColladaParser::ReadScene()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("instance_visual_scene"))
+            {
+                // should be the first and only occurrence
+                if (mRootNode)
+                    ThrowException("Invalid scene containing multiple root nodes in <instance_visual_scene> element");
+
+                // read the url of the scene to instance. Should be of format "#some_name"
+                int urlIndex = GetAttribute("url");
+                const char* url = mReader->getAttributeValue(urlIndex);
+                if (url[0] != '#')
+                    ThrowException("Unknown reference format in <instance_visual_scene> element");
+
+                // find the referred scene, skip the leading #
+                NodeLibrary::const_iterator sit = mNodeLibrary.find(url + 1);
+                if (sit == mNodeLibrary.end())
+                    ThrowException("Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
+                mRootNode = sit->second;
+            }
+            else {
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+AI_WONT_RETURN void ColladaParser::ThrowException(const std::string& pError) const {
+    throw DeadlyImportError(format() << "Collada: " << mFileName << " - " << pError);
+}
+
+void ColladaParser::ReportWarning(const char* msg, ...) {
+    ai_assert(nullptr != msg);
+
+    va_list args;
+    va_start(args, msg);
+
+    char szBuffer[3000];
+    const int iLen = vsprintf(szBuffer, msg, args);
+    ai_assert(iLen > 0);
+
+    va_end(args);
+    ASSIMP_LOG_WARN_F("Validation warning: ", std::string(szBuffer, iLen));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skips all data until the end node of the current element
+void ColladaParser::SkipElement() {
+    // nothing to skip if it's an <element />
+    if (mReader->isEmptyElement()) {
+        return;
+    }
+
+    // reroute
+    SkipElement(mReader->getNodeName());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skips all data until the end node of the given element
+void ColladaParser::SkipElement(const char* pElement) {
+    // copy the current node's name because it'a pointer to the reader's internal buffer,
+    // which is going to change with the upcoming parsing
+    std::string element = pElement;
+    while (mReader->read()) {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (mReader->getNodeName() == element) {
+                break;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for an opening element of the given name, throws an exception if not found
+void ColladaParser::TestOpening(const char* pName) {
+    // read element start
+    if (!mReader->read()) {
+        ThrowException(format() << "Unexpected end of file while beginning of <" << pName << "> element.");
+    }
+    // whitespace in front is ok, just read again if found
+    if (mReader->getNodeType() == irr::io::EXN_TEXT) {
+        if (!mReader->read()) {
+            ThrowException(format() << "Unexpected end of file while reading beginning of <" << pName << "> element.");
+        }
+    }
+
+    if (mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp(mReader->getNodeName(), pName) != 0) {
+        ThrowException(format() << "Expected start of <" << pName << "> element.");
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for the closing tag of the given element, throws an exception if not found
+void ColladaParser::TestClosing(const char* pName) {
+    // check if we have an empty (self-closing) element
+    if (mReader->isEmptyElement()) {
+        return;
+    }
+
+    // check if we're already on the closing tag and return right away
+    if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp(mReader->getNodeName(), pName) == 0) {
+        return;
+    }
+
+    // if not, read some more
+    if (!mReader->read()) {
+        ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
+    }
+    // whitespace in front is ok, just read again if found
+    if (mReader->getNodeType() == irr::io::EXN_TEXT) {
+        if (!mReader->read()) {
+            ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
+        }
+    }
+
+    // but this has the be the closing tag, or we're lost
+    if (mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp(mReader->getNodeName(), pName) != 0) {
+        ThrowException(format() << "Expected end of <" << pName << "> element.");
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
+int ColladaParser::GetAttribute(const char* pAttr) const {
+    int index = TestAttribute(pAttr);
+    if (index != -1) {
+        return index;
+    }
+
+    // attribute not found -> throw an exception
+    ThrowException(format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">.");
+    return -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found
+int ColladaParser::TestAttribute(const char* pAttr) const
+{
+    for (int a = 0; a < mReader->getAttributeCount(); a++)
+        if (strcmp(mReader->getAttributeName(a), pAttr) == 0)
+            return a;
+
+    return -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace.
+const char* ColladaParser::GetTextContent()
+{
+    const char* sz = TestTextContent();
+    if (!sz) {
+        ThrowException("Invalid contents in element \"n\".");
+    }
+    return sz;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the text contents of an element, returns NULL if not given. Skips leading whitespace.
+const char* ColladaParser::TestTextContent()
+{
+    // present node should be the beginning of an element
+    if (mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement())
+        return NULL;
+
+    // read contents of the element
+    if (!mReader->read())
+        return NULL;
+    if (mReader->getNodeType() != irr::io::EXN_TEXT && mReader->getNodeType() != irr::io::EXN_CDATA)
+        return NULL;
+
+    // skip leading whitespace
+    const char* text = mReader->getNodeData();
+    SkipSpacesAndLineEnd(&text);
+
+    return text;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates the resulting transformation fromm all the given transform steps
+aiMatrix4x4 ColladaParser::CalculateResultTransform(const std::vector<Transform>& pTransforms) const
+{
+    aiMatrix4x4 res;
+
+    for (std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it)
+    {
+        const Transform& tf = *it;
+        switch (tf.mType)
+        {
+        case TF_LOOKAT:
+        {
+            aiVector3D pos(tf.f[0], tf.f[1], tf.f[2]);
+            aiVector3D dstPos(tf.f[3], tf.f[4], tf.f[5]);
+            aiVector3D up = aiVector3D(tf.f[6], tf.f[7], tf.f[8]).Normalize();
+            aiVector3D dir = aiVector3D(dstPos - pos).Normalize();
+            aiVector3D right = (dir ^ up).Normalize();
+
+            res *= aiMatrix4x4(
+                right.x, up.x, -dir.x, pos.x,
+                right.y, up.y, -dir.y, pos.y,
+                right.z, up.z, -dir.z, pos.z,
+                0, 0, 0, 1);
+            break;
+        }
+        case TF_ROTATE:
+        {
+            aiMatrix4x4 rot;
+            ai_real angle = tf.f[3] * ai_real(AI_MATH_PI) / ai_real(180.0);
+            aiVector3D axis(tf.f[0], tf.f[1], tf.f[2]);
+            aiMatrix4x4::Rotation(angle, axis, rot);
+            res *= rot;
+            break;
+        }
+        case TF_TRANSLATE:
+        {
+            aiMatrix4x4 trans;
+            aiMatrix4x4::Translation(aiVector3D(tf.f[0], tf.f[1], tf.f[2]), trans);
+            res *= trans;
+            break;
+        }
+        case TF_SCALE:
+        {
+            aiMatrix4x4 scale(tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
+                0.0f, 0.0f, 0.0f, 1.0f);
+            res *= scale;
+            break;
+        }
+        case TF_SKEW:
+            // TODO: (thom)
+            ai_assert(false);
+            break;
+        case TF_MATRIX:
+        {
+            aiMatrix4x4 mat(tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
+                tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
+            res *= mat;
+            break;
+        }
+        default:
+            ai_assert(false);
+            break;
+        }
+    }
+
+    return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Determines the input data type for the given semantic string
+Collada::InputType ColladaParser::GetTypeForSemantic(const std::string& semantic)
+{
+    if (semantic.empty()) {
+        ASSIMP_LOG_WARN("Vertex input type is empty.");
+        return IT_Invalid;
+    }
+
+    if (semantic == "POSITION")
+        return IT_Position;
+    else if (semantic == "TEXCOORD")
+        return IT_Texcoord;
+    else if (semantic == "NORMAL")
+        return IT_Normal;
+    else if (semantic == "COLOR")
+        return IT_Color;
+    else if (semantic == "VERTEX")
+        return IT_Vertex;
+    else if (semantic == "BINORMAL" || semantic == "TEXBINORMAL")
+        return IT_Bitangent;
+    else if (semantic == "TANGENT" || semantic == "TEXTANGENT")
+        return IT_Tangent;
+
+    ASSIMP_LOG_WARN_F("Unknown vertex input type \"", semantic, "\". Ignoring.");
+    return IT_Invalid;
+}
+
+#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

+ 27 - 2
code/ColladaParser.h → code/Collada/ColladaParser.h

@@ -2,7 +2,7 @@
  Open Asset Import Library (assimp)
  ----------------------------------------------------------------------
 
- Copyright (c) 2006-2018, assimp team
+ Copyright (c) 2006-2019, assimp team
 
 
  All rights reserved.
@@ -54,6 +54,7 @@
 
 namespace Assimp
 {
+    class ZipArchiveIOSystem;
 
     // ------------------------------------------------------------------------------------------
     /** Parser helper class for the Collada loader.
@@ -65,13 +66,22 @@ namespace Assimp
     {
         friend class ColladaLoader;
 
+        /** Converts a path read from a collada file to the usual representation */
+        static void UriDecodePath(aiString& ss);
+
     protected:
+        /** Map for generic metadata as aiString */
+        typedef std::map<std::string, aiString> StringMetaData;
+
         /** Constructor from XML file */
-        ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
+        ColladaParser(IOSystem* pIOHandler, const std::string& pFile);
 
         /** Destructor */
         ~ColladaParser();
 
+        /** Attempts to read the ZAE manifest and returns the DAE to open */
+        static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
+
         /** Reads the contents of the file */
         void ReadContents();
 
@@ -81,6 +91,15 @@ namespace Assimp
         /** Reads asset information such as coordinate system information and legal blah */
         void ReadAssetInfo();
 
+        /** Reads contributor information such as author and legal blah */
+        void ReadContributorInfo();
+
+        /** Reads generic metadata into provided map */
+        void ReadMetaDataItem(StringMetaData &metadata);
+
+        /** Convert underscore_seperated to CamelCase "authoring_tool" becomes "AuthoringTool" */
+        static void ToCamelCase(std::string &text);
+
         /** Reads the animation library */
         void ReadAnimationLibrary();
 
@@ -223,6 +242,9 @@ namespace Assimp
         // Processes bind_vertex_input and bind elements
         void ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl);
 
+        /** Reads embedded textures from a ZAE archive*/
+        void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
+
     protected:
         /** Aborts the file reading with an exception */
         AI_WONT_RETURN void ThrowException( const std::string& pError) const AI_WONT_RETURN_SUFFIX;
@@ -343,6 +365,9 @@ namespace Assimp
         /** Which is the up vector */
         enum { UP_X, UP_Y, UP_Z } mUpDirection;
 
+        /** Asset metadata (global for scene) */
+        StringMetaData mAssetMetaData;
+
         /** Collada file format version */
         Collada::FormatVersion mFormat;
     };

+ 0 - 3216
code/ColladaParser.cpp

@@ -1,3216 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2018, assimp team
-
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
-copyright notice, this list of conditions and the
-following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the
-following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-contributors may be used to endorse or promote products
-derived from this software without specific prior
-written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/** @file ColladaParser.cpp
- *  @brief Implementation of the Collada parser helper
- */
-
-#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
-
-#include <sstream>
-#include <stdarg.h>
-#include "ColladaParser.h"
-#include <assimp/fast_atof.h>
-#include <assimp/ParsingUtils.h>
-#include <assimp/StringUtils.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/IOSystem.hpp>
-#include <assimp/light.h>
-#include <assimp/TinyFormatter.h>
-
-#include <memory>
-
-using namespace Assimp;
-using namespace Assimp::Collada;
-using namespace Assimp::Formatter;
-
-// ------------------------------------------------------------------------------------------------
-// Constructor to be privately used by Importer
-ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
-    : mFileName( pFile )
-    , mReader( NULL )
-    , mDataLibrary()
-    , mAccessorLibrary()
-    , mMeshLibrary()
-    , mNodeLibrary()
-    , mImageLibrary()
-    , mEffectLibrary()
-    , mMaterialLibrary()
-    , mLightLibrary()
-    , mCameraLibrary()
-    , mControllerLibrary()
-    , mRootNode( NULL )
-    , mAnims()
-    , mUnitSize( 1.0f )
-    , mUpDirection( UP_Y )
-    , mFormat(FV_1_5_n )    // We assume the newest file format by default
-{
-    // validate io-handler instance
-    if ( NULL == pIOHandler ) {
-        throw DeadlyImportError("IOSystem is NULL." );
-    }
-
-    // open the file
-    std::unique_ptr<IOStream> file( pIOHandler->Open(pFile ) );
-    if (file.get() == NULL) {
-        throw DeadlyImportError( "Failed to open 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) {
-        ThrowException("Collada: Unable to open file.");
-    }
-
-    // start reading
-    ReadContents();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-ColladaParser::~ColladaParser()
-{
-    delete mReader;
-    for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
-        delete it->second;
-    for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
-        delete it->second;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Read bool from text contents of current element
-bool ColladaParser::ReadBoolFromTextContent()
-{
-    const char* cur = GetTextContent();
-    return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Read float from text contents of current element
-ai_real ColladaParser::ReadFloatFromTextContent()
-{
-    const char* cur = GetTextContent();
-    return fast_atof(cur);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the contents of the file
-void ColladaParser::ReadContents()
-{
-    while( mReader->read())
-    {
-        // handle the root element "COLLADA"
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "COLLADA"))
-            {
-                // check for 'version' attribute
-                const int attrib = TestAttribute("version");
-                if (attrib != -1) {
-                    const char* version = mReader->getAttributeValue(attrib);
-
-                    if (!::strncmp(version,"1.5",3)) {
-                        mFormat =  FV_1_5_n;
-                        DefaultLogger::get()->debug("Collada schema version is 1.5.n");
-                    }
-                    else if (!::strncmp(version,"1.4",3)) {
-                        mFormat =  FV_1_4_n;
-                        DefaultLogger::get()->debug("Collada schema version is 1.4.n");
-                    }
-                    else if (!::strncmp(version,"1.3",3)) {
-                        mFormat =  FV_1_3_n;
-                        DefaultLogger::get()->debug("Collada schema version is 1.3.n");
-                    }
-                }
-
-                ReadStructure();
-            } else
-            {
-                DefaultLogger::get()->debug( format() << "Ignoring global element <" << mReader->getNodeName() << ">." );
-                SkipElement();
-            }
-        } else
-        {
-            // skip everything else silently
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the structure of the file
-void ColladaParser::ReadStructure()
-{
-    while( mReader->read())
-    {
-        // beginning of elements
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "asset"))
-                ReadAssetInfo();
-            else if( IsElement( "library_animations"))
-                ReadAnimationLibrary();
-			else if (IsElement("library_animation_clips"))
-				ReadAnimationClipLibrary();
-            else if( IsElement( "library_controllers"))
-                ReadControllerLibrary();
-            else if( IsElement( "library_images"))
-                ReadImageLibrary();
-            else if( IsElement( "library_materials"))
-                ReadMaterialLibrary();
-            else if( IsElement( "library_effects"))
-                ReadEffectLibrary();
-            else if( IsElement( "library_geometries"))
-                ReadGeometryLibrary();
-            else if( IsElement( "library_visual_scenes"))
-                ReadSceneLibrary();
-            else if( IsElement( "library_lights"))
-                ReadLightLibrary();
-            else if( IsElement( "library_cameras"))
-                ReadCameraLibrary();
-            else if( IsElement( "library_nodes"))
-                ReadSceneNode(NULL); /* some hacking to reuse this piece of code */
-            else if( IsElement( "scene"))
-                ReadScene();
-            else
-                SkipElement();
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            break;
-        }
-    }
-
-	PostProcessRootAnimations();
-    PostProcessControllers();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads asset information such as coordinate system information and legal blah
-void ColladaParser::ReadAssetInfo()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "unit"))
-            {
-                // read unit data from the element's attributes
-                const int attrIndex = TestAttribute( "meter");
-                if (attrIndex == -1) {
-                    mUnitSize = 1.f;
-                }
-                else {
-                    mUnitSize = mReader->getAttributeValueAsFloat( attrIndex);
-                }
-
-                // consume the trailing stuff
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else if( IsElement( "up_axis"))
-            {
-                // read content, strip whitespace, compare
-                const char* content = GetTextContent();
-                if( strncmp( content, "X_UP", 4) == 0)
-                    mUpDirection = UP_X;
-                else if( strncmp( content, "Z_UP", 4) == 0)
-                    mUpDirection = UP_Z;
-                else
-                    mUpDirection = UP_Y;
-
-                // check element end
-                TestClosing( "up_axis");
-            } else
-            {
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "asset") != 0)
-                ThrowException( "Expected end of <asset> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the animation clips
-void ColladaParser::ReadAnimationClipLibrary()
-{
-	if (mReader->isEmptyElement())
-		return;
-
-	while (mReader->read())
-	{
-		if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
-		{
-			if (IsElement("animation_clip"))
-			{
-				// optional name given as an attribute
-				std::string animName;
-				int indexName = TestAttribute("name");
-				int indexID = TestAttribute("id");
-				if (indexName >= 0)
-					animName = mReader->getAttributeValue(indexName);
-				else if (indexID >= 0)
-					animName = mReader->getAttributeValue(indexID);
-				else
-					animName = std::string("animation_") + to_string(mAnimationClipLibrary.size());
-
-				std::pair<std::string, std::vector<std::string> > clip;
-
-				clip.first = animName;
-
-				while (mReader->read())
-				{
-					if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
-					{
-						if (IsElement("instance_animation"))
-						{
-							int indexUrl = TestAttribute("url");
-							if (indexUrl >= 0)
-							{
-								const char* url = mReader->getAttributeValue(indexUrl);
-								if (url[0] != '#')
-									ThrowException("Unknown reference format");
-
-								url++;
-
-								clip.second.push_back(url);
-							}
-						}
-						else
-						{
-							// ignore the rest
-							SkipElement();
-						}
-					}
-					else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-					{
-						if (strcmp(mReader->getNodeName(), "animation_clip") != 0)
-							ThrowException("Expected end of <animation_clip> element.");
-
-						break;
-					}
-				}
-
-				if (clip.second.size() > 0)
-				{
-					mAnimationClipLibrary.push_back(clip);
-				}
-			}
-			else
-			{
-				// ignore the rest
-				SkipElement();
-			}
-		}
-		else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-		{
-			if (strcmp(mReader->getNodeName(), "library_animation_clips") != 0)
-				ThrowException("Expected end of <library_animation_clips> element.");
-
-			break;
-		}
-	}
-}
-
-void ColladaParser::PostProcessControllers()
-{
-  for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it)
-  {
-    std::string meshId = it->second.mMeshId;
-    ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
-    while(findItr != mControllerLibrary.end()) {
-      meshId = findItr->second.mMeshId;
-      findItr = mControllerLibrary.find(meshId);
-    }
-    
-    it->second.mMeshId = meshId;
-  }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Re-build animations from animation clip library, if present, otherwise combine single-channel animations
-void ColladaParser::PostProcessRootAnimations()
-{
-	if (mAnimationClipLibrary.size() > 0)
-	{
-		Animation temp;
-
-		for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it)
-		{
-			std::string clipName = it->first;
-
-			Animation *clip = new Animation();
-			clip->mName = clipName;
-
-			temp.mSubAnims.push_back(clip);
-
-			for (std::vector<std::string>::iterator a = it->second.begin(); a != it->second.end(); ++a)
-			{
-				std::string animationID = *a;
-
-				AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
-
-				if (animation != mAnimationLibrary.end())
-				{
-					Animation *pSourceAnimation = animation->second;
-
-					pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
-				}
-			}
-		}
-
-		mAnims = temp;
-
-		// Ensure no double deletes.
-		temp.mSubAnims.clear();
-	}
-	else
-	{
-		mAnims.CombineSingleChannelAnimations();
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the animation library
-void ColladaParser::ReadAnimationLibrary()
-{
-    if (mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "animation"))
-            {
-                // delegate the reading. Depending on the inner elements it will be a container or a anim channel
-                ReadAnimation( &mAnims);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_animations") != 0)
-                ThrowException( "Expected end of <library_animations> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an animation into the given parent structure
-void ColladaParser::ReadAnimation( Collada::Animation* pParent)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    // an <animation> element may be a container for grouping sub-elements or an animation channel
-    // this is the channel collection by ID, in case it has channels
-    typedef std::map<std::string, AnimationChannel> ChannelMap;
-    ChannelMap channels;
-    // this is the anim container in case we're a container
-    Animation* anim = NULL;
-
-    // optional name given as an attribute
-    std::string animName;
-	std::string animID;
-    int indexName = TestAttribute( "name");
-    int indexID = TestAttribute( "id");
-
-	if (indexID >= 0)
-		animID = mReader->getAttributeValue(indexID);
-
-    if( indexName >= 0)
-        animName = mReader->getAttributeValue( indexName);
-    else if( indexID >= 0)
-        animName = animID;
-    else
-        animName = "animation";
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // we have subanimations
-            if( IsElement( "animation"))
-            {
-                // create container from our element
-                if( !anim)
-                {
-                    anim = new Animation;
-                    anim->mName = animName;
-                    pParent->mSubAnims.push_back( anim);
-                }
-
-                // recurse into the subelement
-                ReadAnimation( anim);
-            }
-            else if( IsElement( "source"))
-            {
-                // possible animation data - we'll never know. Better store it
-                ReadSource();
-            }
-            else if( IsElement( "sampler"))
-            {
-                // read the ID to assign the corresponding collada channel afterwards.
-                int indexID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( indexID);
-                ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first;
-
-                // have it read into a channel
-                ReadAnimationSampler( newChannel->second);
-            }
-            else if( IsElement( "channel"))
-            {
-                // the binding element whose whole purpose is to provide the target to animate
-                // Thanks, Collada! A directly posted information would have been too simple, I guess.
-                // Better add another indirection to that! Can't have enough of those.
-                int indexTarget = GetAttribute( "target");
-                int indexSource = GetAttribute( "source");
-                const char* sourceId = mReader->getAttributeValue( indexSource);
-                if( sourceId[0] == '#')
-                    sourceId++;
-                ChannelMap::iterator cit = channels.find( sourceId);
-                if( cit != channels.end())
-                    cit->second.mTarget = mReader->getAttributeValue( indexTarget);
-
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "animation") != 0)
-                ThrowException( "Expected end of <animation> element.");
-
-            break;
-        }
-    }
-
-    // it turned out to have channels - add them
-    if( !channels.empty())
-    {
-		// FIXME: Is this essentially doing the same as "single-anim-node" codepath in
-		//        ColladaLoader::StoreAnimations? For now, this has been deferred to after
-		//        all animations and all clips have been read. Due to handling of
-		//        <library_animation_clips> this cannot be done here, as the channel owner
-		//        is lost, and some exporters make up animations by referring to multiple
-		//        single-channel animations from an <instance_animation>.
-/*
-        // special filtering for stupid exporters packing each channel into a separate animation
-        if( channels.size() == 1)
-        {
-            pParent->mChannels.push_back( channels.begin()->second);
-        } else
-*/
-        {
-            // else create the animation, if not done yet, and store the channels
-            if( !anim)
-            {
-                anim = new Animation;
-                anim->mName = animName;
-                pParent->mSubAnims.push_back( anim);
-            }
-            for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it)
-                anim->mChannels.push_back( it->second);
-
-			if (indexID >= 0)
-			{
-				mAnimationLibrary[animID] = anim;
-			}
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an animation sampler into the given anim channel
-void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "input"))
-            {
-                int indexSemantic = GetAttribute( "semantic");
-                const char* semantic = mReader->getAttributeValue( indexSemantic);
-                int indexSource = GetAttribute( "source");
-                const char* source = mReader->getAttributeValue( indexSource);
-                if( source[0] != '#')
-                    ThrowException( "Unsupported URL format");
-                source++;
-
-                if( strcmp( semantic, "INPUT") == 0)
-                    pChannel.mSourceTimes = source;
-                else if( strcmp( semantic, "OUTPUT") == 0)
-                    pChannel.mSourceValues = source;
-                else if( strcmp( semantic, "IN_TANGENT") == 0)
-                    pChannel.mInTanValues = source;
-                else if( strcmp( semantic, "OUT_TANGENT") == 0)
-                    pChannel.mOutTanValues = source;
-                else if( strcmp( semantic, "INTERPOLATION") == 0)
-                    pChannel.mInterpolationValues = source;
-
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "sampler") != 0)
-                ThrowException( "Expected end of <sampler> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the skeleton controller library
-void ColladaParser::ReadControllerLibrary()
-{
-    if (mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "controller"))
-            {
-                // read ID. Ask the spec if it's necessary or optional... you might be surprised.
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mControllerLibrary[id] = Controller();
-
-                // read on from there
-                ReadController( mControllerLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_controllers") != 0)
-                ThrowException( "Expected end of <library_controllers> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a controller into the given mesh structure
-void ColladaParser::ReadController( Collada::Controller& pController)
-{
-    // initial values
-    pController.mType = Skin;
-    pController.mMethod = Normalized;
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
-            if( IsElement( "morph"))
-            {
-                pController.mType = Morph;
-                int baseIndex = GetAttribute("source");
-                pController.mMeshId = mReader->getAttributeValue(baseIndex) + 1;
-                int methodIndex = GetAttribute("method");
-                if (methodIndex > 0) {
-                    const char *method = mReader->getAttributeValue(methodIndex);
-                    if (strcmp(method, "RELATIVE") == 0)
-                        pController.mMethod = Relative;
-                }
-            }
-            else if( IsElement( "skin"))
-            {
-                // read the mesh it refers to. According to the spec this could also be another
-                // controller, but I refuse to implement every single idea they've come up with
-                int sourceIndex = GetAttribute( "source");
-                pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1;
-            }
-            else if( IsElement( "bind_shape_matrix"))
-            {
-                // content is 16 floats to define a matrix... it seems to be important for some models
-                const char* content = GetTextContent();
-
-                // read the 16 floats
-                for( unsigned int a = 0; a < 16; a++)
-                {
-                    // read a number
-                    content = fast_atoreal_move<ai_real>( content, pController.mBindShapeMatrix[a]);
-                    // skip whitespace after it
-                    SkipSpacesAndLineEnd( &content);
-                }
-
-                TestClosing( "bind_shape_matrix");
-            }
-            else if( IsElement( "source"))
-            {
-                // data array - we have specialists to handle this
-                ReadSource();
-            }
-            else if( IsElement( "joints"))
-            {
-                ReadControllerJoints( pController);
-            }
-            else if( IsElement( "vertex_weights"))
-            {
-                ReadControllerWeights( pController);
-            }
-            else if ( IsElement( "targets" ))
-            {
-                while (mReader->read()) {
-                    if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-                        if ( IsElement( "input")) {
-                            int semanticsIndex = GetAttribute("semantic");
-                            int sourceIndex = GetAttribute("source");
-
-                            const char *semantics = mReader->getAttributeValue(semanticsIndex);
-                            const char *source = mReader->getAttributeValue(sourceIndex);
-                            if (strcmp(semantics, "MORPH_TARGET") == 0) {
-                                pController.mMorphTarget = source + 1;
-                            }
-                            else if (strcmp(semantics, "MORPH_WEIGHT") == 0)
-                            {
-                                pController.mMorphWeight = source + 1;
-                            }
-                        }
-                    } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-                        if( strcmp( mReader->getNodeName(), "targets") == 0)
-                            break;
-                        else
-                            ThrowException( "Expected end of <targets> element.");
-                    }
-                }
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "controller") == 0)
-                break;
-            else if( strcmp( mReader->getNodeName(), "skin") != 0 && strcmp( mReader->getNodeName(), "morph") != 0)
-                ThrowException( "Expected end of <controller> element.");
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the joint definitions for the given controller
-void ColladaParser::ReadControllerJoints( Collada::Controller& pController)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
-            if( IsElement( "input"))
-            {
-                int indexSemantic = GetAttribute( "semantic");
-                const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
-                int indexSource = GetAttribute( "source");
-                const char* attrSource = mReader->getAttributeValue( indexSource);
-
-                // local URLS always start with a '#'. We don't support global URLs
-                if( attrSource[0] != '#')
-                    ThrowException( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <joints> data <input> element" );
-                attrSource++;
-
-                // parse source URL to corresponding source
-                if( strcmp( attrSemantic, "JOINT") == 0)
-                    pController.mJointNameSource = attrSource;
-                else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0)
-                    pController.mJointOffsetMatrixSource = attrSource;
-                else
-                    ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in <joints> data <input> element" );
-
-                // skip inner data, if present
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "joints") != 0)
-                ThrowException( "Expected end of <joints> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the joint weights for the given controller
-void ColladaParser::ReadControllerWeights( Collada::Controller& pController)
-{
-    // read vertex count from attributes and resize the array accordingly
-    int indexCount = GetAttribute( "count");
-    size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount);
-    pController.mWeightCounts.resize( vertexCount);
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
-            if( IsElement( "input") && vertexCount > 0 )
-            {
-                InputChannel channel;
-
-                int indexSemantic = GetAttribute( "semantic");
-                const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
-                int indexSource = GetAttribute( "source");
-                const char* attrSource = mReader->getAttributeValue( indexSource);
-                int indexOffset = TestAttribute( "offset");
-                if( indexOffset >= 0)
-                    channel.mOffset = mReader->getAttributeValueAsInt( indexOffset);
-
-                // local URLS always start with a '#'. We don't support global URLs
-                if( attrSource[0] != '#')
-                    ThrowException( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <vertex_weights> data <input> element" );
-                channel.mAccessor = attrSource + 1;
-
-                // parse source URL to corresponding source
-                if( strcmp( attrSemantic, "JOINT") == 0)
-                    pController.mWeightInputJoints = channel;
-                else if( strcmp( attrSemantic, "WEIGHT") == 0)
-                    pController.mWeightInputWeights = channel;
-                else
-                    ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in <vertex_weights> data <input> element" );
-
-                // skip inner data, if present
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else if( IsElement( "vcount") && vertexCount > 0 )
-            {
-                // read weight count per vertex
-                const char* text = GetTextContent();
-                size_t numWeights = 0;
-                for( std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
-                {
-                    if( *text == 0)
-                        ThrowException( "Out of data while reading <vcount>");
-
-                    *it = strtoul10( text, &text);
-                    numWeights += *it;
-                    SkipSpacesAndLineEnd( &text);
-                }
-
-                TestClosing( "vcount");
-
-                // reserve weight count
-                pController.mWeights.resize( numWeights);
-            }
-            else if( IsElement( "v") && vertexCount > 0 )
-            {
-                // read JointIndex - WeightIndex pairs
-                const char* text = GetTextContent();
-
-                for( std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
-                {
-                    if( *text == 0)
-                        ThrowException( "Out of data while reading <vertex_weights>");
-                    it->first = strtoul10( text, &text);
-                    SkipSpacesAndLineEnd( &text);
-                    if( *text == 0)
-                        ThrowException( "Out of data while reading <vertex_weights>");
-                    it->second = strtoul10( text, &text);
-                    SkipSpacesAndLineEnd( &text);
-                }
-
-                TestClosing( "v");
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "vertex_weights") != 0)
-                ThrowException( "Expected end of <vertex_weights> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the image library contents
-void ColladaParser::ReadImageLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "image"))
-            {
-                // read ID. Another entry which is "optional" by design but obligatory in reality
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mImageLibrary[id] = Image();
-
-                // read on from there
-                ReadImage( mImageLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "library_images") != 0)
-                ThrowException( "Expected end of <library_images> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an image entry into the given image
-void ColladaParser::ReadImage( Collada::Image& pImage)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
-            // Need to run different code paths here, depending on the Collada XSD version
-            if (IsElement("image")) {
-                SkipElement();
-            }
-            else if(  IsElement( "init_from"))
-            {
-                if (mFormat == FV_1_4_n)
-                {
-                    // FIX: C4D exporter writes empty <init_from/> tags
-                    if (!mReader->isEmptyElement()) {
-                        // element content is filename - hopefully
-                        const char* sz = TestTextContent();
-                        if (sz)pImage.mFileName = sz;
-                        TestClosing( "init_from");
-                    }
-                    if (!pImage.mFileName.length()) {
-                        pImage.mFileName = "unknown_texture";
-                    }
-                }
-                else if (mFormat == FV_1_5_n)
-                {
-                    // make sure we skip over mip and array initializations, which
-                    // we don't support, but which could confuse the loader if
-                    // they're not skipped.
-                    int attrib = TestAttribute("array_index");
-                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
-                        DefaultLogger::get()->warn("Collada: Ignoring texture array index");
-                        continue;
-                    }
-
-                    attrib = TestAttribute("mip_index");
-                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
-                        DefaultLogger::get()->warn("Collada: Ignoring MIP map layer");
-                        continue;
-                    }
-
-                    // TODO: correctly jump over cube and volume maps?
-                }
-            }
-            else if (mFormat == FV_1_5_n)
-            {
-                if( IsElement( "ref"))
-                {
-                    // element content is filename - hopefully
-                    const char* sz = TestTextContent();
-                    if (sz)pImage.mFileName = sz;
-                    TestClosing( "ref");
-                }
-                else if( IsElement( "hex") && !pImage.mFileName.length())
-                {
-                    // embedded image. get format
-                    const int attrib = TestAttribute("format");
-                    if (-1 == attrib)
-                        DefaultLogger::get()->warn("Collada: Unknown image file format");
-                    else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib);
-
-                    const char* data = GetTextContent();
-
-                    // hexadecimal-encoded binary octets. First of all, find the
-                    // required buffer size to reserve enough storage.
-                    const char* cur = data;
-                    while (!IsSpaceOrNewLine(*cur)) cur++;
-
-                    const unsigned int size = (unsigned int)(cur-data) * 2;
-                    pImage.mImageData.resize(size);
-                    for (unsigned int i = 0; i < size;++i)
-                        pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1));
-
-                    TestClosing( "hex");
-                }
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "image") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the material library
-void ColladaParser::ReadMaterialLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    std::map<std::string, int> names;
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "material"))
-            {
-                // read ID. By now you probably know my opinion about this "specification"
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                std::string name;
-                int attrName = TestAttribute("name");
-                if (attrName >= 0)
-                    name = mReader->getAttributeValue( attrName);
-
-                // create an entry and store it in the library under its ID
-                mMaterialLibrary[id] = Material();
-
-                if( !name.empty())
-                {
-                    std::map<std::string, int>::iterator it = names.find( name);
-                    if( it != names.end())
-                    {
-                        std::ostringstream strStream;
-                        strStream << ++it->second;
-                        name.append( " " + strStream.str());
-                    }
-                    else
-                    {
-                        names[name] = 0;
-                    }
-
-                    mMaterialLibrary[id].mName = name;
-                }
-
-                ReadMaterial( mMaterialLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_materials") != 0)
-                ThrowException( "Expected end of <library_materials> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the light library
-void ColladaParser::ReadLightLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "light"))
-            {
-                // read ID. By now you probably know my opinion about this "specification"
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                ReadLight(mLightLibrary[id] = Light());
-
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
-            if( strcmp( mReader->getNodeName(), "library_lights") != 0)
-                ThrowException( "Expected end of <library_lights> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the camera library
-void ColladaParser::ReadCameraLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "camera"))
-            {
-                // read ID. By now you probably know my opinion about this "specification"
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                Camera& cam = mCameraLibrary[id];
-                attrID = TestAttribute( "name");
-                if (attrID != -1)
-                    cam.mName = mReader->getAttributeValue( attrID);
-
-                ReadCamera(cam);
-
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
-            if( strcmp( mReader->getNodeName(), "library_cameras") != 0)
-                ThrowException( "Expected end of <library_cameras> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a material entry into the given material
-void ColladaParser::ReadMaterial( Collada::Material& pMaterial)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if (IsElement("material")) {
-                SkipElement();
-            }
-            else if( IsElement( "instance_effect"))
-            {
-                // referred effect by URL
-                int attrUrl = GetAttribute( "url");
-                const char* url = mReader->getAttributeValue( attrUrl);
-                if( url[0] != '#')
-                    ThrowException( "Unknown reference format");
-
-                pMaterial.mEffect = url+1;
-
-                SkipElement();
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "material") != 0)
-                ThrowException( "Expected end of <material> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a light entry into the given light
-void ColladaParser::ReadLight( Collada::Light& pLight)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if (IsElement("light")) {
-                SkipElement();
-            }
-            else if (IsElement("spot")) {
-                pLight.mType = aiLightSource_SPOT;
-            }
-            else if (IsElement("ambient")) {
-                pLight.mType = aiLightSource_AMBIENT;
-            }
-            else if (IsElement("directional")) {
-                pLight.mType = aiLightSource_DIRECTIONAL;
-            }
-            else if (IsElement("point")) {
-                pLight.mType = aiLightSource_POINT;
-            }
-            else if (IsElement("color")) {
-                // text content contains 3 floats
-                const char* content = GetTextContent();
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.r);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.g);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.b);
-                SkipSpacesAndLineEnd( &content);
-
-                TestClosing( "color");
-            }
-            else if (IsElement("constant_attenuation")) {
-                pLight.mAttConstant = ReadFloatFromTextContent();
-                TestClosing("constant_attenuation");
-            }
-            else if (IsElement("linear_attenuation")) {
-                pLight.mAttLinear = ReadFloatFromTextContent();
-                TestClosing("linear_attenuation");
-            }
-            else if (IsElement("quadratic_attenuation")) {
-                pLight.mAttQuadratic = ReadFloatFromTextContent();
-                TestClosing("quadratic_attenuation");
-            }
-            else if (IsElement("falloff_angle")) {
-                pLight.mFalloffAngle = ReadFloatFromTextContent();
-                TestClosing("falloff_angle");
-            }
-            else if (IsElement("falloff_exponent")) {
-                pLight.mFalloffExponent = ReadFloatFromTextContent();
-                TestClosing("falloff_exponent");
-            }
-            // FCOLLADA extensions
-            // -------------------------------------------------------
-            else if (IsElement("outer_cone")) {
-                pLight.mOuterAngle = ReadFloatFromTextContent();
-                TestClosing("outer_cone");
-            }
-            // ... and this one is even deprecated
-            else if (IsElement("penumbra_angle")) {
-                pLight.mPenumbraAngle = ReadFloatFromTextContent();
-                TestClosing("penumbra_angle");
-            }
-            else if (IsElement("intensity")) {
-                pLight.mIntensity = ReadFloatFromTextContent();
-                TestClosing("intensity");
-            }
-            else if (IsElement("falloff")) {
-                pLight.mOuterAngle = ReadFloatFromTextContent();
-                TestClosing("falloff");
-            }
-            else if (IsElement("hotspot_beam")) {
-                pLight.mFalloffAngle = ReadFloatFromTextContent();
-                TestClosing("hotspot_beam");
-            }
-            // OpenCOLLADA extensions
-            // -------------------------------------------------------
-            else if (IsElement("decay_falloff")) {
-                pLight.mOuterAngle = ReadFloatFromTextContent();
-                TestClosing("decay_falloff");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "light") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a camera entry into the given light
-void ColladaParser::ReadCamera( Collada::Camera& pCamera)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if (IsElement("camera")) {
-                SkipElement();
-            }
-            else if (IsElement("orthographic")) {
-                pCamera.mOrtho = true;
-            }
-            else if (IsElement("xfov") || IsElement("xmag")) {
-                pCamera.mHorFov = ReadFloatFromTextContent();
-                TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
-            }
-            else if (IsElement("yfov") || IsElement("ymag")) {
-                pCamera.mVerFov = ReadFloatFromTextContent();
-                TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
-            }
-            else if (IsElement("aspect_ratio")) {
-                pCamera.mAspect = ReadFloatFromTextContent();
-                TestClosing("aspect_ratio");
-            }
-            else if (IsElement("znear")) {
-                pCamera.mZNear = ReadFloatFromTextContent();
-                TestClosing("znear");
-            }
-            else if (IsElement("zfar")) {
-                pCamera.mZFar = ReadFloatFromTextContent();
-                TestClosing("zfar");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "camera") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the effect library
-void ColladaParser::ReadEffectLibrary()
-{
-    if (mReader->isEmptyElement()) {
-        return;
-    }
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "effect"))
-            {
-                // read ID. Do I have to repeat my ranting about "optional" attributes?
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mEffectLibrary[id] = Effect();
-                // read on from there
-                ReadEffect( mEffectLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "library_effects") != 0)
-                ThrowException( "Expected end of <library_effects> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect entry into the given effect
-void ColladaParser::ReadEffect( Collada::Effect& pEffect)
-{
-    // for the moment we don't support any other type of effect.
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "profile_COMMON"))
-                ReadEffectProfileCommon( pEffect);
-            else
-                SkipElement();
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "effect") != 0)
-                ThrowException( "Expected end of <effect> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an COMMON effect profile
-void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "newparam")) {
-                // save ID
-                int attrSID = GetAttribute( "sid");
-                std::string sid = mReader->getAttributeValue( attrSID);
-                pEffect.mParams[sid] = EffectParam();
-                ReadEffectParam( pEffect.mParams[sid]);
-            }
-            else if( IsElement( "technique") || IsElement( "extra"))
-            {
-                // just syntactic sugar
-            }
-
-            else if( mFormat == FV_1_4_n && IsElement( "image"))
-            {
-                // read ID. Another entry which is "optional" by design but obligatory in reality
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mImageLibrary[id] = Image();
-
-                // read on from there
-                ReadImage( mImageLibrary[id]);
-            }
-
-            /* Shading modes */
-            else if( IsElement( "phong"))
-                pEffect.mShadeType = Shade_Phong;
-            else if( IsElement( "constant"))
-                pEffect.mShadeType = Shade_Constant;
-            else if( IsElement( "lambert"))
-                pEffect.mShadeType = Shade_Lambert;
-            else if( IsElement( "blinn"))
-                pEffect.mShadeType = Shade_Blinn;
-
-            /* Color + texture properties */
-            else if( IsElement( "emission"))
-                ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive);
-            else if( IsElement( "ambient"))
-                ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient);
-            else if( IsElement( "diffuse"))
-                ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse);
-            else if( IsElement( "specular"))
-                ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular);
-            else if( IsElement( "reflective")) {
-                ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective);
-            }
-            else if( IsElement( "transparent")) {
-                pEffect.mHasTransparency = true;
-
-                const char* opaque = mReader->getAttributeValueSafe("opaque");
-
-                if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) {
-                    pEffect.mRGBTransparency = true;
-                }
-
-                // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure...
-				if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) {
-					pEffect.mInvertTransparency = true;
-				}
-
-                ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent);
-            }
-            else if( IsElement( "shininess"))
-                ReadEffectFloat( pEffect.mShininess);
-            else if( IsElement( "reflectivity"))
-                ReadEffectFloat( pEffect.mReflectivity);
-
-            /* Single scalar properties */
-            else if( IsElement( "transparency"))
-                ReadEffectFloat( pEffect.mTransparency);
-            else if( IsElement( "index_of_refraction"))
-                ReadEffectFloat( pEffect.mRefractIndex);
-
-            // GOOGLEEARTH/OKINO extensions
-            // -------------------------------------------------------
-            else if( IsElement( "double_sided"))
-                pEffect.mDoubleSided = ReadBoolFromTextContent();
-
-            // FCOLLADA extensions
-            // -------------------------------------------------------
-            else if( IsElement( "bump")) {
-                aiColor4D dummy;
-                ReadEffectColor( dummy,pEffect.mTexBump);
-            }
-
-            // MAX3D extensions
-            // -------------------------------------------------------
-            else if( IsElement( "wireframe"))   {
-                pEffect.mWireframe = ReadBoolFromTextContent();
-                TestClosing( "wireframe");
-            }
-            else if( IsElement( "faceted")) {
-                pEffect.mFaceted = ReadBoolFromTextContent();
-                TestClosing( "faceted");
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0)
-            {
-                break;
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Read texture wrapping + UV transform settings from a profile==Maya chunk
-void ColladaParser::ReadSamplerProperties( Sampler& out )
-{
-    if (mReader->isEmptyElement()) {
-        return;
-    }
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-
-            // MAYA extensions
-            // -------------------------------------------------------
-            if( IsElement( "wrapU"))        {
-                out.mWrapU = ReadBoolFromTextContent();
-                TestClosing( "wrapU");
-            }
-            else if( IsElement( "wrapV"))   {
-                out.mWrapV = ReadBoolFromTextContent();
-                TestClosing( "wrapV");
-            }
-            else if( IsElement( "mirrorU"))     {
-                out.mMirrorU = ReadBoolFromTextContent();
-                TestClosing( "mirrorU");
-            }
-            else if( IsElement( "mirrorV")) {
-                out.mMirrorV = ReadBoolFromTextContent();
-                TestClosing( "mirrorV");
-            }
-            else if( IsElement( "repeatU")) {
-                out.mTransform.mScaling.x = ReadFloatFromTextContent();
-                TestClosing( "repeatU");
-            }
-            else if( IsElement( "repeatV")) {
-                out.mTransform.mScaling.y = ReadFloatFromTextContent();
-                TestClosing( "repeatV");
-            }
-            else if( IsElement( "offsetU")) {
-                out.mTransform.mTranslation.x = ReadFloatFromTextContent();
-                TestClosing( "offsetU");
-            }
-            else if( IsElement( "offsetV")) {
-                out.mTransform.mTranslation.y = ReadFloatFromTextContent();
-                TestClosing( "offsetV");
-            }
-            else if( IsElement( "rotateUV"))    {
-                out.mTransform.mRotation = ReadFloatFromTextContent();
-                TestClosing( "rotateUV");
-            }
-            else if( IsElement( "blend_mode"))  {
-
-                const char* sz = GetTextContent();
-                // http://www.feelingsoftware.com/content/view/55/72/lang,en/
-                // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
-                if (0 == ASSIMP_strincmp(sz,"ADD",3))
-                    out.mOp = aiTextureOp_Add;
-
-                else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8))
-                    out.mOp = aiTextureOp_Subtract;
-
-                else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8))
-                    out.mOp = aiTextureOp_Multiply;
-
-                else  {
-                    DefaultLogger::get()->warn("Collada: Unsupported MAYA texture blend mode");
-                }
-                TestClosing( "blend_mode");
-            }
-            // OKINO extensions
-            // -------------------------------------------------------
-            else if( IsElement( "weighting"))   {
-                out.mWeighting = ReadFloatFromTextContent();
-                TestClosing( "weighting");
-            }
-            else if( IsElement( "mix_with_previous_layer")) {
-                out.mMixWithPrevious = ReadFloatFromTextContent();
-                TestClosing( "mix_with_previous_layer");
-            }
-            // MAX3D extensions
-            // -------------------------------------------------------
-            else if( IsElement( "amount"))  {
-                out.mWeighting = ReadFloatFromTextContent();
-                TestClosing( "amount");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "technique") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect entry containing a color or a texture defining that color
-void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler)
-{
-    if (mReader->isEmptyElement())
-        return;
-
-    // Save current element name
-    const std::string curElem = mReader->getNodeName();
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "color"))
-            {
-                // text content contains 4 floats
-                const char* content = GetTextContent();
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.r);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.g);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.b);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.a);
-                SkipSpacesAndLineEnd( &content);
-                TestClosing( "color");
-            }
-            else if( IsElement( "texture"))
-            {
-                // get name of source texture/sampler
-                int attrTex = GetAttribute( "texture");
-                pSampler.mName = mReader->getAttributeValue( attrTex);
-
-                // get name of UV source channel. Specification demands it to be there, but some exporters
-                // don't write it. It will be the default UV channel in case it's missing.
-                attrTex = TestAttribute( "texcoord");
-                if( attrTex >= 0 )
-                    pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
-                //SkipElement();
-
-                // as we've read texture, the color needs to be 1,1,1,1
-                pColor = aiColor4D(1.f, 1.f, 1.f, 1.f);
-            }
-            else if( IsElement( "technique"))
-            {
-                const int _profile = GetAttribute( "profile");
-                const char* profile = mReader->getAttributeValue( _profile );
-
-                // Some extensions are quite useful ... ReadSamplerProperties processes
-                // several extensions in MAYA, OKINO and MAX3D profiles.
-                if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO"))
-                {
-                    // get more information on this sampler
-                    ReadSamplerProperties(pSampler);
-                }
-                else SkipElement();
-            }
-            else if( !IsElement( "extra"))
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
-            if (mReader->getNodeName() == curElem)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect entry containing a float
-void ColladaParser::ReadEffectFloat( ai_real& pFloat)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
-            if( IsElement( "float"))
-            {
-                // text content contains a single floats
-                const char* content = GetTextContent();
-                content = fast_atoreal_move<ai_real>( content, pFloat);
-                SkipSpacesAndLineEnd( &content);
-
-                TestClosing( "float");
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect parameter specification of any kind
-void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "surface"))
-            {
-                // image ID given inside <init_from> tags
-                TestOpening( "init_from");
-                const char* content = GetTextContent();
-                pParam.mType = Param_Surface;
-                pParam.mReference = content;
-                TestClosing( "init_from");
-
-                // don't care for remaining stuff
-                SkipElement( "surface");
-            }
-            else if( IsElement( "sampler2D") && (FV_1_4_n == mFormat || FV_1_3_n == mFormat))
-            {
-                // surface ID is given inside <source> tags
-                TestOpening( "source");
-                const char* content = GetTextContent();
-                pParam.mType = Param_Sampler;
-                pParam.mReference = content;
-                TestClosing( "source");
-
-                // don't care for remaining stuff
-                SkipElement( "sampler2D");
-            }
-            else if( IsElement( "sampler2D"))
-            {
-                // surface ID is given inside <instance_image> tags
-                TestOpening( "instance_image");
-                int attrURL = GetAttribute("url");
-                const char* url = mReader->getAttributeValue( attrURL);
-                if( url[0] != '#')
-                    ThrowException( "Unsupported URL format in instance_image");
-                url++;
-                pParam.mType = Param_Sampler;
-                pParam.mReference = url;
-                SkipElement( "sampler2D");
-            } else
-            {
-                // ignore unknown element
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the geometry library contents
-void ColladaParser::ReadGeometryLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "geometry"))
-            {
-                // read ID. Another entry which is "optional" by design but obligatory in reality
-                int indexID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( indexID);
-
-                // TODO: (thom) support SIDs
-                // ai_assert( TestAttribute( "sid") == -1);
-
-                // create a mesh and store it in the library under its ID
-                Mesh* mesh = new Mesh;
-                mMeshLibrary[id] = mesh;
-
-                // read the mesh name if it exists
-                const int nameIndex = TestAttribute("name");
-                if(nameIndex != -1)
-                {
-                    mesh->mName = mReader->getAttributeValue(nameIndex);
-                }
-
-                // read on from there
-                ReadGeometry( mesh);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_geometries") != 0)
-                ThrowException( "Expected end of <library_geometries> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a geometry from the geometry library.
-void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "mesh"))
-            {
-                // read on from there
-                ReadMesh( pMesh);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "geometry") != 0)
-                ThrowException( "Expected end of <geometry> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a mesh from the geometry library
-void ColladaParser::ReadMesh( Mesh* pMesh)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "source"))
-            {
-                // we have professionals dealing with this
-                ReadSource();
-            }
-            else if( IsElement( "vertices"))
-            {
-                // read per-vertex mesh data
-                ReadVertexData( pMesh);
-            }
-            else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips")
-                || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips"))
-            {
-                // read per-index mesh data and faces setup
-                ReadIndexData( pMesh);
-            } else
-            {
-                // ignore the restf
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "technique_common") == 0)
-            {
-                // end of another meaningless element - read over it
-            }
-            else if( strcmp( mReader->getNodeName(), "mesh") == 0)
-            {
-                // end of <mesh> element - we're done here
-                break;
-            } else
-            {
-                // everything else should be punished
-                ThrowException( "Expected end of <mesh> element.");
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a source element
-void ColladaParser::ReadSource()
-{
-    int indexID = GetAttribute( "id");
-    std::string sourceID = mReader->getAttributeValue( indexID);
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array"))
-            {
-                ReadDataArray();
-            }
-            else if( IsElement( "technique_common"))
-            {
-                // I don't care for your profiles
-            }
-            else if( IsElement( "accessor"))
-            {
-                ReadAccessor( sourceID);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "source") == 0)
-            {
-                // end of <source> - we're done
-                break;
-            }
-            else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
-            {
-                // end of another meaningless element - read over it
-            } else
-            {
-                // everything else should be punished
-                ThrowException( "Expected end of <source> element.");
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a data array holding a number of floats, and stores it in the global library
-void ColladaParser::ReadDataArray()
-{
-    std::string elmName = mReader->getNodeName();
-    bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
-  bool isEmptyElement = mReader->isEmptyElement();
-
-    // read attributes
-    int indexID = GetAttribute( "id");
-    std::string id = mReader->getAttributeValue( indexID);
-    int indexCount = GetAttribute( "count");
-    unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
-    const char* content = TestTextContent();
-
-  // read values and store inside an array in the data library
-  mDataLibrary[id] = Data();
-  Data& data = mDataLibrary[id];
-  data.mIsStringArray = isStringArray;
-
-  // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
-  if (content)
-  {
-        if( isStringArray)
-        {
-            data.mStrings.reserve( count);
-            std::string s;
-
-            for( unsigned int a = 0; a < count; a++)
-            {
-                if( *content == 0)
-                    ThrowException( "Expected more values while reading IDREF_array contents.");
-
-                s.clear();
-                while( !IsSpaceOrNewLine( *content))
-                    s += *content++;
-                data.mStrings.push_back( s);
-
-                SkipSpacesAndLineEnd( &content);
-            }
-        } else
-        {
-            data.mValues.reserve( count);
-
-            for( unsigned int a = 0; a < count; a++)
-            {
-                if( *content == 0)
-                    ThrowException( "Expected more values while reading float_array contents.");
-
-                ai_real value;
-                // read a number
-                content = fast_atoreal_move<ai_real>( content, value);
-                data.mValues.push_back( value);
-                // skip whitespace after it
-                SkipSpacesAndLineEnd( &content);
-            }
-        }
-    }
-
-  // test for closing tag
-  if( !isEmptyElement )
-    TestClosing( elmName.c_str());
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an accessor and stores it in the global library
-void ColladaParser::ReadAccessor( const std::string& pID)
-{
-    // read accessor attributes
-    int attrSource = GetAttribute( "source");
-    const char* source = mReader->getAttributeValue( attrSource);
-    if( source[0] != '#')
-        ThrowException( format() << "Unknown reference format in url \"" << source << "\" in source attribute of <accessor> element." );
-    int attrCount = GetAttribute( "count");
-    unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount);
-    int attrOffset = TestAttribute( "offset");
-    unsigned int offset = 0;
-    if( attrOffset > -1)
-        offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset);
-    int attrStride = TestAttribute( "stride");
-    unsigned int stride = 1;
-    if( attrStride > -1)
-        stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride);
-
-    // store in the library under the given ID
-    mAccessorLibrary[pID] = Accessor();
-    Accessor& acc = mAccessorLibrary[pID];
-    acc.mCount = count;
-    acc.mOffset = offset;
-    acc.mStride = stride;
-    acc.mSource = source+1; // ignore the leading '#'
-    acc.mSize = 0; // gets incremented with every param
-
-    // and read the components
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "param"))
-            {
-                // read data param
-                int attrName = TestAttribute( "name");
-                std::string name;
-                if( attrName > -1)
-                {
-                    name = mReader->getAttributeValue( attrName);
-
-                    // analyse for common type components and store it's sub-offset in the corresponding field
-
-                    /* Cartesian coordinates */
-                    if( name == "X") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size();
-                    else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size();
-
-                    /* RGBA colors */
-                    else if( name == "R") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "G") acc.mSubOffset[1] = acc.mParams.size();
-                    else if( name == "B") acc.mSubOffset[2] = acc.mParams.size();
-                    else if( name == "A") acc.mSubOffset[3] = acc.mParams.size();
-
-                    /* UVWQ (STPQ) texture coordinates */
-                    else if( name == "S") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "T") acc.mSubOffset[1] = acc.mParams.size();
-                    else if( name == "P") acc.mSubOffset[2] = acc.mParams.size();
-                //  else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size();
-                    /* 4D uv coordinates are not supported in Assimp */
-
-                    /* Generic extra data, interpreted as UV data, too*/
-                    else if( name == "U") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "V") acc.mSubOffset[1] = acc.mParams.size();
-                    //else
-                    //  DefaultLogger::get()->warn( format() << "Unknown accessor parameter \"" << name << "\". Ignoring data channel." );
-                }
-
-                // read data type
-                int attrType = TestAttribute( "type");
-                if( attrType > -1)
-                {
-                    // for the moment we only distinguish between a 4x4 matrix and anything else.
-                    // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
-                    // which should be tested for here.
-                    std::string type = mReader->getAttributeValue( attrType);
-                    if( type == "float4x4")
-                        acc.mSize += 16;
-                    else
-                        acc.mSize += 1;
-                }
-
-                acc.mParams.push_back( name);
-
-                // skip remaining stuff of this element, if any
-                SkipElement();
-            } else
-            {
-                ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <accessor>" );
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "accessor") != 0)
-                ThrowException( "Expected end of <accessor> element.");
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads input declarations of per-vertex mesh data into the given mesh
-void ColladaParser::ReadVertexData( Mesh* pMesh)
-{
-    // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
-    int attrID= GetAttribute( "id");
-    pMesh->mVertexID = mReader->getAttributeValue( attrID);
-
-    // a number of <input> elements
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "input"))
-            {
-                ReadInputChannel( pMesh->mPerVertexData);
-            } else
-            {
-                ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <vertices>" );
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "vertices") != 0)
-                ThrowException( "Expected end of <vertices> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads input declarations of per-index mesh data into the given mesh
-void ColladaParser::ReadIndexData( Mesh* pMesh)
-{
-    std::vector<size_t> vcount;
-    std::vector<InputChannel> perIndexData;
-
-    // read primitive count from the attribute
-    int attrCount = GetAttribute( "count");
-    size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
-    // some mesh types (e.g. tristrips) don't specify primitive count upfront,
-    // so we need to sum up the actual number of primitives while we read the <p>-tags
-    size_t actualPrimitives = 0;
-
-    // material subgroup
-    int attrMaterial = TestAttribute( "material");
-    SubMesh subgroup;
-    if( attrMaterial > -1)
-        subgroup.mMaterial = mReader->getAttributeValue( attrMaterial);
-
-    // distinguish between polys and triangles
-    std::string elementName = mReader->getNodeName();
-    PrimitiveType primType = Prim_Invalid;
-    if( IsElement( "lines"))
-        primType = Prim_Lines;
-    else if( IsElement( "linestrips"))
-        primType = Prim_LineStrip;
-    else if( IsElement( "polygons"))
-        primType = Prim_Polygon;
-    else if( IsElement( "polylist"))
-        primType = Prim_Polylist;
-    else if( IsElement( "triangles"))
-        primType = Prim_Triangles;
-    else if( IsElement( "trifans"))
-        primType = Prim_TriFans;
-    else if( IsElement( "tristrips"))
-        primType = Prim_TriStrips;
-
-    ai_assert( primType != Prim_Invalid);
-
-    // also a number of <input> elements, but in addition a <p> primitive collection and probably index counts for all primitives
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "input"))
-            {
-                ReadInputChannel( perIndexData);
-            }
-            else if( IsElement( "vcount"))
-            {
-                if( !mReader->isEmptyElement())
-                {
-                    if (numPrimitives)  // It is possible to define a mesh without any primitives
-                    {
-                        // case <polylist> - specifies the number of indices for each polygon
-                        const char* content = GetTextContent();
-                        vcount.reserve( numPrimitives);
-                        for( unsigned int a = 0; a < numPrimitives; a++)
-                        {
-                            if( *content == 0)
-                                ThrowException( "Expected more values while reading <vcount> contents.");
-                            // read a number
-                            vcount.push_back( (size_t) strtoul10( content, &content));
-                            // skip whitespace after it
-                            SkipSpacesAndLineEnd( &content);
-                        }
-                    }
-
-                    TestClosing( "vcount");
-                }
-            }
-            else if( IsElement( "p"))
-            {
-                if( !mReader->isEmptyElement())
-                {
-                    // now here the actual fun starts - these are the indices to construct the mesh data from
-                    actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
-                }
-            }
-            else if (IsElement("extra"))
-            {
-                SkipElement("extra");
-            } else if ( IsElement("ph")) {                
-                SkipElement("ph");
-            } else {
-                ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">" );
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( mReader->getNodeName() != elementName)
-                ThrowException( format() << "Expected end of <" << elementName << "> element." );
-
-            break;
-        }
-    }
-
-#ifdef ASSIMP_BUILD_DEBUG
-	if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip &&
-        primType != Prim_Lines) { // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'.
-        ai_assert(actualPrimitives == numPrimitives);
-    }
-#endif
-
-    // only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
-    subgroup.mNumFaces = actualPrimitives;
-    pMesh->mSubMeshes.push_back(subgroup);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a single input channel element and stores it in the given array, if valid
-void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
-{
-    InputChannel channel;
-
-    // read semantic
-    int attrSemantic = GetAttribute( "semantic");
-    std::string semantic = mReader->getAttributeValue( attrSemantic);
-    channel.mType = GetTypeForSemantic( semantic);
-
-    // read source
-    int attrSource = GetAttribute( "source");
-    const char* source = mReader->getAttributeValue( attrSource);
-    if( source[0] != '#')
-        ThrowException( format() << "Unknown reference format in url \"" << source << "\" in source attribute of <input> element." );
-    channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only
-
-    // read index offset, if per-index <input>
-    int attrOffset = TestAttribute( "offset");
-    if( attrOffset > -1)
-        channel.mOffset = mReader->getAttributeValueAsInt( attrOffset);
-
-    // read set if texture coordinates
-    if(channel.mType == IT_Texcoord || channel.mType == IT_Color){
-        int attrSet = TestAttribute("set");
-        if(attrSet > -1){
-            attrSet = mReader->getAttributeValueAsInt( attrSet);
-            if(attrSet < 0)
-                ThrowException( format() << "Invalid index \"" << (attrSet) << "\" in set attribute of <input> element" );
-
-            channel.mIndex = attrSet;
-        }
-    }
-
-    // store, if valid type
-    if( channel.mType != IT_Invalid)
-        poChannels.push_back( channel);
-
-    // skip remaining stuff of this element, if any
-    SkipElement();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a <p> primitive index list and assembles the mesh data into the given mesh
-size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
-    size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
-{
-    // determine number of indices coming per vertex
-    // find the offset index for all per-vertex channels
-    size_t numOffsets = 1;
-    size_t perVertexOffset = SIZE_MAX; // invalid value
-    for( const InputChannel& channel : pPerIndexChannels)
-    {
-        numOffsets = std::max( numOffsets, channel.mOffset+1);
-        if( channel.mType == IT_Vertex)
-            perVertexOffset = channel.mOffset;
-    }
-
-    // determine the expected number of indices
-    size_t expectedPointCount = 0;
-    switch( pPrimType)
-    {
-        case Prim_Polylist:
-        {
-            for( size_t i : pVCount)
-                expectedPointCount += i;
-            break;
-        }
-        case Prim_Lines:
-            expectedPointCount = 2 * pNumPrimitives;
-            break;
-        case Prim_Triangles:
-            expectedPointCount = 3 * pNumPrimitives;
-            break;
-        default:
-            // other primitive types don't state the index count upfront... we need to guess
-            break;
-    }
-
-    // and read all indices into a temporary array
-    std::vector<size_t> indices;
-    if( expectedPointCount > 0)
-        indices.reserve( expectedPointCount * numOffsets);
-
-    if (pNumPrimitives > 0) // It is possible to not contain any indices
-    {
-        const char* content = GetTextContent();
-        while( *content != 0)
-        {
-            // read a value.
-            // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
-            int value = std::max( 0, strtol10( content, &content));
-            indices.push_back( size_t( value));
-            // skip whitespace after it
-            SkipSpacesAndLineEnd( &content);
-        }
-    }
-
-	// complain if the index count doesn't fit
-    if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) {
-        if (pPrimType == Prim_Lines) {
-            // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
-            ReportWarning( "Expected different index count in <p> element, %d instead of %d.", indices.size(), expectedPointCount * numOffsets);
-            pNumPrimitives = (indices.size() / numOffsets) / 2;
-        } else
-            ThrowException( "Expected different index count in <p> element.");
-
-    } else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
-		ThrowException( "Expected different index count in <p> element.");
-
-	// find the data for all sources
-    for( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
-    {
-        InputChannel& input = *it;
-        if( input.mResolved)
-            continue;
-
-        // find accessor
-        input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
-        // resolve accessor's data pointer as well, if necessary
-        const Accessor* acc = input.mResolved;
-        if( !acc->mData)
-            acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
-    }
-    // and the same for the per-index channels
-    for( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
-    {
-        InputChannel& input = *it;
-        if( input.mResolved)
-            continue;
-
-        // ignore vertex pointer, it doesn't refer to an accessor
-        if( input.mType == IT_Vertex)
-        {
-            // warn if the vertex channel does not refer to the <vertices> element in the same mesh
-            if( input.mAccessor != pMesh->mVertexID)
-                ThrowException( "Unsupported vertex referencing scheme.");
-            continue;
-        }
-
-        // find accessor
-        input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
-        // resolve accessor's data pointer as well, if necessary
-        const Accessor* acc = input.mResolved;
-        if( !acc->mData)
-            acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
-    }
-
-    // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
-    size_t numPrimitives = pNumPrimitives;
-    if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
-        numPrimitives = 1;
-    // For continued primitives, the given count is actually the number of <p>'s inside the parent tag
-    if ( pPrimType == Prim_TriStrips){
-        size_t numberOfVertices = indices.size() / numOffsets;
-        numPrimitives = numberOfVertices - 2;
-    }
-    if (pPrimType == Prim_LineStrip) {
-        size_t numberOfVertices = indices.size() / numOffsets;
-        numPrimitives = numberOfVertices - 1;
-    }
-
-    pMesh->mFaceSize.reserve( numPrimitives);
-    pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
-
-    size_t polylistStartVertex = 0;
-    for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++)
-    {
-        // determine number of points for this primitive
-        size_t numPoints = 0;
-        switch( pPrimType)
-        {
-            case Prim_Lines:
-                numPoints = 2;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_LineStrip:
-                numPoints = 2;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_Triangles:
-                numPoints = 3;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_TriStrips:
-                numPoints = 3;
-                ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_Polylist:
-                numPoints = pVCount[currentPrimitive];
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
-                polylistStartVertex += numPoints;
-                break;
-            case Prim_TriFans:
-            case Prim_Polygon:
-                numPoints = indices.size() / numOffsets;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            default:
-                // LineStrip is not supported due to expected index unmangling
-                ThrowException( "Unsupported primitive type.");
-                break;
-        }
-
-        // store the face size to later reconstruct the face from
-        pMesh->mFaceSize.push_back( numPoints);
-    }
-
-    // if I ever get my hands on that guy who invented this steaming pile of indirection...
-    TestClosing( "p");
-    return numPrimitives;
-}
-
-///@note This function willn't work correctly if both PerIndex and PerVertex channels have same channels.
-///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
-///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior
-void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
-    // calculate the base offset of the vertex whose attributes we ant to copy
-    size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
-
-    // don't overrun the boundaries of the index list
-    ai_assert((baseOffset + numOffsets - 1) < indices.size());
-
-    // extract per-vertex channels using the global per-vertex offset
-    for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
-        ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
-    // and extract per-index channels using there specified offset
-    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
-        ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
-
-    // store the vertex-data index for later assignment of bone vertex weights
-    pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
-}
-
-void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
-    if (currentPrimitive % 2 != 0){
-        //odd tristrip triangles need their indices mangled, to preserve winding direction
-        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-    }
-    else {//for non tristrips or even tristrip triangles
-        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Extracts a single object from an input channel and stores it in the appropriate mesh data array
-void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
-{
-    // ignore vertex referrer - we handle them that separate
-    if( pInput.mType == IT_Vertex)
-        return;
-
-    const Accessor& acc = *pInput.mResolved;
-    if( pLocalIndex >= acc.mCount)
-        ThrowException( format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification" );
-
-    // get a pointer to the start of the data object referred to by the accessor and the local index
-    const ai_real* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride;
-
-    // assemble according to the accessors component sub-offset list. We don't care, yet,
-    // what kind of object exactly we're extracting here
-    ai_real obj[4];
-    for( size_t c = 0; c < 4; ++c)
-        obj[c] = dataObject[acc.mSubOffset[c]];
-
-    // now we reinterpret it according to the type we're reading here
-    switch( pInput.mType)
-    {
-        case IT_Position: // ignore all position streams except 0 - there can be only one position
-            if( pInput.mIndex == 0)
-                pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                DefaultLogger::get()->error("Collada: just one vertex position stream supported");
-            break;
-        case IT_Normal:
-            // pad to current vertex count if necessary
-            if( pMesh->mNormals.size() < pMesh->mPositions.size()-1)
-                pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0));
-
-            // ignore all normal streams except 0 - there can be only one normal
-            if( pInput.mIndex == 0)
-                pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                DefaultLogger::get()->error("Collada: just one vertex normal stream supported");
-            break;
-        case IT_Tangent:
-            // pad to current vertex count if necessary
-            if( pMesh->mTangents.size() < pMesh->mPositions.size()-1)
-                pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0));
-
-            // ignore all tangent streams except 0 - there can be only one tangent
-            if( pInput.mIndex == 0)
-                pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                DefaultLogger::get()->error("Collada: just one vertex tangent stream supported");
-            break;
-        case IT_Bitangent:
-            // pad to current vertex count if necessary
-            if( pMesh->mBitangents.size() < pMesh->mPositions.size()-1)
-                pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1));
-
-            // ignore all bitangent streams except 0 - there can be only one bitangent
-            if( pInput.mIndex == 0)
-                pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                DefaultLogger::get()->error("Collada: just one vertex bitangent stream supported");
-            break;
-        case IT_Texcoord:
-            // up to 4 texture coord sets are fine, ignore the others
-            if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
-            {
-                // pad to current vertex count if necessary
-                if( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1)
-                    pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(),
-                        pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0));
-
-                pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2]));
-                if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
-                    pMesh->mNumUVComponents[pInput.mIndex]=3;
-            }   else
-            {
-                DefaultLogger::get()->error("Collada: too many texture coordinate sets. Skipping.");
-            }
-            break;
-        case IT_Color:
-            // up to 4 color sets are fine, ignore the others
-            if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
-            {
-                // pad to current vertex count if necessary
-                if( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1)
-                    pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(),
-                        pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1));
-
-                aiColor4D result(0, 0, 0, 1);
-                for (size_t i = 0; i < pInput.mResolved->mSize; ++i)
-                {
-                    result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
-                }
-                pMesh->mColors[pInput.mIndex].push_back(result);
-            } else
-            {
-                DefaultLogger::get()->error("Collada: too many vertex color sets. Skipping.");
-            }
-
-            break;
-        default:
-            // IT_Invalid and IT_Vertex
-            ai_assert(false && "shouldn't ever get here");
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the library of node hierarchies and scene parts
-void ColladaParser::ReadSceneLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // a visual scene - generate root node under its ID and let ReadNode() do the recursive work
-            if( IsElement( "visual_scene"))
-            {
-                // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
-                int indexID = GetAttribute( "id");
-                const char* attrID = mReader->getAttributeValue( indexID);
-
-                // read name if given.
-                int indexName = TestAttribute( "name");
-                const char* attrName = "unnamed";
-                if( indexName > -1)
-                    attrName = mReader->getAttributeValue( indexName);
-
-                // create a node and store it in the library under its ID
-                Node* node = new Node;
-                node->mID = attrID;
-                node->mName = attrName;
-                mNodeLibrary[node->mID] = node;
-
-                ReadSceneNode( node);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0)
-                //ThrowException( "Expected end of \"library_visual_scenes\" element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a scene node's contents including children and stores it in the given node
-void ColladaParser::ReadSceneNode( Node* pNode)
-{
-    // quit immediately on <bla/> elements
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "node"))
-            {
-                Node* child = new Node;
-                int attrID = TestAttribute( "id");
-                if( attrID > -1)
-                    child->mID = mReader->getAttributeValue( attrID);
-                int attrSID = TestAttribute( "sid");
-                if( attrSID > -1)
-                    child->mSID = mReader->getAttributeValue( attrSID);
-
-                int attrName = TestAttribute( "name");
-                if( attrName > -1)
-                    child->mName = mReader->getAttributeValue( attrName);
-
-                // TODO: (thom) support SIDs
-                // ai_assert( TestAttribute( "sid") == -1);
-
-                if (pNode)
-                {
-                    pNode->mChildren.push_back( child);
-                    child->mParent = pNode;
-                }
-                else
-                {
-                    // no parent node given, probably called from <library_nodes> element.
-                    // create new node in node library
-                    mNodeLibrary[child->mID] = child;
-                }
-
-                // read on recursively from there
-                ReadSceneNode( child);
-                continue;
-            }
-            // For any further stuff we need a valid node to work on
-            else if (!pNode)
-                continue;
-
-            if( IsElement( "lookat"))
-                ReadNodeTransformation( pNode, TF_LOOKAT);
-            else if( IsElement( "matrix"))
-                ReadNodeTransformation( pNode, TF_MATRIX);
-            else if( IsElement( "rotate"))
-                ReadNodeTransformation( pNode, TF_ROTATE);
-            else if( IsElement( "scale"))
-                ReadNodeTransformation( pNode, TF_SCALE);
-            else if( IsElement( "skew"))
-                ReadNodeTransformation( pNode, TF_SKEW);
-            else if( IsElement( "translate"))
-                ReadNodeTransformation( pNode, TF_TRANSLATE);
-            else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
-            {
-                // ... scene evaluation or, in other words, postprocessing pipeline,
-                // or, again in other words, a turing-complete description how to
-                // render a Collada scene. The only thing that is interesting for
-                // us is the primary camera.
-                int attrId = TestAttribute("camera_node");
-                if (-1 != attrId)
-                {
-                    const char* s = mReader->getAttributeValue(attrId);
-                    if (s[0] != '#')
-                        DefaultLogger::get()->error("Collada: Unresolved reference format of camera");
-                    else
-                        pNode->mPrimaryCamera = s+1;
-                }
-            }
-            else if( IsElement( "instance_node"))
-            {
-                // find the node in the library
-                int attrID = TestAttribute( "url");
-                if( attrID != -1)
-                {
-                    const char* s = mReader->getAttributeValue(attrID);
-                    if (s[0] != '#')
-                        DefaultLogger::get()->error("Collada: Unresolved reference format of node");
-                    else
-                    {
-                        pNode->mNodeInstances.push_back(NodeInstance());
-                        pNode->mNodeInstances.back().mNode = s+1;
-                    }
-                }
-            }
-            else if( IsElement( "instance_geometry") || IsElement( "instance_controller"))
-            {
-                // Reference to a mesh or controller, with possible material associations
-                ReadNodeGeometry( pNode);
-            }
-            else if( IsElement( "instance_light"))
-            {
-                // Reference to a light, name given in 'url' attribute
-                int attrID = TestAttribute("url");
-                if (-1 == attrID)
-                    DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_light> element");
-                else
-                {
-                    const char* url = mReader->getAttributeValue( attrID);
-                    if( url[0] != '#')
-                        ThrowException( "Unknown reference format in <instance_light> element");
-
-                    pNode->mLights.push_back(LightInstance());
-                    pNode->mLights.back().mLight = url+1;
-                }
-            }
-            else if( IsElement( "instance_camera"))
-            {
-                // Reference to a camera, name given in 'url' attribute
-                int attrID = TestAttribute("url");
-                if (-1 == attrID)
-                    DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_camera> element");
-                else
-                {
-                    const char* url = mReader->getAttributeValue( attrID);
-                    if( url[0] != '#')
-                        ThrowException( "Unknown reference format in <instance_camera> element");
-
-                    pNode->mCameras.push_back(CameraInstance());
-                    pNode->mCameras.back().mCamera = url+1;
-                }
-            }
-            else
-            {
-                // skip everything else for the moment
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
-void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    std::string tagName = mReader->getNodeName();
-
-    Transform tf;
-    tf.mType = pType;
-
-    // read SID
-    int indexSID = TestAttribute( "sid");
-    if( indexSID >= 0)
-        tf.mID = mReader->getAttributeValue( indexSID);
-
-    // how many parameters to read per transformation type
-    static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
-    const char* content = GetTextContent();
-
-    // read as many parameters and store in the transformation
-    for( unsigned int a = 0; a < sNumParameters[pType]; a++)
-    {
-        // read a number
-        content = fast_atoreal_move<ai_real>( content, tf.f[a]);
-        // skip whitespace after it
-        SkipSpacesAndLineEnd( &content);
-    }
-
-    // place the transformation at the queue of the node
-    pNode->mTransforms.push_back( tf);
-
-    // and consume the closing tag
-    TestClosing( tagName.c_str());
-}
-
-// ------------------------------------------------------------------------------------------------
-// Processes bind_vertex_input and bind elements
-void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "bind_vertex_input"))
-            {
-                Collada::InputSemanticMapEntry vn;
-
-                // effect semantic
-                int n = GetAttribute("semantic");
-                std::string s = mReader->getAttributeValue(n);
-
-                // input semantic
-                n = GetAttribute("input_semantic");
-                vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) );
-
-                // index of input set
-                n = TestAttribute("input_set");
-                if (-1 != n)
-                    vn.mSet = mReader->getAttributeValueAsInt(n);
-
-                tbl.mMap[s] = vn;
-            }
-            else if( IsElement( "bind")) {
-                DefaultLogger::get()->warn("Collada: Found unsupported <bind> element");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
-            if( strcmp( mReader->getNodeName(), "instance_material") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a mesh reference in a node and adds it to the node's mesh list
-void ColladaParser::ReadNodeGeometry( Node* pNode)
-{
-    // referred mesh is given as an attribute of the <instance_geometry> element
-    int attrUrl = GetAttribute( "url");
-    const char* url = mReader->getAttributeValue( attrUrl);
-    if( url[0] != '#')
-        ThrowException( "Unknown reference format");
-
-    Collada::MeshInstance instance;
-    instance.mMeshOrController = url+1; // skipping the leading #
-
-    if( !mReader->isEmptyElement())
-    {
-        // read material associations. Ignore additional elements in between
-        while( mReader->read())
-        {
-            if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-            {
-                if( IsElement( "instance_material"))
-                {
-                    // read ID of the geometry subgroup and the target material
-                    int attrGroup = GetAttribute( "symbol");
-                    std::string group = mReader->getAttributeValue( attrGroup);
-                    int attrMaterial = GetAttribute( "target");
-                    const char* urlMat = mReader->getAttributeValue( attrMaterial);
-                    Collada::SemanticMappingTable s;
-                    if( urlMat[0] == '#')
-                        urlMat++;
-
-                    s.mMatName = urlMat;
-
-                    // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff
-                    if( !mReader->isEmptyElement())
-                        ReadMaterialVertexInputBinding(s);
-
-                    // store the association
-                    instance.mMaterials[group] = s;
-                }
-            }
-            else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-            {
-                if( strcmp( mReader->getNodeName(), "instance_geometry") == 0
-                    || strcmp( mReader->getNodeName(), "instance_controller") == 0)
-                    break;
-            }
-        }
-    }
-
-    // store it
-    pNode->mMeshes.push_back( instance);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the collada scene
-void ColladaParser::ReadScene()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "instance_visual_scene"))
-            {
-                // should be the first and only occurrence
-                if( mRootNode)
-                    ThrowException( "Invalid scene containing multiple root nodes in <instance_visual_scene> element");
-
-                // read the url of the scene to instance. Should be of format "#some_name"
-                int urlIndex = GetAttribute( "url");
-                const char* url = mReader->getAttributeValue( urlIndex);
-                if( url[0] != '#')
-                    ThrowException( "Unknown reference format in <instance_visual_scene> element");
-
-                // find the referred scene, skip the leading #
-                NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1);
-                if( sit == mNodeLibrary.end())
-                    ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
-                mRootNode = sit->second;
-            } else  {
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Aborts the file reading with an exception
-AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const
-{
-    throw DeadlyImportError( format() << "Collada: " << mFileName << " - " << pError );
-}
-void ColladaParser::ReportWarning(const char* msg,...)
-{
-    ai_assert(NULL != msg);
-
-    va_list args;
-    va_start(args,msg);
-
-    char szBuffer[3000];
-    const int iLen = vsprintf(szBuffer,msg,args);
-    ai_assert(iLen > 0);
-
-    va_end(args);
-    DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen));
-}
-
-
-// ------------------------------------------------------------------------------------------------
-// Skips all data until the end node of the current element
-void ColladaParser::SkipElement()
-{
-    // nothing to skip if it's an <element />
-    if( mReader->isEmptyElement())
-        return;
-
-    // reroute
-    SkipElement( mReader->getNodeName());
-}
-
-// ------------------------------------------------------------------------------------------------
-// Skips all data until the end node of the given element
-void ColladaParser::SkipElement( const char* pElement)
-{
-    // copy the current node's name because it'a pointer to the reader's internal buffer,
-    // which is going to change with the upcoming parsing
-    std::string element = pElement;
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-            if( mReader->getNodeName() == element)
-                break;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Tests for an opening element of the given name, throws an exception if not found
-void ColladaParser::TestOpening( const char* pName)
-{
-    // read element start
-    if( !mReader->read())
-        ThrowException( format() << "Unexpected end of file while beginning of <" << pName << "> element." );
-    // whitespace in front is ok, just read again if found
-    if( mReader->getNodeType() == irr::io::EXN_TEXT)
-        if( !mReader->read())
-            ThrowException( format() << "Unexpected end of file while reading beginning of <" << pName << "> element." );
-
-    if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0)
-        ThrowException( format() << "Expected start of <" << pName << "> element." );
-}
-
-// ------------------------------------------------------------------------------------------------
-// Tests for the closing tag of the given element, throws an exception if not found
-void ColladaParser::TestClosing( const char* pName)
-{
-    // check if we're already on the closing tag and return right away
-    if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0)
-        return;
-
-    // if not, read some more
-    if( !mReader->read())
-        ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." );
-    // whitespace in front is ok, just read again if found
-    if( mReader->getNodeType() == irr::io::EXN_TEXT)
-        if( !mReader->read())
-            ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." );
-
-    // but this has the be the closing tag, or we're lost
-    if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
-        ThrowException( format() << "Expected end of <" << pName << "> element." );
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
-int ColladaParser::GetAttribute( const char* pAttr) const
-{
-    int index = TestAttribute( pAttr);
-    if( index != -1)
-        return index;
-
-    // attribute not found -> throw an exception
-    ThrowException( format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">." );
-    return -1;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found
-int ColladaParser::TestAttribute( const char* pAttr) const
-{
-    for( int a = 0; a < mReader->getAttributeCount(); a++)
-        if( strcmp( mReader->getAttributeName( a), pAttr) == 0)
-            return a;
-
-    return -1;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace.
-const char* ColladaParser::GetTextContent()
-{
-    const char* sz = TestTextContent();
-    if(!sz) {
-        ThrowException( "Invalid contents in element \"n\".");
-    }
-    return sz;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the text contents of an element, returns NULL if not given. Skips leading whitespace.
-const char* ColladaParser::TestTextContent()
-{
-    // present node should be the beginning of an element
-    if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement())
-        return NULL;
-
-    // read contents of the element
-    if( !mReader->read() )
-        return NULL;
-    if( mReader->getNodeType() != irr::io::EXN_TEXT)
-        return NULL;
-
-    // skip leading whitespace
-    const char* text = mReader->getNodeData();
-    SkipSpacesAndLineEnd( &text);
-
-    return text;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Calculates the resulting transformation fromm all the given transform steps
-aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform>& pTransforms) const
-{
-    aiMatrix4x4 res;
-
-    for( std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it)
-    {
-        const Transform& tf = *it;
-        switch( tf.mType)
-        {
-            case TF_LOOKAT:
-      {
-        aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]);
-        aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]);
-        aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize();
-        aiVector3D dir = aiVector3D( dstPos - pos).Normalize();
-        aiVector3D right = (dir ^ up).Normalize();
-
-        res *= aiMatrix4x4(
-          right.x, up.x, -dir.x, pos.x,
-          right.y, up.y, -dir.y, pos.y,
-          right.z, up.z, -dir.z, pos.z,
-          0, 0, 0, 1);
-                break;
-      }
-            case TF_ROTATE:
-            {
-                aiMatrix4x4 rot;
-                ai_real angle = tf.f[3] * ai_real( AI_MATH_PI) / ai_real( 180.0 );
-                aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]);
-                aiMatrix4x4::Rotation( angle, axis, rot);
-                res *= rot;
-                break;
-            }
-            case TF_TRANSLATE:
-            {
-                aiMatrix4x4 trans;
-                aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans);
-                res *= trans;
-                break;
-            }
-            case TF_SCALE:
-            {
-                aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
-                    0.0f, 0.0f, 0.0f, 1.0f);
-                res *= scale;
-                break;
-            }
-            case TF_SKEW:
-                // TODO: (thom)
-                ai_assert( false);
-                break;
-            case TF_MATRIX:
-            {
-                aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
-                    tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
-                res *= mat;
-                break;
-            }
-            default:
-                ai_assert( false);
-                break;
-        }
-    }
-
-    return res;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Determines the input data type for the given semantic string
-Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& semantic)
-{
-    if ( semantic.empty() ) {
-        DefaultLogger::get()->warn( format() << "Vertex input type is empty." );
-        return IT_Invalid;
-    }
-
-    if( semantic == "POSITION")
-        return IT_Position;
-    else if( semantic == "TEXCOORD")
-        return IT_Texcoord;
-    else if( semantic == "NORMAL")
-        return IT_Normal;
-    else if( semantic == "COLOR")
-        return IT_Color;
-    else if( semantic == "VERTEX")
-        return IT_Vertex;
-    else if( semantic == "BINORMAL" || semantic ==  "TEXBINORMAL")
-        return IT_Bitangent;
-    else if( semantic == "TANGENT" || semantic == "TEXTANGENT")
-        return IT_Tangent;
-
-    DefaultLogger::get()->warn( format() << "Unknown vertex input type \"" << semantic << "\". Ignoring." );
-    return IT_Invalid;
-}
-
-#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

Vissa filer visades inte eftersom för många filer har ändrats