Bläddra i källkod

Merge remote-tracking branch 'upstream/master' into feature/collada-up_axis-api-improvements

Doug Stephen 8 år sedan
förälder
incheckning
100fa76a90
100 ändrade filer med 6136 tillägg och 1150 borttagningar
  1. 7 0
      .gitattributes
  2. 31 1
      .travis.sh
  3. 34 13
      .travis.yml
  4. 12 0
      CHANGES
  5. 98 49
      CMakeLists.txt
  6. 53 28
      Readme.md
  7. 43 22
      appveyor.yml
  8. 6 3
      code/3DSConverter.cpp
  9. 1 1
      code/3DSLoader.cpp
  10. 0 1
      code/AMFImporter_Postprocess.cpp
  11. 15 4
      code/AssbinExporter.cpp
  12. 0 1
      code/AssbinLoader.h
  13. 24 2
      code/B3DImporter.cpp
  14. 2 0
      code/B3DImporter.h
  15. 5 1
      code/BlenderBMesh.cpp
  16. 1 1
      code/BlenderDNA.h
  17. 4 1
      code/BlenderDNA.inl
  18. 5 1
      code/BlenderLoader.cpp
  19. 10 2
      code/BlenderTessellator.cpp
  20. 2 2
      code/C4DImporter.cpp
  21. 1 1
      code/C4DImporter.h
  22. 28 8
      code/CMakeLists.txt
  23. 5 5
      code/ColladaExporter.cpp
  24. 4 1
      code/ColladaExporter.h
  25. 10 10
      code/ColladaLoader.cpp
  26. 10 1
      code/ColladaParser.cpp
  27. 2 6
      code/D3MFImporter.cpp
  28. 2 1
      code/D3MFOpcPackage.cpp
  29. 2 2
      code/DefaultLogger.cpp
  30. 3 0
      code/Exporter.cpp
  31. 10 18
      code/FBXAnimation.cpp
  32. 58 59
      code/FBXBinaryTokenizer.cpp
  33. 1 1
      code/FBXCompileConfig.h
  34. 29 31
      code/FBXConverter.cpp
  35. 21 46
      code/FBXDocument.cpp
  36. 9 30
      code/FBXDocument.h
  37. 5 1
      code/FBXImporter.cpp
  38. 0 1
      code/FBXMeshGeometry.cpp
  39. 0 3
      code/FBXModel.cpp
  40. 6 9
      code/FBXNodeAttribute.cpp
  41. 5 11
      code/FBXParser.cpp
  42. 0 19
      code/FBXParser.h
  43. 15 28
      code/FBXProperties.h
  44. 0 1
      code/FBXTokenizer.h
  45. 7 1
      code/FIReader.cpp
  46. 1 1
      code/GenericProperty.h
  47. 2 1
      code/HMPLoader.cpp
  48. 1 2
      code/IFCBoolean.cpp
  49. 0 2
      code/IFCCurve.cpp
  50. 5 1
      code/IFCLoader.cpp
  51. 3 3
      code/IFCOpenings.cpp
  52. 1 1
      code/IFCProfile.cpp
  53. 14 3
      code/IOStreamBuffer.h
  54. 2 2
      code/Importer.cpp
  55. 2 0
      code/ImporterRegistry.cpp
  56. 6 7
      code/LogAux.h
  57. 31 31
      code/MDLFileData.h
  58. 3 1
      code/MDLLoader.cpp
  59. 4 4
      code/MMDImporter.cpp
  60. 1 75
      code/MMDPmdParser.h
  61. 9 12
      code/MMDPmxParser.cpp
  62. 0 120
      code/MMDPmxParser.h
  63. 0 32
      code/MMDVmdParser.h
  64. 2 2
      code/MakeVerboseFormat.cpp
  65. 1 1
      code/OFFLoader.cpp
  66. 25 19
      code/ObjExporter.cpp
  67. 3 3
      code/ObjExporter.h
  68. 24 27
      code/ObjFileMtlImporter.cpp
  69. 9 12
      code/ObjFileParser.cpp
  70. 45 12
      code/OpenGEXImporter.cpp
  71. 5 2
      code/OpenGEXImporter.h
  72. 1 4
      code/OptimizeMeshes.cpp
  73. 177 223
      code/PlyLoader.cpp
  74. 2 2
      code/PlyParser.cpp
  75. 10 4
      code/PretransformVertices.cpp
  76. 9 11
      code/Profiler.h
  77. 0 5
      code/Q3BSPZipArchive.cpp
  78. 0 1
      code/Q3BSPZipArchive.h
  79. 24 31
      code/SIBImporter.cpp
  80. 7 3
      code/SMDLoader.cpp
  81. 12 4
      code/SceneCombiner.cpp
  82. 2 6
      code/StreamReader.h
  83. 2 2
      code/UnrealLoader.cpp
  84. 3 5
      code/ValidateDataStructure.cpp
  85. 3 4
      code/Version.cpp
  86. 3 3
      code/X3DImporter.cpp
  87. 4 1
      code/XFileExporter.h
  88. 5 2
      code/XGLLoader.cpp
  89. 1108 0
      code/glTF2Asset.h
  90. 1319 0
      code/glTF2Asset.inl
  91. 93 0
      code/glTF2AssetWriter.h
  92. 639 0
      code/glTF2AssetWriter.inl
  93. 1007 0
      code/glTF2Exporter.cpp
  94. 134 0
      code/glTF2Exporter.h
  95. 654 0
      code/glTF2Importer.cpp
  96. 91 0
      code/glTF2Importer.h
  97. 5 3
      code/glTFAsset.h
  98. 24 15
      code/glTFAsset.inl
  99. 2 7
      code/glTFAssetWriter.inl
  100. 11 11
      code/glTFExporter.cpp

+ 7 - 0
.gitattributes

@@ -13,3 +13,10 @@ CHANGES text eol=lf
 CREDITS text eol=lf
 LICENSE text eol=lf
 Readme.md text eol=lf
+# make sure that repo-specific settings (.gitignore, CI-setup,...)
+# are excluded from the source-package generated via 'git archive'
+.git*      	export-ignore
+/.travis*	export-ignore
+/.coveralls*	export-ignore
+appveyor.yml	export-ignore
+

+ 31 - 1
.travis.sh

@@ -1,6 +1,36 @@
 function generate()
 {
-    cmake -G "Unix Makefiles" -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -DBUILD_SHARED_LIBS=$SHARED_BUILD -DASSIMP_COVERALLS=$ENABLE_COVERALLS
+    OPTIONS="-DASSIMP_WERROR=ON"
+
+    if [ "$DISABLE_EXPORTERS" = "YES" ] ; then
+        OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=YES"
+    else
+        OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=NO"
+    fi
+
+    if [ "$SHARED_BUILD" = "ON" ] ; then
+        OPTIONS="$OPTIONS -DBUILD_SHARED_LIBS=ON"
+    else
+        OPTIONS="$OPTIONS -DBUILD_SHARED_LIBS=OFF"
+    fi
+
+    if [ "$ENABLE_COVERALLS" = "ON" ] ; then
+        OPTIONS="$OPTIONS -DASSIMP_COVERALLS=ON"
+    else
+        OPTIONS="$OPTIONS -DASSIMP_COVERALLS=OFF"
+    fi
+
+    if [ "$ASAN" = "ON" ] ; then
+        OPTIONS="$OPTIONS -DASSIMP_ASAN=ON"
+    else
+        OPTIONS="$OPTIONS -DASSIMP_ASAN=OFF"
+    fi
+
+    if [ "$UBSAN" = "ON" ] ; then
+        OPTIONS="$OPTIONS -DASSIMP_UBSAN=ON"
+    fi
+
+    cmake -G "Unix Makefiles" $OPTIONS
 }
 
 if [ $ANDROID ]; then

+ 34 - 13
.travis.yml

@@ -1,9 +1,19 @@
 sudo: required
 language: cpp
 
+cache: ccache
+
 before_install:
   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq && sudo apt-get install cmake && sudo apt-get install cmake python3 && sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- ; fi
-  - if [ "$TRAVIS_OS_NAME" =   "osx" ]; then brew install cmake python3 homebrew/x11/freeglut; fi
+  - 'if [ "$TRAVIS_OS_NAME" = "osx" ];  then
+       if brew ls --versions cmake > /dev/null; then
+         echo cmake already installed.;
+       else
+         brew install cmake;
+       fi;
+       brew install python3;
+       brew install homebrew/x11/freeglut; 
+    fi'
   - echo -e "#ifndef A_R_H_INC\n#define A_R_H_INC\n#define GitVersion ${TRAVIS_JOB_ID}\n#define GitBranch \"${TRAVIS_BRANCH}\"\n#endif // A_R_H_INC" > revision.h
   # install latest LCOV (1.9 was failing)
   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz && tar xf lcov_1.11.orig.tar.gz && sudo make -C lcov-1.11/ install && gem install coveralls-lcov && lcov --version && g++ --version ; fi
@@ -12,22 +22,36 @@ branches:
   only:
     - master
 
-osx_image: xcode8.3
+os:
+  - linux
+
+compiler:
+  - gcc
+  - clang
 
 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:
-    - LINUX=1 TRAVIS_NO_EXPORT=YES ENABLE_COVERALLS=ON
-    - LINUX=1 TRAVIS_NO_EXPORT=NO  ENABLE_COVERALLS=OFF
-    - LINUX=1 SHARED_BUILD=ON      ENABLE_COVERALLS=OFF
-    - LINUX=1 SHARED_BUILD=OFF     ENABLE_COVERALLS=OFF
 
-compiler:
-  - gcc
-  - clang
+matrix:
+  include:
+    - os: linux
+      compiler: gcc
+      env: DISABLE_EXPORTERS=YES ENABLE_COVERALLS=ON
+    - os: linux
+      compiler: gcc
+      env: SHARED_BUILD=ON
+    - os: linux
+      compiler: clang
+      env: ASAN=ON
+    - os: linux
+      compiler: clang
+      env: UBSAN=ON
+    - os: linux
+      compiler: clang
+      env: SHARED_BUILD=ON
 
 install:
   - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi
@@ -40,9 +64,6 @@ script:
   - export COVERALLS_SERVICE_NAME=travis-ci
   - export COVERALLS_REPO_TOKEN=abc12345
   - . ./.travis.sh
-os:
-  - linux
-  - osx
  
 after_success:
   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && lcov --directory . --capture --output-file coverage.info && lcov --remove coverage.info '/usr/*' 'contrib/*' 'test/*' --output-file coverage.info && lcov --list coverage.info && coveralls-lcov --source-encoding=ISO-8859-1 --repo-token=${COVERALLS_TOKEN} coverage.info ; fi

+ 12 - 0
CHANGES

@@ -1,6 +1,18 @@
 ----------------------------------------------------------------------
 CHANGELOG
 ----------------------------------------------------------------------
+4.0.1 (2017-07-28)
+    - FIXES/HOUSEKEEPING:
+    - fix version test.
+    - Not compiling when using ASSIMP_DOUBLE_PRECISION
+    - Added support for python3
+    - Check if cmake is installed with brew
+    - Low performance in OptimizeMeshesProcess::ProcessNode with huge numbers of meshes
+    - Elapsed seconds not shown correctly
+    - StreamReader: fix out-of-range exception
+    - PPdPmdParser: fix compilation for clang
+
+
 4.0.0 (2017-07-18)
 
 FEATURES:

+ 98 - 49
CMakeLists.txt

@@ -34,7 +34,7 @@
 # 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 )
+CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
 PROJECT( Assimp )
 
 # All supported options ###############################################
@@ -62,11 +62,11 @@ OPTION( ASSIMP_BUILD_ZLIB
   "Build your own zlib"
   OFF
 )
-option( ASSIMP_BUILD_ASSIMP_TOOLS
+OPTION( ASSIMP_BUILD_ASSIMP_TOOLS
   "If the supplementary tools for Assimp are built in addition to the library."
   ON
 )
-option ( ASSIMP_BUILD_SAMPLES
+OPTION ( ASSIMP_BUILD_SAMPLES
   "If the official samples are built as well (needs Glut)."
   OFF
 )
@@ -75,24 +75,36 @@ OPTION ( ASSIMP_BUILD_TESTS
   ON
 )
 OPTION ( ASSIMP_COVERALLS
-   "Eańable this to measure test coverage."
-   OFF
+  "Enable this to measure test coverage."
+  OFF
+)
+OPTION ( ASSIMP_WERROR
+  "Treat warnings as errors."
+  OFF
 )
-option ( SYSTEM_IRRXML
-    "Use system installed Irrlicht/IrrXML library."
-    OFF
+OPTION ( ASSIMP_ASAN
+  "Enable AddressSanitizer."
+  OFF
+)
+OPTION ( ASSIMP_UBSAN
+  "Enable Undefined Behavior sanitizer."
+  OFF
+)
+OPTION ( SYSTEM_IRRXML
+  "Use system installed Irrlicht/IrrXML library."
+  OFF
 )
 OPTION ( BUILD_DOCS
-   "Build documentation using Doxygen."
-   OFF
+  "Build documentation using Doxygen."
+  OFF
 )
 
 if (WIN32)
-    add_definitions( -DWIN32_LEAN_AND_MEAN )
+  ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 endif()
 
 IF(MSVC)
-  set (CMAKE_PREFIX_PATH "D:\\libs\\devil")
+  SET (CMAKE_PREFIX_PATH "D:\\libs\\devil")
   OPTION( ASSIMP_INSTALL_PDB
     "Install MSVC debug files."
     ON
@@ -100,21 +112,24 @@ IF(MSVC)
 ENDIF(MSVC)
 
 IF(NOT BUILD_SHARED_LIBS)
+  MESSAGE(STATUS "Shared libraries disabled")
   SET(LINK_SEARCH_START_STATIC TRUE)
+ELSE()
+  MESSAGE(STATUS "Shared libraries enabled")
 ENDIF(NOT BUILD_SHARED_LIBS)
 
 # Define here the needed parameters
 SET (ASSIMP_VERSION_MAJOR 4)
 SET (ASSIMP_VERSION_MINOR 0)
-SET (ASSIMP_VERSION_PATCH 0) # subversion revision?
+SET (ASSIMP_VERSION_PATCH 1)
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 SET (ASSIMP_SOVERSION 4)
 SET (PROJECT_VERSION "${ASSIMP_VERSION}")
 
-SET(ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources")
+SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
 
 # Needed for openddl_parser config, no use of c++11 at this moment
-add_definitions( -DOPENDDL_NO_USE_CPP11 )
+ADD_DEFINITIONS( -DOPENDDL_NO_USE_CPP11 )
 set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
 
 # Get the current working branch
@@ -136,32 +151,26 @@ EXECUTE_PROCESS(
 )
 
 IF(NOT GIT_COMMIT_HASH)
-  SET(GIT_COMMIT_HASH 0)
+    SET(GIT_COMMIT_HASH 0)
 ENDIF(NOT GIT_COMMIT_HASH)
 
 IF(ASSIMP_DOUBLE_PRECISION)
-  ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION)
+    ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION)
 ENDIF(ASSIMP_DOUBLE_PRECISION)
 
-# Check for OpenMP support
-find_package(OpenMP)
-if (OPENMP_FOUND)
-    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
-    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
-endif()
-
-configure_file(
+CONFIGURE_FILE(
   ${CMAKE_CURRENT_LIST_DIR}/revision.h.in
   ${CMAKE_CURRENT_BINARY_DIR}/revision.h
 )
 
-configure_file(
+CONFIGURE_FILE(
   ${CMAKE_CURRENT_LIST_DIR}/include/assimp/config.h.in
   ${CMAKE_CURRENT_BINARY_DIR}/include/assimp/config.h
 )
 
-include_directories(
+INCLUDE_DIRECTORIES(
     ./
+	include
     ${CMAKE_CURRENT_BINARY_DIR}
     ${CMAKE_CURRENT_BINARY_DIR}/include
 )
@@ -179,7 +188,7 @@ IF( UNIX )
   ENDIF()
 
   # Use GNUInstallDirs for Unix predefined directories
-  include(GNUInstallDirs)
+  INCLUDE(GNUInstallDirs)
 ENDIF( UNIX )
 
 
@@ -192,20 +201,48 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
 ELSEIF(MSVC)
   # enable multi-core compilation with MSVC
   add_compile_options(/MP)
+
+  # disable "elements of array '' will be default initialized" warning on MSVC2013
+  IF(MSVC12)
+    add_compile_options(/wd4351)
+  ENDIF()
 ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -Wno-long-long -pedantic -std=c++11" )
+  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")
 ELSEIF( CMAKE_COMPILER_IS_MINGW )
-  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wno-long-long -pedantic -std=c++11" )
-  add_definitions( -U__STRICT_ANSI__ )
+  SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wno-long-long -std=c++11" )
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+  ADD_DEFINITIONS( -U__STRICT_ANSI__ )
 ENDIF()
 
 if (ASSIMP_COVERALLS)
-    include(Coveralls)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
+    MESSAGE(STATUS "Coveralls enabled")
+    INCLUDE(Coveralls)
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
+endif()
+
+if (ASSIMP_WERROR)
+  MESSAGE(STATUS "Treating warnings as errors")
+  IF (MSVC)
+    add_compile_options(/WX)
+  ELSE()
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
+  ENDIF()
+endif()
+
+if (ASSIMP_ASAN)
+    MESSAGE(STATUS "AddressSanitizer enabled")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
 endif()
 
-INCLUDE_DIRECTORIES( include )
+if (ASSIMP_UBSAN)
+    MESSAGE(STATUS "Undefined Behavior sanitizer enabled")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
+endif()
 
 INCLUDE (FindPkgMacros)
 INCLUDE (PrecompiledHeader)
@@ -237,13 +274,13 @@ ENDIF()
 # Only generate this target if no higher-level project already has
 IF (NOT TARGET uninstall)
   # add make uninstall capability
-  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
   add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
 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)
+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})
 
 FIND_PACKAGE( DirectX )
@@ -265,9 +302,9 @@ ENDIF( NOT ASSIMP_BUILD_ZLIB )
 
 IF( NOT ZLIB_FOUND )
   message(STATUS "compiling zlib from souces")
-  include(CheckIncludeFile)
-  include(CheckTypeSize)
-  include(CheckFunctionExists)
+  INCLUDE(CheckIncludeFile)
+  INCLUDE(CheckTypeSize)
+  INCLUDE(CheckFunctionExists)
   # compile from sources
   add_subdirectory(contrib/zlib)
   SET(ZLIB_FOUND 1)
@@ -472,13 +509,25 @@ if(WIN32)
 
   if(MSVC12 OR MSVC14)
     add_custom_target(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.dll	${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.exp	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.lib	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.lib VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll		${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.dll  VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.exp		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.exp VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.lib		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.lib VERBATIM)
-    add_custom_command(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
+    IF(CMAKE_GENERATOR MATCHES "^Visual Studio")
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.dll	${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.exp	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.lib	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll		${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.dll  VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.exp		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.lib		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Debug/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
+    ELSE()
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.dll	${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.exp	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mt.lib	${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.dll		${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.dll  VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.exp		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.exp VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.ilk VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.lib		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.lib VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
+      ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb		${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM)
+    ENDIF()
   ENDIF(MSVC12 OR MSVC14)
 ENDIF (WIN32)

+ 53 - 28
Readme.md

@@ -1,5 +1,6 @@
 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 ###
 [![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)
@@ -7,7 +8,6 @@ Open Asset Import Library (assimp)
   <img alt="Coverity Scan Build Status"
        src="https://scan.coverity.com/projects/5607/badge.svg"/>
 </a>
-<span class="badge-patreon"><a href="https://www.patreon.com/assimp" title="Donate to this project using Patreon"><img src="https://img.shields.io/badge/patreon-donate-yellow.svg" alt="Patreon donate button" /></a></span>
 [![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)
 <br>
@@ -18,6 +18,11 @@ Additionally, assimp features various __mesh post processing tools__: normals an
 
 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).
 
+Monthly donations via Patreon:
+<br>[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/assimp)
+
+<br>
+
 One-off donations via PayPal:
 <br>[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JRJVPXC4QJM4)
 
@@ -29,50 +34,67 @@ Please check our Wiki as well: https://github.com/assimp/assimp/wiki
 
 A full list [is here](http://assimp.org/main_features_formats.html).
 __Importers__:
-
+- 3D
 - 3DS
+- 3MF
+- AC
+- AC3D
+- ACC
+- AMJ
+- ASE
+- ASK
+- B3D;
 - BLEND (Blender)
+- BVH
+- COB
+- CMS
 - DAE/Collada
+- DXF
+- ENFF
 - FBX
+- glTF 1.0 + GLB
+- glTF 2.0
+- HMB
 - IFC-STEP
-- ASE
-- DXF
-- HMP
+- IRR / IRRMESH
+- LWO
+- LWS
+- LXO
 - MD2
 - MD3
 - MD5
 - MDC
 - MDL
+- MESH / MESH.XML
+- MOT
+- MS3D
+- NDO
 - NFF
-- PLY
-- STL
-- X
 - OBJ
-- OpenGEX
+- OFF
+- OGEX
+- PLY
+- PMX
+- PRJ
+- Q3O
+- Q3S
+- RAW
+- SCN
+- SIB
 - SMD
-- LWO
-- LXO
-- LWS  
+- STL
+- STP
 - TER
-- AC3D
-- MS3D
-- COB
-- Q3BSP
+- UC
+- VTA
+- X
+- X3D
 - XGL
-- CSM
-- BVH
-- B3D
-- NDO
-- Ogre Binary
-- Ogre XML
-- Q3D
-- ASSBIN (Assimp custom format)
-- glTF (partial)
-- 3MF
+- ZGL
 
 Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default):
 
-- C4D (https://github.com/acgessler/assimp-cinema4d)
+- C4D (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange)
 
 __Exporters__:
 
@@ -85,7 +107,8 @@ __Exporters__:
 - JSON (for WebGl, via https://github.com/acgessler/assimp2json)
 - ASSBIN
 - STEP
-- glTF (partial)
+- glTF 1.0 (partial)
+- glTF 2.0 (partial)
 
 ### 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.
@@ -96,6 +119,8 @@ Take a look into the `INSTALL` file. Our build system is CMake, if you used CMak
 * [.NET](port/AssimpNET/Readme.md)
 * [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 (currently supported obj, ply, stl, ~collada)
 
 ### Other tools ###
 [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.

+ 43 - 22
appveyor.yml

@@ -10,33 +10,54 @@ branches:
   only:
     - master
 
+matrix:
+  fast_finish: true
+    
+image:
+  - Visual Studio 2013
+  - Visual Studio 2015
+  - Visual Studio 2017
+    
 platform:
-    - x86
-    - x64
-
-configuration:
-  - 14 2015
-  - 12 2013
-  #- MinGW
-  #- 10 2010 # only works for x86
-
-init:
-- if "%platform%" EQU "x64" ( for %%a in (2008 2010 MinGW) do ( if "%Configuration%"=="%%a" (echo "Skipping unsupported configuration" && exit /b 1 ) ) )
+  - Win32
+  - x64
+  
+configuration: Release
 
 install:
-# Make compiler command line tools available
-- call c:\projects\assimp\scripts\appveyor\compiler_setup.bat
-
-build_script:
-- cd c:\projects\assimp
-- if "%platform%" equ "x64" (cmake CMakeLists.txt -G "Visual Studio %Configuration% Win64")
-- if "%platform%" equ "x86" (cmake CMakeLists.txt -G "Visual Studio %Configuration%")
-- if "%platform%" equ "x64" (msbuild /m /p:Configuration=Release /p:Platform="x64" Assimp.sln)
-- if "%platform%" equ "x86" (msbuild /m /p:Configuration=Release /p:Platform="Win32" Assimp.sln)
-
+  - set PATH=C:\Ruby24-x64\bin;%PATH%
+  - set CMAKE_DEFINES -DASSIMP_WERROR=ON
+  - 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%"
+  
+cache:
+  - code\assimp.dir\%CONFIGURATION%
+  - contrib\zlib\zlibstatic.dir\%CONFIGURATION%
+  - contrib\zlib\zlib.dir\%CONFIGURATION%
+  - tools\assimp_cmd\assimp_cmd.dir\%CONFIGURATION%
+  - tools\assimp_view\assimp_viewer.dir\%CONFIGURATION%
+  - test\unit.dir\%CONFIGURATION%
+  - bin\.mtime_cache
+  
+before_build:
+  - ruby scripts\AppVeyor\mtime_cache -g scripts\AppVeyor\cacheglobs.txt -c bin\.mtime_cache\cache.json
+  
+build:
+  parallel: true
+  project: Assimp.sln
+  
 after_build:
-  - 7z a assimp.7z c:\projects\assimp\bin\release\* c:\projects\assimp\lib\release\*
+  - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
+
+test_script:
+  - cmd: bin\%CONFIGURATION%\unit.exe --gtest_output=xml:testout.xml
 
+on_finish:
+  - ps: (new-object net.webclient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\testout.xml))
+  
 artifacts:
   - path: assimp.7z
     name: assimp_lib

+ 6 - 3
code/3DSConverter.cpp

@@ -56,18 +56,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
+static const unsigned int NotSet = 0xcdcdcdcd;
+
 // ------------------------------------------------------------------------------------------------
 // Setup final material indices, generae a default material if necessary
 void Discreet3DSImporter::ReplaceDefaultMaterial()
 {
-
     // Try to find an existing material that matches the
     // typical default material setting:
     // - no textures
     // - diffuse color (in grey!)
     // NOTE: This is here to workaround the fact that some
     // exporters are writing a default material, too.
-    unsigned int idx = 0xcdcdcdcd;
+    unsigned int idx( NotSet );
     for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
     {
         std::string s = mScene->mMaterials[i].mName;
@@ -93,7 +94,9 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
         }
         idx = i;
     }
-    if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size();
+    if ( NotSet == idx ) {
+        idx = ( unsigned int )mScene->mMaterials.size();
+    }
 
     // now iterate through all meshes and through all faces and
     // find all faces that are using the default material

+ 1 - 1
code/3DSLoader.cpp

@@ -1381,7 +1381,7 @@ void Discreet3DSImporter::ParseColorChunk( aiColor3D* out, bool acceptPercent )
         bGamma = true;
 
     case Discreet3DS::CHUNK_RGBF:
-        if (sizeof(ai_real) * 3 > diff)   {
+        if (sizeof(float) * 3 > diff)   {
             *out = clrError;
             return;
         }

+ 0 - 1
code/AMFImporter_Postprocess.cpp

@@ -686,7 +686,6 @@ std::list<unsigned int> mesh_idx;
 				tmesh->mNumVertices = static_cast<unsigned int>(vert_arr.size());
 				tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
 				tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
-				tmesh->mFaces = new aiFace[face_list_cur.size()];
 
 				memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
 				memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D));

+ 15 - 4
code/AssbinExporter.cpp

@@ -139,6 +139,17 @@ inline size_t Write<aiVector3D>(IOStream * stream, const aiVector3D& v)
     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 <>
@@ -325,7 +336,7 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size)
         {
             AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODE );
 
-			size_t nb_metadata = (node->mMetaData != NULL ? node->mMetaData->mNumProperties : 0);
+			unsigned int nb_metadata = (node->mMetaData != NULL ? node->mMetaData->mNumProperties : 0);
 
             Write<aiString>(&chunk,node->mName);
             Write<aiMatrix4x4>(&chunk,node->mTransformation);
@@ -639,9 +650,9 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size)
                 Write<float>(&chunk,l->mAttenuationQuadratic);
             }
 
-            Write<aiVector3D>(&chunk,(const aiVector3D&)l->mColorDiffuse);
-            Write<aiVector3D>(&chunk,(const aiVector3D&)l->mColorSpecular);
-            Write<aiVector3D>(&chunk,(const aiVector3D&)l->mColorAmbient);
+            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);

+ 0 - 1
code/AssbinLoader.h

@@ -71,7 +71,6 @@ class AssbinImporter : public BaseImporter
 private:
   bool shortened;
   bool compressed;
-protected:
 
 public:
   virtual bool CanRead(

+ 24 - 2
code/B3DImporter.cpp

@@ -82,6 +82,20 @@ static const aiImporterDesc desc = {
 
 //#define DEBUG_B3D
 
+template<typename T>
+void DeleteAllBarePointers(std::vector<T>& x)
+{
+    for(auto p : x)
+    {
+        delete p;
+    }
+}
+
+B3DImporter::~B3DImporter()
+{
+    DeleteAllBarePointers(_animations);
+}
+
 // ------------------------------------------------------------------------------------------------
 bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{
 
@@ -157,7 +171,8 @@ int B3DImporter::ReadByte(){
 // ------------------------------------------------------------------------------------------------
 int B3DImporter::ReadInt(){
     if( _pos+4<=_buf.size() ){
-        int n=*(int*)&_buf[_pos];
+        int n;
+        memcpy(&n, &_buf[_pos], 4);
         _pos+=4;
         return n;
     }
@@ -168,7 +183,8 @@ int B3DImporter::ReadInt(){
 // ------------------------------------------------------------------------------------------------
 float B3DImporter::ReadFloat(){
     if( _pos+4<=_buf.size() ){
-        float n=*(float*)&_buf[_pos];
+        float n;
+        memcpy(&n, &_buf[_pos], 4);
         _pos+=4;
         return n;
     }
@@ -558,13 +574,19 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){
 void B3DImporter::ReadBB3D( aiScene *scene ){
 
     _textures.clear();
+
     _materials.clear();
 
     _vertices.clear();
+
     _meshes.clear();
 
+    DeleteAllBarePointers(_nodes);
     _nodes.clear();
+
     _nodeAnims.clear();
+
+    DeleteAllBarePointers(_animations);
     _animations.clear();
 
     string t=ReadChunk();

+ 2 - 0
code/B3DImporter.h

@@ -59,6 +59,8 @@ namespace Assimp{
 
 class B3DImporter : public BaseImporter{
 public:
+    B3DImporter() = default;
+    virtual ~B3DImporter();
 
     virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
 

+ 5 - 1
code/BlenderBMesh.cpp

@@ -52,7 +52,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp
 {
-    template< > const std::string LogFunctions< BlenderBMeshConverter >::log_prefix = "BLEND_BMESH: ";
+    template< > const char* LogFunctions< BlenderBMeshConverter >::Prefix()
+    {
+        static auto prefix = "BLEND_BMESH: ";
+        return prefix;
+    }
 }
 
 using namespace Assimp;

+ 1 - 1
code/BlenderDNA.h

@@ -253,7 +253,7 @@ public:
      *  a compiler complain is the result.
      *  @param dest Destination value to be written
      *  @param db File database, including input stream. */
-    template <typename T> inline void Convert (T& dest, const FileDatabase& db) const;
+    template <typename T> void Convert (T& dest, const FileDatabase& db) const;
 
     // --------------------------------------------------------
     // generic converter

+ 4 - 1
code/BlenderDNA.inl

@@ -589,7 +589,10 @@ template <> inline void Structure :: Convert<short>  (short& dest,const FileData
 {
     // automatic rescaling from short to float and vice versa (seems to be used by normals)
     if (name == "float") {
-        dest = static_cast<short>(db.reader->GetF4() * 32767.f);
+        float f = db.reader->GetF4();
+        if ( f > 1.0f )
+            f = 1.0f;
+        dest = static_cast<short>( f * 32767.f);
         //db.reader->IncPtr(-4);
         return;
     }

+ 5 - 1
code/BlenderLoader.cpp

@@ -74,7 +74,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endif
 
 namespace Assimp {
-    template<> const std::string LogFunctions<BlenderImporter>::log_prefix = "BLEND: ";
+    template<> const char* LogFunctions<BlenderImporter>::Prefix()
+    {
+        static auto prefix = "BLEND: ";
+        return prefix;
+    }
 }
 
 using namespace Assimp;

+ 10 - 2
code/BlenderTessellator.cpp

@@ -59,7 +59,11 @@ static const unsigned int BLEND_TESS_MAGIC = 0x83ed9ac3;
 
 namspace Assimp
 {
-    template< > const std::string LogFunctions< BlenderTessellatorGL >::log_prefix = "BLEND_TESS_GL: ";
+    template< > const char* LogFunctions< BlenderTessellatorGL >::Prefix()
+    {
+        static auto prefix = "BLEND_TESS_GL: ";
+        return prefix;
+    }
 }
 
 using namespace Assimp;
@@ -252,7 +256,11 @@ void BlenderTessellatorGL::TessellateError( GLenum errorCode, void* )
 
 namespace Assimp
 {
-    template< > const std::string LogFunctions< BlenderTessellatorP2T >::log_prefix = "BLEND_TESS_P2T: ";
+    template< > const char* LogFunctions< BlenderTessellatorP2T >::Prefix()
+    {
+        static auto prefix = "BLEND_TESS_P2T: ";
+        return prefix;
+    }
 }
 
 using namespace Assimp;

+ 2 - 2
code/C4DImporter.cpp

@@ -393,7 +393,7 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
 // ------------------------------------------------------------------------------------------------
 aiMesh* C4DImporter::ReadMesh(BaseObject* object)
 {
-    assert(object != NULL && object->GetType() == Opolygon);
+    ai_assert(object != NULL && object->GetType() == Opolygon);
 
     // based on Melange sample code
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
@@ -635,7 +635,7 @@ unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
     TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
 
     BaseMaterial* const mat = ttag.GetMaterial();
-    assert(mat != NULL);
+    ai_assert(mat != NULL);
 
     const MaterialMap::const_iterator it = material_mapping.find(mat);
     if(it == material_mapping.end()) {

+ 1 - 1
code/C4DImporter.h

@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BaseImporter.h"
 #include "LogAux.h"
 
-#include <set>
+#include <map>
 struct aiNode;
 struct aiMesh;
 struct aiMaterial;

+ 28 - 8
code/CMakeLists.txt

@@ -61,6 +61,7 @@ SET( PUBLIC_HEADERS
   ${HEADER_PATH}/color4.inl
   ${CMAKE_CURRENT_BINARY_DIR}/../include/assimp/config.h
   ${HEADER_PATH}/defs.h
+  ${HEADER_PATH}/Defines.h
   ${HEADER_PATH}/cfileio.h
   ${HEADER_PATH}/light.h
   ${HEADER_PATH}/material.h
@@ -661,6 +662,14 @@ ADD_ASSIMP_IMPORTER( GLTF
   glTFImporter.h
   glTFExporter.h
   glTFExporter.cpp
+  glTF2Asset.h
+  glTF2Asset.inl
+  glTF2AssetWriter.h
+  glTF2AssetWriter.inl
+  glTF2Importer.cpp
+  glTF2Importer.h
+  glTF2Exporter.h
+  glTF2Exporter.cpp
 )
 
 ADD_ASSIMP_IMPORTER( 3MF
@@ -934,14 +943,25 @@ if (ASSIMP_ANDROID_JNIIOSYSTEM)
 endif(ASSIMP_ANDROID_JNIIOSYSTEM)
 
 if(MSVC AND ASSIMP_INSTALL_PDB)
-  install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb
-    DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
-    CONFIGURATIONS Debug
-  )
-  install(FILES ${Assimp_BINARY_DIR}/code/RelWithDebInfo/assimp${LIBRARY_SUFFIX}.pdb
-    DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
-    CONFIGURATIONS RelWithDebInfo
-  )
+  IF(CMAKE_GENERATOR MATCHES "^Visual Studio")
+    install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb
+      DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+      CONFIGURATIONS Debug
+    )
+    install(FILES ${Assimp_BINARY_DIR}/code/RelWithDebInfo/assimp${LIBRARY_SUFFIX}.pdb
+      DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+      CONFIGURATIONS RelWithDebInfo
+    )
+  ELSE()
+    install(FILES ${Assimp_BINARY_DIR}/code/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb
+      DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+      CONFIGURATIONS Debug
+    )
+    install(FILES ${Assimp_BINARY_DIR}/code/assimp${LIBRARY_SUFFIX}.pdb
+      DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+      CONFIGURATIONS RelWithDebInfo
+    )
+  ENDIF()
 endif ()
 
 if (ASSIMP_COVERALLS)

+ 5 - 5
code/ColladaExporter.cpp

@@ -866,8 +866,8 @@ void ColladaExporter::WriteController( size_t pIndex)
 
     std::vector<ai_real> bind_poses;
     bind_poses.reserve(mesh->mNumBones * 16);
-    for( size_t i = 0; i < mesh->mNumBones; ++i)
-        for( size_t j = 0; j < 4; ++j)
+    for(unsigned int i = 0; i < mesh->mNumBones; ++i)
+        for( unsigned int j = 0; j < 4; ++j)
             bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4);
 
     WriteFloatArray( idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real*) bind_poses.data(), bind_poses.size() / 16);
@@ -924,11 +924,11 @@ void ColladaExporter::WriteController( size_t pIndex)
 
     ai_uint weight_index = 0;
     std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1);
-    for( size_t i = 0; i < mesh->mNumBones; ++i)
-        for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+    for( unsigned int i = 0; i < mesh->mNumBones; ++i)
+        for( unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
         {
             unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId;
-            for( size_t k = 0; k < num_influences[vId]; ++k)
+            for( ai_uint k = 0; k < num_influences[vId]; ++k)
             {
                 if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1)
                 {

+ 4 - 1
code/ColladaExporter.h

@@ -128,7 +128,10 @@ protected:
     /// Enters a new xml element, which increases the indentation
     void PushTag() { startstr.append( "  "); }
     /// Leaves an element, decreasing the indentation
-    void PopTag() { ai_assert( startstr.length() > 1); startstr.erase( startstr.length() - 2); }
+    void PopTag() { 
+        ai_assert( startstr.length() > 1); 
+        startstr.erase( startstr.length() - 2); 
+    }
 
     /// Creates a mesh ID for the given mesh
     std::string GetMeshId( size_t pIndex) const {

+ 10 - 10
code/ColladaLoader.cpp

@@ -119,7 +119,7 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo
          *  might be NULL and it's our duty to return true here.
          */
         if (!pIOHandler)return true;
-        const char* tokens[] = {"collada"};
+        const char* tokens[] = {"<collada"};
         return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
     }
     return false;
@@ -674,7 +674,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
     // create morph target meshes if any
     std::vector<aiMesh*> targetMeshes;
     std::vector<float> targetWeights;
-    Collada::MorphMethod method;
+    Collada::MorphMethod method = Collada::Normalized;
 
     for(std::map<std::string, Collada::Controller>::const_iterator it = pParser.mControllerLibrary.begin();
         it != pParser.mControllerLibrary.end(); it++)
@@ -728,7 +728,7 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
                                 ? aiMorphingMethod_MORPH_RELATIVE
                                 : aiMorphingMethod_MORPH_NORMALIZED;
         dstMesh->mAnimMeshes = new aiAnimMesh*[animMeshes.size()];
-        dstMesh->mNumAnimMeshes = animMeshes.size();
+        dstMesh->mNumAnimMeshes = static_cast<unsigned int>(animMeshes.size());
         for (unsigned int i = 0; i < animMeshes.size(); i++)
             dstMesh->mAnimMeshes[i] = animMeshes.at(i);
     }
@@ -1377,9 +1377,9 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
         {
               aiNodeAnim* dstAnim = new aiNodeAnim;
               dstAnim->mNodeName = nodeName;
-              dstAnim->mNumPositionKeys = resultTrafos.size();
-              dstAnim->mNumRotationKeys= resultTrafos.size();
-              dstAnim->mNumScalingKeys = resultTrafos.size();
+              dstAnim->mNumPositionKeys = static_cast<unsigned int>(resultTrafos.size());
+              dstAnim->mNumRotationKeys = static_cast<unsigned int>(resultTrafos.size());
+              dstAnim->mNumScalingKeys = static_cast<unsigned int>(resultTrafos.size());
               dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
               dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
               dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
@@ -1445,11 +1445,11 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
                     ++morphAnimChannelIndex;
                 }
 
-                morphAnim->mNumKeys = morphTimeValues.size();
+                morphAnim->mNumKeys = static_cast<unsigned int>(morphTimeValues.size());
                 morphAnim->mKeys = new aiMeshMorphKey[morphAnim->mNumKeys];
                 for (unsigned int key = 0; key < morphAnim->mNumKeys; key++)
                 {
-                    morphAnim->mKeys[key].mNumValuesAndWeights = morphChannels.size();
+                    morphAnim->mKeys[key].mNumValuesAndWeights = static_cast<unsigned int>(morphChannels.size());
                     morphAnim->mKeys[key].mValues = new unsigned int [morphChannels.size()];
                     morphAnim->mKeys[key].mWeights = new double [morphChannels.size()];
 
@@ -1470,13 +1470,13 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars
     {
         aiAnimation* anim = new aiAnimation;
         anim->mName.Set( pName);
-        anim->mNumChannels = anims.size();
+        anim->mNumChannels = static_cast<unsigned int>(anims.size());
         if (anim->mNumChannels > 0)
         {
             anim->mChannels = new aiNodeAnim*[anims.size()];
             std::copy( anims.begin(), anims.end(), anim->mChannels);
         }
-        anim->mNumMorphMeshChannels = morphAnims.size();
+        anim->mNumMorphMeshChannels = static_cast<unsigned int>(morphAnims.size());
         if (anim->mNumMorphMeshChannels > 0)
         {
             anim->mMorphMeshChannels = new aiMeshMorphAnim*[anim->mNumMorphMeshChannels];

+ 10 - 1
code/ColladaParser.cpp

@@ -2231,7 +2231,7 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
     }
 
 #ifdef ASSIMP_BUILD_DEBUG
-	if (primType != Prim_TriFans && primType != Prim_TriStrips &&
+	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);
     }
@@ -2400,6 +2400,10 @@ size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pP
         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);
@@ -2416,6 +2420,11 @@ size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pP
                 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++)

+ 2 - 6
code/D3MFImporter.cpp

@@ -95,14 +95,10 @@ public:
     XmlSerializer(XmlReader* xmlReader)
         : xmlReader(xmlReader)
     {
-
+		// empty
     }
 
-    void ImportXml(aiScene* scene)
-    {
-
-        scene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
-
+    void ImportXml(aiScene* scene) {
         scene->mRootNode = new aiNode();
         std::vector<aiNode*> children;
 

+ 2 - 1
code/D3MFOpcPackage.cpp

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/ai_assert.h>
 
+#include <cstdlib>
 #include <memory>
 #include <vector>
 #include <map>
@@ -228,7 +229,7 @@ ZipFile::~ZipFile() {
 
 size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
     const size_t size = pSize * pCount;
-    assert(size <= m_Size);
+    ai_assert(size <= m_Size);
 
     std::memcpy(pvBuffer, m_Buffer, size);
 

+ 2 - 2
code/DefaultLogger.cpp

@@ -253,8 +253,8 @@ void DefaultLogger::kill()
 //  Debug message
 void DefaultLogger::OnDebug( const char* message )
 {
-    if ( m_Severity == Logger::NORMAL )
-        return;
+	if ( m_Severity == Logger::NORMAL )
+		return;
 
 	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
 	char msg[Size];

+ 3 - 0
code/Exporter.cpp

@@ -90,6 +90,7 @@ void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportPr
 void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
@@ -144,6 +145,8 @@ Exporter::ExportFormatEntry gExporters[] =
         aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType),
     Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
         aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType),
+    Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2,
+        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER

+ 10 - 18
code/FBXAnimation.cpp

@@ -50,12 +50,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXDocument.h"
 #include "FBXImporter.h"
 #include "FBXDocumentUtil.h"
-#include "FBXProperties.h"
 
 namespace Assimp {
 namespace FBX {
 
-    using namespace Util;
+using namespace Util;
 
 // ------------------------------------------------------------------------------------------------
 AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/)
@@ -88,17 +87,16 @@ AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::s
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 AnimationCurve::~AnimationCurve()
 {
-
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
-AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc,
-    const char* const * target_prop_whitelist /*= NULL*/, size_t whitelist_size /*= 0*/)
+AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, 
+        const Document& doc, const char* const * target_prop_whitelist /*= NULL*/, 
+        size_t whitelist_size /*= 0*/)
 : Object(id, element, name)
 , target()
 , doc(doc)
@@ -155,18 +153,16 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, cons
     props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false);
 }
 
-
 // ------------------------------------------------------------------------------------------------
 AnimationCurveNode::~AnimationCurveNode()
 {
-
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 const AnimationCurveMap& AnimationCurveNode::Curves() const
 {
-    if(curves.empty()) {
+    if ( curves.empty() ) {
         // resolve attached animation curves
         const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
 
@@ -196,7 +192,6 @@ const AnimationCurveMap& AnimationCurveNode::Curves() const
     return curves;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc)
 : Object(id, element, name)
@@ -208,14 +203,12 @@ AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::s
     props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true);
 }
 
-
 // ------------------------------------------------------------------------------------------------
 AnimationLayer::~AnimationLayer()
 {
-
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/,
     size_t whitelist_size /*= 0*/) const
@@ -299,14 +292,13 @@ AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::s
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 AnimationStack::~AnimationStack()
 {
-
+    // empty
 }
 
 } //!FBX
 } //!Assimp
 
-#endif
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 58 - 59
code/FBXBinaryTokenizer.cpp

@@ -56,47 +56,46 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace FBX {
 
-enum Flag
-{
-   e_unknown_0 = 1 << 0,
-   e_unknown_1 = 1 << 1,
-   e_unknown_2 = 1 << 2,
-   e_unknown_3 = 1 << 3,
-   e_unknown_4 = 1 << 4,
-   e_unknown_5 = 1 << 5,
-   e_unknown_6 = 1 << 6,
-   e_unknown_7 = 1 << 7,
-   e_unknown_8 = 1 << 8,
-   e_unknown_9 = 1 << 9,
-   e_unknown_10 = 1 << 10,
-   e_unknown_11 = 1 << 11,
-   e_unknown_12 = 1 << 12,
-   e_unknown_13 = 1 << 13,
-   e_unknown_14 = 1 << 14,
-   e_unknown_15 = 1 << 15,
-   e_unknown_16 = 1 << 16,
-   e_unknown_17 = 1 << 17,
-   e_unknown_18 = 1 << 18,
-   e_unknown_19 = 1 << 19,
-   e_unknown_20 = 1 << 20,
-   e_unknown_21 = 1 << 21,
-   e_unknown_22 = 1 << 22,
-   e_unknown_23 = 1 << 23,
-   e_flag_field_size_64_bit = 1 << 24, // Not sure what is 
-   e_unknown_25 = 1 << 25,
-   e_unknown_26 = 1 << 26,
-   e_unknown_27 = 1 << 27,
-   e_unknown_28 = 1 << 28,
-   e_unknown_29 = 1 << 29,
-   e_unknown_30 = 1 << 30,
-   e_unknown_31 = 1 << 31
-};
-
-bool check_flag(uint32_t flags, Flag to_check)
-{
-   return (flags & to_check) != 0;
-}
-
+//enum Flag
+//{
+//   e_unknown_0 = 1 << 0,
+//   e_unknown_1 = 1 << 1,
+//   e_unknown_2 = 1 << 2,
+//   e_unknown_3 = 1 << 3,
+//   e_unknown_4 = 1 << 4,
+//   e_unknown_5 = 1 << 5,
+//   e_unknown_6 = 1 << 6,
+//   e_unknown_7 = 1 << 7,
+//   e_unknown_8 = 1 << 8,
+//   e_unknown_9 = 1 << 9,
+//   e_unknown_10 = 1 << 10,
+//   e_unknown_11 = 1 << 11,
+//   e_unknown_12 = 1 << 12,
+//   e_unknown_13 = 1 << 13,
+//   e_unknown_14 = 1 << 14,
+//   e_unknown_15 = 1 << 15,
+//   e_unknown_16 = 1 << 16,
+//   e_unknown_17 = 1 << 17,
+//   e_unknown_18 = 1 << 18,
+//   e_unknown_19 = 1 << 19,
+//   e_unknown_20 = 1 << 20,
+//   e_unknown_21 = 1 << 21,
+//   e_unknown_22 = 1 << 22,
+//   e_unknown_23 = 1 << 23,
+//   e_flag_field_size_64_bit = 1 << 24, // Not sure what is 
+//   e_unknown_25 = 1 << 25,
+//   e_unknown_26 = 1 << 26,
+//   e_unknown_27 = 1 << 27,
+//   e_unknown_28 = 1 << 28,
+//   e_unknown_29 = 1 << 29,
+//   e_unknown_30 = 1 << 30,
+//   e_unknown_31 = 1 << 31
+//};
+//
+//bool check_flag(uint32_t flags, Flag to_check)
+//{
+//	return (flags & to_check) != 0;
+//}
 // ------------------------------------------------------------------------------------------------
 Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int offset)
     :
@@ -152,7 +151,8 @@ uint32_t ReadWord(const char* input, const char*& cursor, const char* end)
         TokenizeError("cannot ReadWord, out of bounds",input, cursor);
     }
 
-    uint32_t word = *reinterpret_cast<const uint32_t*>(cursor);
+    uint32_t word;
+    memcpy(&word, cursor, 4);
     AI_SWAP4(word);
 
     cursor += k_to_read;
@@ -341,10 +341,10 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
 
 
 // ------------------------------------------------------------------------------------------------
-bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, uint32_t const flags)
+bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, bool const is64bits)
 {
     // the first word contains the offset at which this block ends
-    const uint64_t end_offset = /*check_flag(flags, e_flag_field_size_64_bit) ? ReadDoubleWord(input, cursor, end) : */ReadWord(input, cursor, end);
+	const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
 
     // we may get 0 if reading reached the end of the file -
     // fbx files have a mysterious extra footer which I don't know
@@ -362,10 +362,10 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
     }
 
     // the second data word contains the number of properties in the scope
-    const uint64_t prop_count = /*check_flag(flags, e_flag_field_size_64_bit) ? ReadDoubleWord(input, cursor, end) : */ReadWord(input, cursor, end);
+	const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
 
     // the third data word contains the length of the property list
-    const uint64_t prop_length = /*check_flag(flags, e_flag_field_size_64_bit) ? ReadDoubleWord(input, cursor, end) :*/ ReadWord(input, cursor, end);
+	const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
 
     // now comes the name of the scope/key
     const char* sbeg, *send;
@@ -392,7 +392,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
     // at the end of each nested block, there is a NUL record to indicate
     // that the sub-scope exists (i.e. to distinguish between P: and P : {})
     // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit.
-    const size_t sentinel_block_length = /*check_flag(flags, e_flag_field_size_64_bit) ? (sizeof(uint64_t) * 3 + 1) : */(sizeof(uint32_t) * 3 + 1);
+	const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t)* 3 + 1) : (sizeof(uint32_t)* 3 + 1);
 
     if (Offset(input, cursor) < end_offset) {
         if (end_offset - Offset(input, cursor) < sentinel_block_length) {
@@ -403,7 +403,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 
         // XXX this is vulnerable to stack overflowing ..
         while(Offset(input, cursor) < end_offset - sentinel_block_length) {
-            ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, flags);
+			ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
         }
         output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) ));
 
@@ -426,6 +426,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 }
 
 // ------------------------------------------------------------------------------------------------
+// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
 void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length)
 {
     ai_assert(input);
@@ -438,19 +439,17 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int le
         TokenizeError("magic bytes not found",0);
     }
 
-
-    //uint32_t offset = 0x15;
-    const char* cursor = input + 0x15;
-
-    const uint32_t flags = ReadWord(input, cursor, input + length);
-
-    const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused
-    (void) padding_0;
-    const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused
-    (void) padding_1;
+    const char* cursor = input + 18;
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	const uint32_t version = ReadWord(input, cursor, input + length);
+	const bool is64bits = version >= 7500;
     while (cursor < input + length)
     {
-        if(!ReadScope(output_tokens, input, cursor, input + length, flags)) {
+		if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
             break;
         }
     }

+ 1 - 1
code/FBXCompileConfig.h

@@ -66,4 +66,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #   endif
 #endif
 
-#endif
+#endif // INCLUDED_AI_FBX_COMPILECONFIG_H

+ 29 - 31
code/FBXConverter.cpp

@@ -55,9 +55,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "StringComparison.h"
 
 #include <assimp/scene.h>
+
 #include <tuple>
 #include <memory>
-
 #include <iterator>
 #include <vector>
 
@@ -931,7 +931,7 @@ bool Converter::NeedsComplexTransformationChain( const Model& model )
         const TransformationComp comp = static_cast< TransformationComp >( i );
 
         if ( comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation ||
-            comp == TransformationComp_GeometricScaling || comp == TransformationComp_GeometricRotation || comp == TransformationComp_GeometricTranslation ) {
+                comp == TransformationComp_GeometricScaling || comp == TransformationComp_GeometricRotation || comp == TransformationComp_GeometricTranslation ) {
             continue;
         }
 
@@ -949,8 +949,7 @@ std::string Converter::NameTransformationChainNode( const std::string& name, Tra
     return name + std::string( MAGIC_NODE_TAG ) + "_" + NameTransformationComp( comp );
 }
 
-void Converter::GenerateTransformationNodeChain( const Model& model,
-    std::vector<aiNode*>& output_nodes )
+void Converter::GenerateTransformationNodeChain( const Model& model, std::vector<aiNode*>& output_nodes )
 {
     const PropertyTable& props = model.Props();
     const Model::RotOrder rot = model.RotationOrder();
@@ -1065,6 +1064,10 @@ void Converter::GenerateTransformationNodeChain( const Model& model,
                 continue;
             }
 
+            if ( comp == TransformationComp_PostRotation  ) {
+                chain[ i ] = chain[ i ].Inverse();
+            }
+
             aiNode* nd = new aiNode();
             output_nodes.push_back( nd );
 
@@ -1093,7 +1096,7 @@ void Converter::SetupNodeMetadata( const Model& model, aiNode& nd )
     DirectPropertyMap unparsedProperties = props.GetUnparsedProperties();
 
     // create metadata on node
-    std::size_t numStaticMetaData = 2;
+    const std::size_t numStaticMetaData = 2;
     aiMetadata* data = aiMetadata::Alloc( static_cast<unsigned int>(unparsedProperties.size() + numStaticMetaData) );
     nd.mMetaData = data;
     int index = 0;
@@ -2368,8 +2371,13 @@ void Converter::ConvertAnimationStack( const AnimationStack& st )
 
     int64_t start_time = st.LocalStart();
     int64_t stop_time = st.LocalStop();
-    double start_timeF = CONVERT_FBX_TIME( start_time );
-    double stop_timeF = CONVERT_FBX_TIME( stop_time );
+    bool has_local_startstop = start_time != 0 || stop_time != 0;
+    if ( !has_local_startstop ) {
+        // no time range given, so accept every keyframe and use the actual min/max time
+        // the numbers are INT64_MIN/MAX, the 20000 is for safety because GenerateNodeAnimations uses an epsilon of 10000
+        start_time = -9223372036854775807ll + 20000;
+        stop_time = 9223372036854775807ll - 20000;
+    }
 
     try {
         for( const NodeMap::value_type& kv : node_map ) {
@@ -2401,27 +2409,23 @@ void Converter::ConvertAnimationStack( const AnimationStack& st )
         return;
     }
 
-    //adjust relative timing for animation
-    {
-        double start_fps = start_timeF * anim_fps;
-
-        for ( unsigned int c = 0; c < anim->mNumChannels; c++ )
-        {
-            aiNodeAnim* channel = anim->mChannels[ c ];
-            for ( uint32_t i = 0; i < channel->mNumPositionKeys; i++ )
-                channel->mPositionKeys[ i ].mTime -= start_fps;
-            for ( uint32_t i = 0; i < channel->mNumRotationKeys; i++ )
-                channel->mRotationKeys[ i ].mTime -= start_fps;
-            for ( uint32_t i = 0; i < channel->mNumScalingKeys; i++ )
-                channel->mScalingKeys[ i ].mTime -= start_fps;
-        }
+    double start_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(start_time) * anim_fps) : min_time;
+    double stop_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(stop_time) * anim_fps) : max_time;
 
-        max_time -= min_time;
+    // adjust relative timing for animation
+    for ( unsigned int c = 0; c < anim->mNumChannels; c++ ) {
+        aiNodeAnim* channel = anim->mChannels[ c ];
+        for ( uint32_t i = 0; i < channel->mNumPositionKeys; i++ )
+            channel->mPositionKeys[ i ].mTime -= start_time_fps;
+        for ( uint32_t i = 0; i < channel->mNumRotationKeys; i++ )
+            channel->mRotationKeys[ i ].mTime -= start_time_fps;
+        for ( uint32_t i = 0; i < channel->mNumScalingKeys; i++ )
+            channel->mScalingKeys[ i ].mTime -= start_time_fps;
     }
 
     // for some mysterious reason, mDuration is simply the maximum key -- the
     // validator always assumes animations to start at zero.
-    anim->mDuration = ( stop_timeF - start_timeF ) * anim_fps;
+    anim->mDuration = stop_time_fps - start_time_fps;
     anim->mTicksPerSecond = anim_fps;
 }
 
@@ -3119,7 +3123,6 @@ void Converter::InterpolateKeys( aiVectorKey* valOut, const KeyTimeList& keys, c
     }
 }
 
-
 void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
     const aiVector3D& def_value,
     double& maxTime,
@@ -3140,7 +3143,6 @@ void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, con
 
         valOut[ i ].mTime = temp[ i ].mTime;
 
-
         GetRotationMatrix( order, temp[ i ].mValue, m );
         aiQuaternion quat = aiQuaternion( aiMatrix3x3( m ) );
 
@@ -3159,7 +3161,6 @@ void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, con
     }
 }
 
-
 void Converter::ConvertTransformOrder_TRStoSRT( aiQuatKey* out_quat, aiVectorKey* out_scale,
     aiVectorKey* out_translation,
     const KeyFrameListList& scaling,
@@ -3218,7 +3219,6 @@ void Converter::ConvertTransformOrder_TRStoSRT( aiQuatKey* out_quat, aiVectorKey
     }
 }
 
-
 aiQuaternion Converter::EulerToQuaternion( const aiVector3D& rot, Model::RotOrder order )
 {
     aiMatrix4x4 m;
@@ -3227,7 +3227,6 @@ aiQuaternion Converter::EulerToQuaternion( const aiVector3D& rot, Model::RotOrde
     return aiQuaternion( aiMatrix3x3( m ) );
 }
 
-
 void Converter::ConvertScaleKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/,
     int64_t start, int64_t stop,
     double& maxTime,
@@ -3248,7 +3247,6 @@ void Converter::ConvertScaleKeys( aiNodeAnim* na, const std::vector<const Animat
         InterpolateKeys( na->mScalingKeys, keys, inputs, aiVector3D( 1.0f, 1.0f, 1.0f ), maxTime, minTime );
 }
 
-
 void Converter::ConvertTranslationKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
     const LayerMap& /*layers*/,
     int64_t start, int64_t stop,
@@ -3267,7 +3265,6 @@ void Converter::ConvertTranslationKeys( aiNodeAnim* na, const std::vector<const
         InterpolateKeys( na->mPositionKeys, keys, inputs, aiVector3D( 0.0f, 0.0f, 0.0f ), maxTime, minTime );
 }
 
-
 void Converter::ConvertRotationKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
     const LayerMap& /*layers*/,
     int64_t start, int64_t stop,
@@ -3289,7 +3286,8 @@ void Converter::ConvertRotationKeys( aiNodeAnim* na, const std::vector<const Ani
 
 void Converter::TransferDataToScene()
 {
-    ai_assert( !out->mMeshes && !out->mNumMeshes );
+    ai_assert( !out->mMeshes );
+    ai_assert( !out->mNumMeshes );
 
     // note: the trailing () ensures initialization with NULL - not
     // many C++ users seem to know this, so pointing it out to avoid

+ 21 - 46
code/FBXDocument.cpp

@@ -70,13 +70,13 @@ LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
 , id(id)
 , flags()
 {
-
+    // empty
 }
 
 // ------------------------------------------------------------------------------------------------
 LazyObject::~LazyObject()
 {
-
+    // empty
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -232,16 +232,15 @@ Object::Object(uint64_t id, const Element& element, const std::string& name)
 , name(name)
 , id(id)
 {
-
+    // empty
 }
 
 // ------------------------------------------------------------------------------------------------
 Object::~Object()
 {
-
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props)
 : props(props)
@@ -292,11 +291,10 @@ Document::~Document()
 }
 
 // ------------------------------------------------------------------------------------------------
-static const int LowerSupportedVersion = 7100;
-static const int UpperSupportedVersion = 7400;
+static const unsigned int LowerSupportedVersion = 7100;
+static const unsigned int UpperSupportedVersion = 7400;
 
-void Document::ReadHeader()
-{
+void Document::ReadHeader() {
     // Read ID objects from "Objects" section
     const Scope& sc = parser.GetRootScope();
     const Element* const ehead = sc["FBXHeaderExtension"];
@@ -362,7 +360,6 @@ void Document::ReadGlobalSettings()
     globals.reset(new FileGlobalSettings(*this, props));
 }
 
-
 // ------------------------------------------------------------------------------------------------
 void Document::ReadObjects()
 {
@@ -388,7 +385,6 @@ void Document::ReadObjects()
         }
 
         const char* err;
-
         const uint64_t id = ParseTokenAsID(*tok[0], err);
         if(err) {
             DOMError(err,el.second);
@@ -470,8 +466,6 @@ void Document::ReadPropertyTemplates()
     }
 }
 
-
-
 // ------------------------------------------------------------------------------------------------
 void Document::ReadConnections()
 {
@@ -483,7 +477,6 @@ void Document::ReadConnections()
     }
 
     uint64_t insertionOrder = 0l;
-
     const Scope& sconns = *econns->Compound();
     const ElementCollection conns = sconns.GetCollection("C");
     for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
@@ -492,7 +485,9 @@ void Document::ReadConnections()
 
         // PP = property-property connection, ignored for now
         // (tokens: "PP", ID1, "Property1", ID2, "Property2")
-        if(type == "PP") continue;
+        if ( type == "PP" ) {
+            continue;
+        }
 
         const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1));
         const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2));
@@ -519,11 +514,10 @@ void Document::ReadConnections()
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 const std::vector<const AnimationStack*>& Document::AnimationStacks() const
 {
-    if (!animationStacksResolved.empty() || !animationStacks.size()) {
+    if (!animationStacksResolved.empty() || animationStacks.empty()) {
         return animationStacksResolved;
     }
 
@@ -541,7 +535,6 @@ const std::vector<const AnimationStack*>& Document::AnimationStacks() const
     return animationStacksResolved;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 LazyObject* Document::GetObject(uint64_t id) const
 {
@@ -552,8 +545,7 @@ LazyObject* Document::GetObject(uint64_t id) const
 #define MAX_CLASSNAMES 6
 
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id,
-    const ConnectionMap& conns) const
+std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const
 {
     std::vector<const Connection*> temp;
 
@@ -570,7 +562,6 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id,
     return temp; // NRVO should handle this
 }
 
-
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
     const ConnectionMap& conns,
@@ -579,17 +570,17 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bo
 
 {
     ai_assert(classnames);
-    ai_assert(count != 0 && count <= MAX_CLASSNAMES);
+    ai_assert( count != 0 );
+    ai_assert( count <= MAX_CLASSNAMES);
 
     size_t lenghts[MAX_CLASSNAMES];
 
     const size_t c = count;
     for (size_t i = 0; i < c; ++i) {
-        lenghts[i] = strlen(classnames[i]);
+        lenghts[ i ] = strlen(classnames[i]);
     }
 
     std::vector<const Connection*> temp;
-
     const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
         conns.equal_range(id);
 
@@ -621,49 +612,40 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bo
     return temp; // NRVO should handle this
 }
 
-
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const
 {
     return GetConnectionsSequenced(source, ConnectionsBySource());
 }
 
-
-
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest,
-    const char* classname) const
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest, const char* classname) const
 {
     const char* arr[] = {classname};
     return GetConnectionsBySourceSequenced(dest, arr,1);
 }
 
-
-
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source,
-    const char* const* classnames, size_t count) const
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source, 
+        const char* const* classnames, size_t count) const
 {
     return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
 }
 
-
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
-    const char* classname) const
+        const char* classname) const
 {
     const char* arr[] = {classname};
     return GetConnectionsByDestinationSequenced(dest, arr,1);
 }
 
-
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const
 {
     return GetConnectionsSequenced(dest, ConnectionsByDestination());
 }
 
-
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
     const char* const* classnames, size_t count) const
@@ -672,10 +654,9 @@ std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(ui
     return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count);
 }
 
-
 // ------------------------------------------------------------------------------------------------
 Connection::Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, const std::string& prop,
-    const Document& doc)
+        const Document& doc)
 
 : insertionOrder(insertionOrder)
 , prop(prop)
@@ -688,14 +669,12 @@ Connection::Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, co
     ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
 }
 
-
 // ------------------------------------------------------------------------------------------------
 Connection::~Connection()
 {
-
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 LazyObject& Connection::LazySourceObject() const
 {
@@ -704,7 +683,6 @@ LazyObject& Connection::LazySourceObject() const
     return *lazy;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 LazyObject& Connection::LazyDestinationObject() const
 {
@@ -713,7 +691,6 @@ LazyObject& Connection::LazyDestinationObject() const
     return *lazy;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 const Object* Connection::SourceObject() const
 {
@@ -722,7 +699,6 @@ const Object* Connection::SourceObject() const
     return lazy->Get();
 }
 
-
 // ------------------------------------------------------------------------------------------------
 const Object* Connection::DestinationObject() const
 {
@@ -735,4 +711,3 @@ const Object* Connection::DestinationObject() const
 } // !Assimp
 
 #endif
-

+ 9 - 30
code/FBXDocument.h

@@ -338,12 +338,7 @@ public:
 class Model : public Object
 {
 public:
-    Model(uint64_t id, const Element& element, const Document& doc, const std::string& name);
-    virtual ~Model();
-
-public:
-    enum RotOrder
-    {
+    enum RotOrder {
         RotOrder_EulerXYZ = 0,
         RotOrder_EulerXZY,
         RotOrder_EulerYZX,
@@ -357,8 +352,7 @@ public:
     };
 
 
-    enum TransformInheritance
-    {
+    enum TransformInheritance {
         TransformInheritance_RrSs = 0,
         TransformInheritance_RSrs,
         TransformInheritance_Rrs,
@@ -366,7 +360,10 @@ public:
         TransformInheritance_MAX // end-of-enum sentinel
     };
 
-public:
+    Model(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
+    virtual ~Model();
+
     fbx_simple_property(QuaternionInterpolate, int, 0)
 
     fbx_simple_property(RotationOffset, aiVector3D, aiVector3D())
@@ -443,7 +440,6 @@ public:
     fbx_simple_property(LODBox, bool, false)
     fbx_simple_property(Freeze, bool, false)
 
-public:
     const std::string& Shading() const {
         return shading;
     }
@@ -462,13 +458,11 @@ public:
         return materials;
     }
 
-
     /** Get geometry links */
     const std::vector<const Geometry*>& GetGeometry() const {
         return geometry;
     }
 
-
     /** Get node attachments */
     const std::vector<const NodeAttribute*>& GetAttributes() const {
         return attributes;
@@ -477,7 +471,6 @@ public:
     /** convenience method to check if the node has a Null node marker */
     bool IsNull() const;
 
-
 private:
     void ResolveLinks(const Element& element, const Document& doc);
 
@@ -603,10 +596,10 @@ public:
 		return textures[index];
 
     }
-	const int textureCount() const {
+	int textureCount() const {
 		return static_cast<int>(textures.size());
 	}
-    const BlendMode GetBlendMode() const
+    BlendMode GetBlendMode() const
     {
         return blendMode;
     }
@@ -654,7 +647,7 @@ public:
         return content;
     }
 
-    const uint32_t ContentLength() const {
+    uint32_t ContentLength() const {
         return contentLength;
     }
 
@@ -805,7 +798,6 @@ private:
 
 typedef std::vector<const AnimationCurveNode*> AnimationCurveNodeList;
 
-
 /** Represents a FBX animation layer (i.e. a list of node animations) */
 class AnimationLayer : public Object
 {
@@ -828,10 +820,8 @@ private:
     const Document& doc;
 };
 
-
 typedef std::vector<const AnimationLayer*> AnimationLayerList;
 
-
 /** Represents a FBX animation stack (i.e. a list of animation layers) */
 class AnimationStack : public Object
 {
@@ -839,7 +829,6 @@ public:
     AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     virtual ~AnimationStack();
 
-public:
     fbx_simple_property(LocalStart, int64_t, 0L)
     fbx_simple_property(LocalStop, int64_t, 0L)
     fbx_simple_property(ReferenceStart, int64_t, 0L)
@@ -879,7 +868,6 @@ private:
 typedef std::vector<float> WeightArray;
 typedef std::vector<unsigned int> WeightIndexArray;
 
-
 /** DOM class for skin deformer clusters (aka subdeformers) */
 class Cluster : public Deformer
 {
@@ -924,8 +912,6 @@ private:
     const Model* node;
 };
 
-
-
 /** DOM class for skin deformers */
 class Skin : public Deformer
 {
@@ -1009,10 +995,8 @@ public:
 typedef std::map<uint64_t, LazyObject*> ObjectMap;
 typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap;
 
-
 typedef std::multimap<uint64_t, const Connection*> ConnectionMap;
 
-
 /** DOM class for global document settings, a single instance per document can
  *  be accessed via Document.Globals(). */
 class FileGlobalSettings
@@ -1074,9 +1058,6 @@ private:
     const Document& doc;
 };
 
-
-
-
 /** DOM root for a FBX file */
 class Document
 {
@@ -1154,8 +1135,6 @@ private:
         const ConnectionMap&,
         const char* const* classnames,
         size_t count) const;
-
-private:
     void ReadHeader();
     void ReadObjects();
     void ReadPropertyTemplates();

+ 5 - 1
code/FBXImporter.cpp

@@ -59,7 +59,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/importerdesc.h>
 
 namespace Assimp {
-    template<> const std::string LogFunctions<FBXImporter>::log_prefix = "FBX: ";
+    template<> const char* LogFunctions<FBXImporter>::Prefix()
+    {
+        static auto prefix = "FBX: ";
+        return prefix;
+    }
 }
 
 using namespace Assimp;

+ 0 - 1
code/FBXMeshGeometry.cpp

@@ -243,7 +243,6 @@ const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, un
     ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() );
     count = m_mapping_counts[ in_index ];
 
-//    ai_assert( count != 0 );
     ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() );
 
     return &m_mappings[ m_mapping_offsets[ in_index ] ];

+ 0 - 3
code/FBXModel.cpp

@@ -77,14 +77,12 @@ Model::Model(uint64_t id, const Element& element, const Document& doc, const std
     ResolveLinks(element,doc);
 }
 
-
 // ------------------------------------------------------------------------------------------------
 Model::~Model()
 {
 
 }
 
-
 // ------------------------------------------------------------------------------------------------
 void Model::ResolveLinks(const Element& element, const Document& doc)
 {
@@ -132,7 +130,6 @@ void Model::ResolveLinks(const Element& element, const Document& doc)
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 bool Model::IsNull() const
 {

+ 6 - 9
code/FBXNodeAttribute.cpp

@@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace FBX {
 
-    using namespace Util;
+using namespace Util;
 
 // ------------------------------------------------------------------------------------------------
 NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name)
@@ -75,7 +75,7 @@ NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document
 // ------------------------------------------------------------------------------------------------
 NodeAttribute::~NodeAttribute()
 {
-
+    // empty
 }
 
 
@@ -101,33 +101,30 @@ CameraSwitcher::CameraSwitcher(uint64_t id, const Element& element, const Docume
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 CameraSwitcher::~CameraSwitcher()
 {
-
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 Camera::Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name)
 : NodeAttribute(id,element,doc,name)
 {
-
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 Camera::~Camera()
 {
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name)
 : NodeAttribute(id,element,doc,name)
 {
-
+    // empty
 }
 
 

+ 5 - 11
code/FBXParser.cpp

@@ -224,41 +224,36 @@ Parser::Parser (const TokenList& tokens, bool is_binary)
     root.reset(new Scope(*this,true));
 }
 
-
 // ------------------------------------------------------------------------------------------------
 Parser::~Parser()
 {
+    // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
 TokenPtr Parser::AdvanceToNextToken()
 {
     last = current;
     if (cursor == tokens.end()) {
         current = NULL;
-    }
-    else {
+    } else {
         current = *cursor++;
     }
     return current;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 TokenPtr Parser::CurrentToken() const
 {
     return current;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 TokenPtr Parser::LastToken() const
 {
     return last;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
 {
@@ -286,7 +281,7 @@ uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
     unsigned int length = static_cast<unsigned int>(t.end() - t.begin());
     ai_assert(length > 0);
 
-    const char* out;
+    const char* out = nullptr;
     const uint64_t id = strtoul10_64(t.begin(),&out,&length);
     if (out > t.end()) {
         err_out = "failed to parse ID (text)";
@@ -296,7 +291,6 @@ uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
     return id;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 size_t ParseTokenAsDim(const Token& t, const char*& err_out)
 {
@@ -333,7 +327,7 @@ size_t ParseTokenAsDim(const Token& t, const char*& err_out)
         return 0;
     }
 
-    const char* out;
+    const char* out = nullptr;
     const size_t id = static_cast<size_t>(strtoul10_64(t.begin() + 1,&out,&length));
     if (out > t.end()) {
         err_out = "failed to parse ID";
@@ -446,7 +440,7 @@ int64_t ParseTokenAsInt64(const Token& t, const char*& err_out)
     unsigned int length = static_cast<unsigned int>(t.end() - t.begin());
     ai_assert(length > 0);
 
-    const char* out;
+    const char* out = nullptr;
     const int64_t id = strtol10_64(t.begin(), &out, &length);
     if (out > t.end()) {
         err_out = "failed to parse Int64 (text)";

+ 0 - 19
code/FBXParser.h

@@ -85,12 +85,9 @@ typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> Element
 class Element
 {
 public:
-
     Element(const Token& key_token, Parser& parser);
     ~Element();
 
-public:
-
     const Scope* Compound() const {
         return compound.get();
     }
@@ -104,14 +101,11 @@ public:
     }
 
 private:
-
     const Token& key_token;
     TokenList tokens;
     std::unique_ptr<Scope> compound;
 };
 
-
-
 /** FBX data entity that consists of a 'scope', a collection
  *  of not necessarily unique #Element instances.
  *
@@ -125,14 +119,10 @@ private:
  *  @endverbatim  */
 class Scope
 {
-
 public:
-
     Scope(Parser& parser, bool topLevel = false);
     ~Scope();
 
-public:
-
     const Element* operator[] (const std::string& index) const {
         ElementMap::const_iterator it = elements.find(index);
         return it == elements.end() ? NULL : (*it).second;
@@ -158,28 +148,23 @@ public:
     }
 
 private:
-
     ElementMap elements;
 };
 
-
 /** FBX parsing class, takes a list of input tokens and generates a hierarchy
  *  of nested #Scope instances, representing the fbx DOM.*/
 class Parser
 {
 public:
-
     /** Parse given a token list. Does not take ownership of the tokens -
      *  the objects must persist during the entire parser lifetime */
     Parser (const TokenList& tokens,bool is_binary);
     ~Parser();
 
-public:
     const Scope& GetRootScope() const {
         return *root.get();
     }
 
-
     bool IsBinary() const {
         return is_binary;
     }
@@ -233,8 +218,6 @@ void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el);
 void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& e);
 void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el);
 
-
-
 // extract a required element from a scope, abort if the element cannot be found
 const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL);
 
@@ -243,8 +226,6 @@ const Scope& GetRequiredScope(const Element& el);
 // get token at a particular index
 const Token& GetRequiredToken(const Element& el, unsigned int index);
 
-
-
 // read a 4x4 matrix from an array of 16 floats
 aiMatrix4x4 ReadMatrix(const Element& element);
 

+ 15 - 28
code/FBXProperties.h

@@ -55,17 +55,14 @@ namespace FBX {
 // Forward declarations
 class Element;
 
-
 /** Represents a dynamic property. Type info added by deriving classes,
  *  see #TypedProperty.
  Example:
  @verbatim
    P: "ShininessExponent", "double", "Number", "",0.5
  @endvebatim
-
 */
-class Property
-{
+class Property {
 protected:
     Property();
 
@@ -79,17 +76,14 @@ public:
     }
 };
 
-
 template<typename T>
-class TypedProperty : public Property
-{
+class TypedProperty : public Property {
 public:
     explicit TypedProperty(const T& value)
-        : value(value)
-    {
+    : value(value) {
+        // empty
     }
 
-public:
     const T& Value() const {
         return value;
     }
@@ -100,21 +94,19 @@ private:
 
 
 typedef std::fbx_unordered_map<std::string,std::shared_ptr<Property> > DirectPropertyMap;
-typedef std::fbx_unordered_map<std::string,const Property*> PropertyMap;
-typedef std::fbx_unordered_map<std::string,const Element*> LazyPropertyMap;
+typedef std::fbx_unordered_map<std::string,const Property*>            PropertyMap;
+typedef std::fbx_unordered_map<std::string,const Element*>             LazyPropertyMap;
 
 /** 
  *  Represents a property table as can be found in the newer FBX files (Properties60, Properties70)
  */
-class PropertyTable
-{
+class PropertyTable {
 public:
     // in-memory property table with no source element
     PropertyTable();
     PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps);
     ~PropertyTable();
 
-public:
     const Property* Get(const std::string& name) const;
 
     // PropertyTable's need not be coupled with FBX elements so this can be NULL
@@ -135,41 +127,37 @@ private:
     const Element* const element;
 };
 
-
 // ------------------------------------------------------------------------------------------------
 template <typename T>
-inline T PropertyGet(const PropertyTable& in, const std::string& name,
-    const T& defaultValue)
-{
+inline 
+T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) {
     const Property* const prop = in.Get(name);
-    if(!prop) {
+    if( nullptr == prop) {
         return defaultValue;
     }
 
     // strong typing, no need to be lenient
     const TypedProperty<T>* const tprop = prop->As< TypedProperty<T> >();
-    if(!tprop) {
+    if( nullptr == tprop) {
         return defaultValue;
     }
 
     return tprop->Value();
 }
 
-
 // ------------------------------------------------------------------------------------------------
 template <typename T>
-inline T PropertyGet(const PropertyTable& in, const std::string& name,
-    bool& result)
-{
+inline 
+T PropertyGet(const PropertyTable& in, const std::string& name, bool& result) {
     const Property* const prop = in.Get(name);
-    if(!prop) {
+    if( nullptr == prop) {
         result = false;
         return T();
     }
 
     // strong typing, no need to be lenient
     const TypedProperty<T>* const tprop = prop->As< TypedProperty<T> >();
-    if(!tprop) {
+    if( nullptr == tprop) {
         result = false;
         return T();
     }
@@ -178,7 +166,6 @@ inline T PropertyGet(const PropertyTable& in, const std::string& name,
     return tprop->Value();
 }
 
-
 } //! FBX
 } //! Assimp
 

+ 0 - 1
code/FBXTokenizer.h

@@ -101,7 +101,6 @@ public:
         return std::string(begin(),end());
     }
 
-public:
     bool IsBinary() const {
         return column == BINARY_MARKER;
     }

+ 7 - 1
code/FIReader.cpp

@@ -45,6 +45,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
 
+// Workaround for issue #1361
+// https://github.com/assimp/assimp/issues/1361
+#ifdef __ANDROID__
+#define _GLIBCXX_USE_C99 1
+#endif
+
 #include "FIReader.hpp"
 #include "Exceptional.h"
 #include <assimp/IOStream.hpp>
@@ -162,7 +168,7 @@ struct FIBase64ValueImpl: public FIBase64Value {
         if (!strValueValid) {
             strValueValid = true;
             std::ostringstream os;
-            uint8_t c1, c2;
+            uint8_t c1 = 0, c2;
             int imod3 = 0;
             std::vector<uint8_t>::size_type valueSize = value.size();
             for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {

+ 1 - 1
code/GenericProperty.h

@@ -111,7 +111,7 @@ inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list,
 
 // ------------------------------------------------------------------------------------------------
 template <class T>
-inline const bool HasGenericProperty(const std::map< unsigned int, T >& list,
+inline bool HasGenericProperty(const std::map< unsigned int, T >& list,
     const char* szName)
 {
     ai_assert(NULL != szName);

+ 2 - 1
code/HMPLoader.cpp

@@ -448,7 +448,8 @@ void HMPImporter::CreateOutputFaceList(unsigned int width,unsigned int height)
 void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
     const unsigned char** szCursorOut)
 {
-    ai_assert(0 != iNumSkins && NULL != szCursor);
+    ai_assert( 0 != iNumSkins );
+    ai_assert( nullptr != szCursor);
 
     // read the type of the skin ...
     // sometimes we need to skip 12 bytes here, I don't know why ...

+ 1 - 2
code/IFCBoolean.cpp

@@ -272,7 +272,6 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const
         const IfcVector3& b0 = boundary[i];
         const IfcVector3& b1 = boundary[(i + 1) % bcount];
         IfcVector3 b = b1 - b0;
-        IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
 
         // segment-segment intersection
         // solve b0 + b*s = e0 + e*t for (s,t)
@@ -281,6 +280,7 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const
             // no solutions (parallel lines)
             continue;
         }
+        IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
 
         const IfcFloat x = b0.x - e0.x;
         const IfcFloat y = b0.y - e0.y;
@@ -381,7 +381,6 @@ bool PointInPoly(const IfcVector3& p, const std::vector<IfcVector3>& boundary)
     IntersectsBoundaryProfile(p, p + IfcVector3(0.6, -0.6, 0.0), boundary, true, intersected_boundary, true);
     votes += intersected_boundary.size() % 2;
 
-//  ai_assert(votes == 3 || votes == 0);
     return votes > 1;
 }
 

+ 0 - 2
code/IFCCurve.cpp

@@ -548,8 +548,6 @@ bool Curve :: InRange(IfcFloat u) const
     const ParamRange range = GetParametricRange();
     if (IsClosed()) {
         return true;
-        //ai_assert(range.first != std::numeric_limits<IfcFloat>::infinity() && range.second != std::numeric_limits<IfcFloat>::infinity());
-        //u = range.first + std::fmod(u-range.first,range.second-range.first);
     }
     const IfcFloat epsilon = 1e-5;
     return u - range.first > -epsilon && range.second - u > -epsilon;

+ 5 - 1
code/IFCLoader.cpp

@@ -66,7 +66,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 namespace Assimp {
-    template<> const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: ";
+    template<> const char* LogFunctions<IFCImporter>::Prefix()
+    {
+        static auto prefix = "IFC: ";
+        return prefix;
+    }
 }
 
 using namespace Assimp;

+ 3 - 3
code/IFCOpenings.cpp

@@ -1492,7 +1492,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
     vmax -= vmin;
 
     // If this happens then the projection must have been wrong.
-    assert(vmax.Length());
+    ai_assert(vmax.Length());
 
     ClipperLib::ExPolygons clipped;
     ClipperLib::Polygons holes_union;
@@ -1616,7 +1616,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
         std::vector<IfcVector3> tmpvec;
         for(ClipperLib::Polygon& opening : holes_union) {
 
-            assert(ClipperLib::Orientation(opening));
+            ai_assert(ClipperLib::Orientation(opening));
 
             tmpvec.clear();
 
@@ -1705,7 +1705,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
                     static_cast<IfcFloat>( tri->GetPoint(i)->y )
                 );
 
-                assert(v.x <= 1.0 && v.x >= 0.0 && v.y <= 1.0 && v.y >= 0.0);
+                ai_assert(v.x <= 1.0 && v.x >= 0.0 && v.y <= 1.0 && v.y >= 0.0);
                 const IfcVector3 v3 = minv * IfcVector3(vmin.x + v.x * vmax.x, vmin.y + v.y * vmax.y,coord) ;
 
                 curmesh.verts.push_back(v3);

+ 1 - 1
code/IFCProfile.cpp

@@ -128,7 +128,7 @@ void ProcessParametrizedProfile(const IfcParameterizedProfileDef& def, TempMesh&
             meshout.verts.push_back( IfcVector3( std::cos(angle)*radius, std::sin(angle)*radius, 0.f ));
         }
 
-        meshout.vertcnt.push_back(segments);
+        meshout.vertcnt.push_back(static_cast<unsigned int>(segments));
     }
     else if( const IfcIShapeProfileDef* const ishape = def.ToPtr<IfcIShapeProfileDef>()) {
         // construct simplified IBeam shape

+ 14 - 3
code/IOStreamBuffer.h

@@ -284,11 +284,17 @@ bool IOStreamBuffer<T>::getNextDataLine( std::vector<T> &buffer, T continuationT
     return true;
 }
 
+static 
+inline
+bool isEndOfCache( size_t pos, size_t cacheSize ) {
+    return ( pos == cacheSize );
+}
+
 template<class T>
 inline
 bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
     buffer.resize(m_cacheSize);
-    if (m_cachePos == m_cacheSize || 0 == m_filePos) {
+    if ( isEndOfCache( m_cachePos, m_cacheSize ) || 0 == m_filePos) {
        if (!readNextBlock()) {
           return false;
        }
@@ -300,11 +306,16 @@ bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
             ++m_cachePos;
         }
         ++m_cachePos;
+        if ( isEndOfCache( m_cachePos, m_cacheSize ) ) {
+            if ( !readNextBlock() ) {
+                return false;
+            }
+        }
     }
 
     size_t i = 0;
-    while (!IsLineEnd(m_cache[m_cachePos])) {
-        buffer[i] = m_cache[m_cachePos];
+    while (!IsLineEnd(m_cache[ m_cachePos ])) {
+        buffer[i] = m_cache[ m_cachePos ];
         m_cachePos++;
         i++;
         if (m_cachePos >= m_cacheSize) {

+ 2 - 2
code/Importer.cpp

@@ -831,8 +831,8 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
     pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
 
     // update private scene flags
-  if( pimpl->mScene )
-    ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
+    if( pimpl->mScene )
+      ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
 
     // clear any data allocated by post-process steps
     pimpl->mPPShared->Clean();

+ 2 - 0
code/ImporterRegistry.cpp

@@ -182,6 +182,7 @@ corresponding preprocessor flag to selectively disable formats.
 #endif
 #ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
 #   include "glTFImporter.h"
+#   include "glTF2Importer.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
 #   include "C4DImporter.h"
@@ -336,6 +337,7 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
 #endif
 #if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER )
     out.push_back( new glTFImporter() );
+    out.push_back( new glTF2Importer() );
 #endif
 #if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER )
     out.push_back( new C4DImporter() );

+ 6 - 7
code/LogAux.h

@@ -60,34 +60,34 @@ public:
     // ------------------------------------------------------------------------------------------------
     static void ThrowException(const std::string& msg)
     {
-        throw DeadlyImportError(log_prefix+msg);
+        throw DeadlyImportError(Prefix()+msg);
     }
 
     // ------------------------------------------------------------------------------------------------
     static void LogWarn(const Formatter::format& message)   {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->warn(log_prefix+(std::string)message);
+            DefaultLogger::get()->warn(Prefix()+(std::string)message);
         }
     }
 
     // ------------------------------------------------------------------------------------------------
     static void LogError(const Formatter::format& message)  {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->error(log_prefix+(std::string)message);
+            DefaultLogger::get()->error(Prefix()+(std::string)message);
         }
     }
 
     // ------------------------------------------------------------------------------------------------
     static void LogInfo(const Formatter::format& message)   {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->info(log_prefix+(std::string)message);
+            DefaultLogger::get()->info(Prefix()+(std::string)message);
         }
     }
 
     // ------------------------------------------------------------------------------------------------
     static void LogDebug(const Formatter::format& message)  {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->debug(log_prefix+(std::string)message);
+            DefaultLogger::get()->debug(Prefix()+(std::string)message);
         }
     }
 
@@ -125,8 +125,7 @@ public:
 #endif
 
 private:
-
-    static const std::string log_prefix;
+    static const char* Prefix();
 
 };
 

+ 31 - 31
code/MDLFileData.h

@@ -126,16 +126,16 @@ struct Header {
     int32_t version;
 
     //! scale factors for each axis
-    aiVector3D scale;
+    ai_real scale[3];
 
     //! translation factors for each axis
-    aiVector3D translate;
+    ai_real translate[3];
 
     //! bounding radius of the mesh
     float boundingradius;
 
     //! Position of the viewer's exe. Ignored
-    aiVector3D vEyePos;
+    ai_real vEyePos[3];
 
     //! Number of textures
     int32_t num_skins;
@@ -164,7 +164,7 @@ struct Header {
 
     //! Could be the total size of the file (and not a float)
     float size;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 
 // -------------------------------------------------------------------------------------
@@ -223,7 +223,7 @@ struct Header_MDL7 {
 
     //! Size of the Frame_MDL7 data structure used in the file
     uint16_t frame_stc_size;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 
 // -------------------------------------------------------------------------------------
@@ -242,7 +242,7 @@ struct Bone_MDL7 {
 
     //! Optional name of the bone
     char name[1 /* DUMMY SIZE */];
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 #if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS)
 #   define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS (16 + 20)
@@ -290,7 +290,7 @@ struct Group_MDL7 {
 
     //! Number of frames
     int32_t numframes;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 #define AI_MDL7_SKINTYPE_MIPFLAG                0x08
 #define AI_MDL7_SKINTYPE_MATERIAL               0x10
@@ -312,7 +312,7 @@ struct Deformer_MDL7 {
     int32_t group_index;
     int32_t elements;
     int32_t deformerdata_size;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 
 // -------------------------------------------------------------------------------------
@@ -324,7 +324,7 @@ struct DeformerElement_MDL7 {
     int32_t element_index;
     char    element_name[AI_MDL7_MAX_BONENAMESIZE];
     int32_t weights;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct DeformerWeight_MDL7
@@ -334,7 +334,7 @@ struct DeformerWeight_MDL7 {
     //! for deformer_typ==0 (==bones) index == vertex index
     int32_t index;
     float   weight;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // don't know why this was in the original headers ...
 typedef int32_t MD7_MATERIAL_ASCDEFSIZE;
@@ -345,7 +345,7 @@ typedef int32_t MD7_MATERIAL_ASCDEFSIZE;
  */
 struct ColorValue_MDL7 {
     float r,g,b,a;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct Material_MDL7
@@ -366,7 +366,7 @@ struct Material_MDL7 {
 
     //! Phong power
     float           Power;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct Skin
@@ -388,7 +388,7 @@ struct Skin {
 
     //! Texture data
     uint8_t *data;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 
 // -------------------------------------------------------------------------------------
@@ -399,7 +399,7 @@ struct Skin {
 struct Skin_MDL5 {
     int32_t size, width, height;
     uint8_t *data;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // maximum length of texture file name
 #if (!defined AI_MDL7_MAX_TEXNAMESIZE)
@@ -416,7 +416,7 @@ struct Skin_MDL7 {
     int32_t         width;
     int32_t         height;
     char            texture_name[AI_MDL7_MAX_TEXNAMESIZE];
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct RGB565
@@ -426,7 +426,7 @@ struct RGB565 {
     uint16_t r : 5;
     uint16_t g : 6;
     uint16_t b : 5;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct ARGB4
@@ -455,7 +455,7 @@ struct GroupSkin {
 
     //! Data of each image
     uint8_t **data;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct TexCoord
@@ -470,7 +470,7 @@ struct TexCoord {
 
     //! Texture coordinate in the ty direction
     int32_t t;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct TexCoord_MDL3
@@ -482,7 +482,7 @@ struct TexCoord_MDL3 {
 
     //! position, vertically in range 0..skinheight-1
     int16_t v;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct TexCoord_MDL7
@@ -494,7 +494,7 @@ struct TexCoord_MDL7 {
 
     //! position, vertically in range 0..1
     float v;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct SkinSet_MDL7
@@ -510,7 +510,7 @@ struct SkinSet_MDL7
 
     //! Material index
     int32_t     material;    // size 4
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct Triangle
@@ -523,7 +523,7 @@ struct Triangle
 
     //! Vertex indices
     int32_t vertex[3];
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct Triangle_MDL3
@@ -536,7 +536,7 @@ struct Triangle_MDL3
 
     //! Index of 3 skin vertices in range 0..numskinverts
     uint16_t index_uv[3];
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct Triangle_MDL7
@@ -549,7 +549,7 @@ struct Triangle_MDL7
 
     //! Two skinsets. The second will be used for multi-texturing
     SkinSet_MDL7  skinsets[2];
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 #if (!defined AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV)
 #   define AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV (6+sizeof(SkinSet_MDL7)-sizeof(uint32_t))
@@ -577,7 +577,7 @@ struct Vertex
 {
     uint8_t v[3];
     uint8_t normalIndex;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 
 // -------------------------------------------------------------------------------------
@@ -603,7 +603,7 @@ struct Vertex_MDL7
         uint8_t norm162index;
         float norm[3];
     };
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct BoneTransform_MDL7
@@ -620,7 +620,7 @@ struct BoneTransform_MDL7
     //! I HATE 3DGS AND THE SILLY DEVELOPER WHO DESIGNED
     //! THIS STUPID FILE FORMAT!
     int8_t _unused_[2];
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 
 #define AI_MDL7_MAX_FRAMENAMESIZE       16
@@ -654,7 +654,7 @@ struct SimpleFrame
 
     //! Vertex list of the frame
     Vertex *verts;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct Frame
@@ -667,7 +667,7 @@ struct Frame
 
     //! Frame data
     SimpleFrame frame;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 
 // -------------------------------------------------------------------------------------
@@ -684,7 +684,7 @@ struct SimpleFrame_MDLn_SP
 
     //! Vertex list of the frame
     Vertex_MDL4 *verts;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 // -------------------------------------------------------------------------------------
 /** \struct GroupFrame
@@ -706,7 +706,7 @@ struct GroupFrame
 
     //! List of single frames
     SimpleFrame *frames;
-} /* PACK_STRUCT */;
+} PACK_STRUCT;
 
 #include "./../include/assimp/Compiler/poppack1.h"
 

+ 3 - 1
code/MDLLoader.cpp

@@ -1132,7 +1132,9 @@ bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInf
     const unsigned char*     szCurrent,
     const unsigned char**    szCurrentOut)
 {
-    ai_assert(NULL != szCurrent && NULL != szCurrentOut);
+    ai_assert( nullptr != szCurrent );
+    ai_assert( nullptr != szCurrentOut);
+
     const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)mBuffer;
 
     // if we have no bones we can simply skip all frames,

+ 4 - 4
code/MMDImporter.cpp

@@ -118,7 +118,7 @@ void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
 
   // Get the file-size and validate it, throwing an exception when fails
   fileStream.seekg(0, fileStream.end);
-  size_t fileSize = fileStream.tellg();
+  size_t fileSize = static_cast<size_t>(fileStream.tellg());
   fileStream.seekg(0, fileStream.beg);
 
   if (fileSize < sizeof(pmx::PmxModel)) {
@@ -278,7 +278,7 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
       bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back(
           aiVertexWeight(index, vsBDEF2_ptr->bone_weight));
       bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back(
-          aiVertexWeight(index, 1.0 - vsBDEF2_ptr->bone_weight));
+          aiVertexWeight(index, 1.0f - vsBDEF2_ptr->bone_weight));
       break;
     case pmx::PmxVertexSkinningType::BDEF4:
       bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back(
@@ -295,7 +295,7 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
       bone_vertex_map[vsSDEF_ptr->bone_index1].push_back(
           aiVertexWeight(index, vsSDEF_ptr->bone_weight));
       bone_vertex_map[vsSDEF_ptr->bone_index2].push_back(
-          aiVertexWeight(index, 1.0 - vsSDEF_ptr->bone_weight));
+          aiVertexWeight(index, 1.0f - vsSDEF_ptr->bone_weight));
       break;
     case pmx::PmxVertexSkinningType::QDEF:
       const auto vsQDEF_ptr =
@@ -325,7 +325,7 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
     aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix);
     auto it = bone_vertex_map.find(ii);
     if (it != bone_vertex_map.end()) {
-      pBone->mNumWeights = it->second.size();
+      pBone->mNumWeights = static_cast<unsigned int>(it->second.size());
       pBone->mWeights = it->second.data();
       it->second.swap(*(new vector<aiVertexWeight>));
     }

+ 1 - 75
code/MMDPmdParser.h

@@ -49,17 +49,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace pmd
 {
-	/// ヘッダ
 	class PmdHeader
 	{
 	public:
-		/// モデル名
 		std::string name;
-		/// モデル名(英語)
 		std::string name_english;
-		/// コメント
 		std::string comment;
-		/// コメント(英語)
 		std::string comment_english;
 
 		bool Read(std::ifstream* stream)
@@ -83,26 +78,19 @@ namespace pmd
 		}
 	};
 
-	/// 頂点
 	class PmdVertex
 	{
 	public:
-		/// 位置
 		float position[3];
 
-		/// 法線
 		float normal[3];
 
-		/// UV座標
 		float uv[2];
 
-		/// 関連ボーンインデックス
 		uint16_t bone_index[2];
 
-		/// ボーンウェイト
 		uint8_t bone_weight;
 
-		/// エッジ不可視
 		bool edge_invisible;
 
 		bool Read(std::ifstream* stream)
@@ -117,27 +105,17 @@ namespace pmd
 		}
 	};
 
-	/// 材質
 	class PmdMaterial
 	{
 	public:
-		/// 減衰色
 		float diffuse[4];
-		/// 光沢度
 		float power;
-		/// 光沢色
 		float specular[3];
-		/// 環境色
 		float ambient[3];
-		/// トーンインデックス
 		uint8_t toon_index;
-		/// エッジ
 		uint8_t edge_flag;
-		/// インデックス数
 		uint32_t index_count;
-		/// テクスチャファイル名
 		std::string texture_filename;
-		/// スフィアファイル名
 		std::string sphere_filename;
 
 		bool Read(std::ifstream* stream)
@@ -158,7 +136,7 @@ namespace pmd
 				sphere_filename.clear();
 			}
 			else {
-				*pstar = (char)NULL;
+				*pstar = 0;
 				texture_filename = std::string(buffer);
 				sphere_filename = std::string(pstar+1);
 			}
@@ -180,23 +158,15 @@ namespace pmd
 		RotationMovement
 	};
 
-	/// ボーン
 	class PmdBone
 	{
 	public:
-		/// ボーン名
 		std::string name;
-		/// ボーン名(英語)
 		std::string name_english;
-		/// 親ボーン番号
 		uint16_t parent_bone_index;
-		/// 末端ボーン番号
 		uint16_t tail_pos_bone_index;
-		/// ボーン種類
 		BoneType bone_type;
-		/// IKボーン番号
 		uint16_t ik_parent_bone_index;
-		/// ボーンのヘッドの位置
 		float bone_head_pos[3];
 
 		void Read(std::istream *stream)
@@ -219,19 +189,13 @@ namespace pmd
 		}
 	};
 
-	/// IK
 	class PmdIk
 	{
 	public:
-		/// IKボーン番号
 		uint16_t ik_bone_index;
-		/// IKターゲットボーン番号
 		uint16_t target_bone_index;
-		/// 再帰回数
 		uint16_t interations;
-		/// 角度制限
 		float angle_limit;
-		/// 影響下ボーン番号
 		std::vector<uint16_t> ik_child_bone_index;
 
 		void Read(std::istream *stream)
@@ -303,7 +267,6 @@ namespace pmd
 		}
 	};
 
-	/// ボーン枠用の枠名
 	class PmdBoneDispName
 	{
 	public:
@@ -338,59 +301,36 @@ namespace pmd
 		}
 	};
 
-	/// 衝突形状
 	enum class RigidBodyShape : uint8_t
 	{
-		/// 球
 		Sphere = 0,
-		/// 直方体
 		Box = 1,
-		/// カプセル
 		Cpusel = 2
 	};
 
-	/// 剛体タイプ
 	enum class RigidBodyType : uint8_t
 	{
-		/// ボーン追従
 		BoneConnected = 0,
-		/// 物理演算
 		Physics = 1,
-		/// 物理演算(Bone位置合せ)
 		ConnectedPhysics = 2
 	};
 
-	/// 剛体
 	class PmdRigidBody
 	{
 	public:
-		/// 名前
 		std::string name;
-		/// 関連ボーン番号
 		uint16_t related_bone_index;
-		/// グループ番号
 		uint8_t group_index;
-		/// マスク
 		uint16_t mask;
-		/// 形状
 		RigidBodyShape shape;
-		/// 大きさ
 		float size[3];
-		/// 位置
 		float position[3];
-		/// 回転
 		float orientation[3];
-		/// 質量
 		float weight;
-		/// 移動ダンピング
 		float linear_damping;
-		/// 回転ダンピング
 		float anglar_damping;
-		/// 反発係数
 		float restitution;
-		/// 摩擦係数
 		float friction;
-		/// 演算方法
 		RigidBodyType rigid_type;
 
 		void Read(std::istream *stream)
@@ -414,31 +354,19 @@ namespace pmd
 		}
 	};
 
-	/// 剛体の拘束
 	class PmdConstraint
 	{
 	public:
-		/// 名前
 		std::string name;
-		/// 剛体Aのインデックス
 		uint32_t rigid_body_index_a;
-		/// 剛体Bのインデックス
 		uint32_t rigid_body_index_b;
-		/// 位置
 		float position[3];
-		/// 回転
 		float orientation[3];
-		/// 最小移動制限
 		float linear_lower_limit[3];
-		/// 最大移動制限
 		float linear_upper_limit[3];
-		/// 最小回転制限
 		float angular_lower_limit[3];
-		/// 最大回転制限
 		float angular_upper_limit[3];
-		/// 移動に対する復元力
 		float linear_stiffness[3];
-		/// 回転に対する復元力
 		float angular_stiffness[3];
 
 		void Read(std::istream *stream)
@@ -459,7 +387,6 @@ namespace pmd
 		}
 	};
 
-	/// PMDモデル
 	class PmdModel
 	{
 	public:
@@ -491,7 +418,6 @@ namespace pmd
 			return result;
 		}
 
-		/// ファイルからPmdModelを生成する
 		static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream)
 		{
 			auto result = mmd::make_unique<PmdModel>();

+ 9 - 12
code/MMDPmxParser.cpp

@@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace pmx
 {
-	/// インデックス値を読み込む
 	int ReadIndex(std::istream *stream, int size)
 	{
 		switch (size)
@@ -79,7 +78,6 @@ namespace pmx
 		}
 	}
 
-	/// 文字列を読み込む
 	std::string ReadString(std::istream *stream, uint8_t encoding)
 	{
 		int size;
@@ -218,8 +216,8 @@ namespace pmx
 
 	void PmxMaterial::Read(std::istream *stream, PmxSetting *setting)
 	{
-		this->material_name = std::move(ReadString(stream, setting->encoding));
-		this->material_english_name = std::move(ReadString(stream, setting->encoding));
+		this->material_name = ReadString(stream, setting->encoding);
+		this->material_english_name = ReadString(stream, setting->encoding);
 		stream->read((char*) this->diffuse, sizeof(float) * 4);
 		stream->read((char*) this->specular, sizeof(float) * 3);
 		stream->read((char*) &this->specularlity, sizeof(float));
@@ -238,7 +236,7 @@ namespace pmx
 		else {
 			this->toon_texture_index = ReadIndex(stream, setting->texture_index_size);
 		}
-		this->memo = std::move(ReadString(stream, setting->encoding));
+		this->memo = ReadString(stream, setting->encoding);
 		stream->read((char*) &this->index_count, sizeof(int));
 	}
 
@@ -255,8 +253,8 @@ namespace pmx
 
 	void PmxBone::Read(std::istream *stream, PmxSetting *setting)
 	{
-		this->bone_name = std::move(ReadString(stream, setting->encoding));
-		this->bone_english_name = std::move(ReadString(stream, setting->encoding));
+		this->bone_name = ReadString(stream, setting->encoding);
+		this->bone_english_name = ReadString(stream, setting->encoding);
 		stream->read((char*) this->position, sizeof(float) * 3);
 		this->parent_index = ReadIndex(stream, setting->bone_index_size);
 		stream->read((char*) &this->level, sizeof(int));
@@ -530,10 +528,10 @@ namespace pmx
 		this->setting.Read(stream);
 
 		// モデル情報
-		this->model_name = std::move(ReadString(stream, setting.encoding));
-		this->model_english_name = std::move(ReadString(stream, setting.encoding));
-		this->model_comment = std::move(ReadString(stream, setting.encoding));
-		this->model_english_comment = std::move(ReadString(stream, setting.encoding));
+		this->model_name = ReadString(stream, setting.encoding);
+		this->model_english_name = ReadString(stream, setting.encoding);
+		this->model_comment = ReadString(stream, setting.encoding);
+		this->model_english_comment = ReadString(stream, setting.encoding);
 
 		// 頂点
 		stream->read((char*) &vertex_count, sizeof(int));
@@ -607,7 +605,6 @@ namespace pmx
 			this->joints[i].Read(stream, &setting);
 		}
 
-		//// ソフトボディ
 		//if (this->version == 2.1f)
 		//{
 		//	stream->read((char*) &this->soft_body_count, sizeof(int));

+ 0 - 120
code/MMDPmxParser.h

@@ -49,7 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace pmx
 {
-	/// インデックス設定
 	class PmxSetting
 	{
 	public:
@@ -64,26 +63,17 @@ namespace pmx
 			, rigidbody_index_size(0)
 		{}
 
-		/// エンコード方式
 		uint8_t encoding;
-		/// 追加UV数
 		uint8_t uv;
-		/// 頂点インデックスサイズ
 		uint8_t vertex_index_size;
-		/// テクスチャインデックスサイズ
 		uint8_t texture_index_size;
-		/// マテリアルインデックスサイズ
 		uint8_t material_index_size;
-		/// ボーンインデックスサイズ
 		uint8_t bone_index_size;
-		/// モーフインデックスサイズ
 		uint8_t morph_index_size;
-		/// 剛体インデックスサイズ
 		uint8_t rigidbody_index_size;
 		void Read(std::istream *stream);
 	};
 
-	/// 頂点スキニングタイプ
 	enum class PmxVertexSkinningType : uint8_t
 	{
 		BDEF1 = 0,
@@ -93,7 +83,6 @@ namespace pmx
 		QDEF = 4,
 	};
 
-	/// 頂点スキニング
 	class PmxVertexSkinning
 	{
 	public:
@@ -200,7 +189,6 @@ namespace pmx
 		void Read(std::istream *stresam, PmxSetting *setting);
 	};
 
-	/// 頂点
 	class PmxVertex
 	{
 	public:
@@ -219,24 +207,16 @@ namespace pmx
 			}
 		}
 
-		/// 位置
 		float position[3];
-		/// 法線
 		float normal[3];
-		/// テクスチャ座標
 		float uv[2];
-		/// 追加テクスチャ座標
 		float uva[4][4];
-		/// スキニングタイプ
 		PmxVertexSkinningType skinning_type;
-		/// スキニング
 		std::unique_ptr<PmxVertexSkinning> skinning;
-		/// エッジ倍率
 		float edge;
 		void Read(std::istream *stream, PmxSetting *setting);
 	};
 
-	/// マテリアル
 	class PmxMaterial
 	{
 	public:
@@ -261,42 +241,25 @@ namespace pmx
 			}
 		}
 
-		/// モデル名
 		std::string material_name;
-		/// モデル英名
 		std::string material_english_name;
-		/// 減衰色
 		float diffuse[4];
-		/// 光沢色
 		float specular[3];
-		/// 光沢度
 		float specularlity;
-		/// 環境色
 		float ambient[3];
-		/// 描画フラグ
 		uint8_t flag;
-		/// エッジ色
 		float edge_color[4];
-		/// エッジサイズ
 		float edge_size;
-		/// アルベドテクスチャインデックス
 		int diffuse_texture_index;
-		/// スフィアテクスチャインデックス
 		int sphere_texture_index;
-		/// スフィアテクスチャ演算モード
 		uint8_t sphere_op_mode;
-		/// 共有トゥーンフラグ
 		uint8_t common_toon_flag;
-		/// トゥーンテクスチャインデックス
 		int toon_texture_index;
-		/// メモ
 		std::string memo;
-		/// 頂点インデックス数
 		int index_count;
 		void Read(std::istream *stream, PmxSetting *setting);
 	};
 
-	/// リンク
 	class PmxIkLink
 	{
 	public:
@@ -310,18 +273,13 @@ namespace pmx
 			}
 		}
 
-		/// リンクボーンインデックス
 		int link_target;
-		/// 角度制限
 		uint8_t angle_lock;
-		/// 最大制限角度
 		float max_radian[3];
-		/// 最小制限角度
 		float min_radian[3];
 		void Read(std::istream *stream, PmxSetting *settingn);
 	};
 
-	/// ボーン
 	class PmxBone
 	{
 	public:
@@ -347,43 +305,24 @@ namespace pmx
 			}
 		}
 
-		/// ボーン名
 		std::string bone_name;
-		/// ボーン英名
 		std::string bone_english_name;
-		/// 位置
 		float position[3];
-		/// 親ボーンインデックス
 		int parent_index;
-		/// 階層
 		int level;
-		/// ボーンフラグ
 		uint16_t bone_flag;
-		/// 座標オフセット(has Target)
 		float offset[3];
-		/// 接続先ボーンインデックス(not has Target)
 		int target_index;
-		/// 付与親ボーンインデックス
 		int grant_parent_index;
-		/// 付与率
 		float grant_weight;
-		/// 固定軸の方向
 		float lock_axis_orientation[3];
-		/// ローカル軸のX軸方向
 		float local_axis_x_orientation[3];
-		/// ローカル軸のY軸方向
 		float local_axis_y_orientation[3];
-		/// 外部親変形のkey値
 		int key;
-		/// IKターゲットボーン
 		int ik_target_bone_index;
-		/// IKループ回数
 		int ik_loop;
-		/// IKループ計算時の角度制限(ラジアン)
 		float ik_loop_angle_limit;
-		/// IKリンク数
 		int ik_link_count;
-		/// IKリンク
 		std::unique_ptr<PmxIkLink []> ik_links;
 		void Read(std::istream *stream, PmxSetting *setting);
 	};
@@ -543,7 +482,6 @@ namespace pmx
 		void Read(std::istream *stream, PmxSetting *setting); //override;
 	};
 
-	/// モーフ
 	class PmxMorph
 	{
 	public:
@@ -551,34 +489,21 @@ namespace pmx
 			: offset_count(0)
 		{
 		}
-		/// モーフ名
 		std::string morph_name;
-		/// モーフ英名
 		std::string morph_english_name;
-		/// カテゴリ
 		MorphCategory category;
-		/// モーフタイプ
 		MorphType morph_type;
-		/// オフセット数
 		int offset_count;
-		/// 頂点モーフ配列
 		std::unique_ptr<PmxMorphVertexOffset []> vertex_offsets;
-		/// UVモーフ配列
 		std::unique_ptr<PmxMorphUVOffset []> uv_offsets;
-		/// ボーンモーフ配列
 		std::unique_ptr<PmxMorphBoneOffset []> bone_offsets;
-		/// マテリアルモーフ配列
 		std::unique_ptr<PmxMorphMaterialOffset []> material_offsets;
-		/// グループモーフ配列
 		std::unique_ptr<PmxMorphGroupOffset []> group_offsets;
-		/// フリップモーフ配列
 		std::unique_ptr<PmxMorphFlipOffset []> flip_offsets;
-		/// インパルスモーフ配列
 		std::unique_ptr<PmxMorphImplusOffset []> implus_offsets;
 		void Read(std::istream *stream, PmxSetting *setting);
 	};
 
-	/// 枠内要素
 	class PmxFrameElement
 	{
 	public:
@@ -587,14 +512,11 @@ namespace pmx
 			, index(0)
 		{
 		}
-		/// 要素対象
 		uint8_t element_target;
-		/// 要素対象インデックス
 		int index;
 		void Read(std::istream *stream, PmxSetting *setting);
 	};
 
-	/// 表示枠
 	class PmxFrame
 	{
 	public:
@@ -603,15 +525,10 @@ namespace pmx
 			, element_count(0)
 		{
 		}
-		/// 枠名
 		std::string frame_name;
-		/// 枠英名
 		std::string frame_english_name;
-		/// 特殊枠フラグ
 		uint8_t frame_flag;
-		/// 枠内要素数
 		int element_count;
-		/// 枠内要素配列
 		std::unique_ptr<PmxFrameElement []> elements;
 		void Read(std::istream *stream, PmxSetting *setting);
 	};
@@ -637,17 +554,11 @@ namespace pmx
 				orientation[i] = 0.0f;
 			}
 		}
-		/// 剛体名
 		std::string girid_body_name;
-		/// 剛体英名
 		std::string girid_body_english_name;
-		/// 関連ボーンインデックス
 		int target_bone;
-		/// グループ
 		uint8_t group;
-		/// マスク
 		uint16_t mask;
-		/// 形状
 		uint8_t shape;
 		float size[3];
 		float position[3];
@@ -818,7 +729,6 @@ namespace pmx
 		void Read(std::istream *stream, PmxSetting *setting);
 	};
 
-	/// PMXモデル
 	class PmxModel
 	{
 	public:
@@ -836,65 +746,35 @@ namespace pmx
 			, soft_body_count(0)
 		{}
 
-		/// バージョン
 		float version;
-		/// 設定
 		PmxSetting setting;
-		/// モデル名
 		std::string model_name;
-		/// モデル英名
 		std::string model_english_name;
-		/// コメント
 		std::string model_comment;
-		/// 英語コメント
 		std::string model_english_comment;
-		/// 頂点数
 		int vertex_count;
-		/// 頂点配列
 		std::unique_ptr<PmxVertex []> vertices;
-		/// インデックス数
 		int index_count;
-		/// インデックス配列
 		std::unique_ptr<int []> indices;
-		/// テクスチャ数
 		int texture_count;
-		/// テクスチャ配列
 		std::unique_ptr< std::string []> textures;
-		/// マテリアル数
 		int material_count;
-		/// マテリアル
 		std::unique_ptr<PmxMaterial []> materials;
-		/// ボーン数
 		int bone_count;
-		/// ボーン配列
 		std::unique_ptr<PmxBone []> bones;
-		/// モーフ数
 		int morph_count;
-		/// モーフ配列
 		std::unique_ptr<PmxMorph []> morphs;
-		/// 表示枠数
 		int frame_count;
-		/// 表示枠配列
 		std::unique_ptr<PmxFrame [] > frames;
-		/// 剛体数
 		int rigid_body_count;
-		/// 剛体配列
 		std::unique_ptr<PmxRigidBody []> rigid_bodies;
-		/// ジョイント数
 		int joint_count;
-		/// ジョイント配列
 		std::unique_ptr<PmxJoint []> joints;
-		/// ソフトボディ数
 		int soft_body_count;
-		/// ソフトボディ配列
 		std::unique_ptr<PmxSoftBody []> soft_bodies;
-		/// モデル初期化
 		void Init();
-		/// モデル読み込み
 		void Read(std::istream *stream);
-		///// ファイルからモデルの読み込み
 		//static std::unique_ptr<PmxModel> ReadFromFile(const char *filename);
-		///// 入力ストリームからモデルの読み込み
 		//static std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream);
 	};
 }

+ 0 - 32
code/MMDVmdParser.h

@@ -50,19 +50,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace vmd
 {
-	/// ボーンフレーム
 	class VmdBoneFrame
 	{
 	public:
-		/// ボーン名
 		std::string name;
-		/// フレーム番号
 		int frame;
-		/// 位置
 		float position[3];
-		/// 回転
 		float orientation[4];
-		/// 補間曲線
 		char interpolation[4][4][4];
 
 		void Read(std::istream* stream)
@@ -86,15 +80,11 @@ namespace vmd
 		}
 	};
 
-	/// 表情フレーム
 	class VmdFaceFrame
 	{
 	public:
-		/// 表情名
 		std::string face_name;
-		/// 表情の重み
 		float weight;
-		/// フレーム番号
 		uint32_t frame;
 
 		void Read(std::istream* stream)
@@ -114,23 +104,15 @@ namespace vmd
 		}
 	};
 
-	/// カメラフレーム
 	class VmdCameraFrame
 	{
 	public:
-		/// フレーム番号
 		int frame;
-		/// 距離
 		float distance;
-		/// 位置
 		float position[3];
-		/// 回転
 		float orientation[3];
-		/// 補間曲線
 		char interpolation[6][4];
-		/// 視野角
 		float angle;
-		/// 不明データ
 		char unknown[3];
 
 		void Read(std::istream *stream)
@@ -156,15 +138,11 @@ namespace vmd
 		}
 	};
 
-	/// ライトフレーム
 	class VmdLightFrame
 	{
 	public:
-		/// フレーム番号
 		int frame;
-		/// 色
 		float color[3];
-		/// 位置
 		float position[3];
 
 		void Read(std::istream* stream)
@@ -182,7 +160,6 @@ namespace vmd
 		}
 	};
 
-	/// IKの有効無効
 	class VmdIkEnable
 	{
 	public:
@@ -190,7 +167,6 @@ namespace vmd
 		bool enable;
 	};
 
-	/// IKフレーム
 	class VmdIkFrame
 	{
 	public:
@@ -229,23 +205,15 @@ namespace vmd
 		}
 	};
 
-	/// VMDモーション
 	class VmdMotion
 	{
 	public:
-		/// モデル名
 		std::string model_name;
-		/// バージョン
 		int version;
-		/// ボーンフレーム
 		std::vector<VmdBoneFrame> bone_frames;
-		/// 表情フレーム
 		std::vector<VmdFaceFrame> face_frames;
-		/// カメラフレーム
 		std::vector<VmdCameraFrame> camera_frames;
-		/// ライトフレーム
 		std::vector<VmdLightFrame> light_frames;
-		/// IKフレーム
 		std::vector<VmdIkFrame> ik_frames;
 
 		static std::unique_ptr<VmdMotion> LoadFromFile(char const *filename)

+ 2 - 2
code/MakeVerboseFormat.cpp

@@ -193,14 +193,14 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
     p = 0;
     while (pcMesh->HasTextureCoords(p))
     {
-        delete pcMesh->mTextureCoords[p];
+        delete[] pcMesh->mTextureCoords[p];
         pcMesh->mTextureCoords[p] = apvTextureCoords[p];
         ++p;
     }
     p = 0;
     while (pcMesh->HasVertexColors(p))
     {
-        delete pcMesh->mColors[p];
+        delete[] pcMesh->mColors[p];
         pcMesh->mColors[p] = apvColorSets[p];
         ++p;
     }

+ 1 - 1
code/OFFLoader.cpp

@@ -94,7 +94,7 @@ bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
     {
         if (!pIOHandler)return true;
         const char* tokens[] = {"off"};
-        return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+        return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,3);
     }
     return false;
 }

+ 25 - 19
code/ObjExporter.cpp

@@ -91,11 +91,11 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene)
 , vn()
 , vt()
 , vc() 
-, vpMap()
-, vnMap()
-, vtMap()
-, vcMap()
-, meshes()
+, mVpMap()
+, mVnMap()
+, mVtMap()
+, mVcMap()
+, mMeshes()
 , endl("\n") {
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     const std::locale& l = std::locale("C");
@@ -137,15 +137,21 @@ std::string ObjExporter::GetMaterialLibFileName() {
 }
 
 // ------------------------------------------------------------------------------------------------
-void ObjExporter :: WriteHeader(std::ostringstream& out) {
+void ObjExporter::WriteHeader(std::ostringstream& out) {
     out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl;
-    out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl  << endl;
+    out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.'
+        << aiGetVersionRevision() << ")" << endl  << endl;
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string ObjExporter :: GetMaterialName(unsigned int index)
+std::string ObjExporter::GetMaterialName(unsigned int index)
 {
     const aiMaterial* const mat = pScene->mMaterials[index];
+    if ( nullptr == mat ) {
+        static const std::string EmptyStr;
+        return EmptyStr;
+    }
+
     aiString s;
     if(AI_SUCCESS == mat->Get(AI_MATKEY_NAME,s)) {
         return std::string(s.data,s.length);
@@ -235,8 +241,8 @@ void ObjExporter::WriteGeometryFile() {
     AddNode(pScene->mRootNode, mBase);
 
     // write vertex positions with colors, if any
-    vpMap.getVectors( vp );
-    vcMap.getColors( vc );
+    mVpMap.getVectors( vp );
+    mVcMap.getColors( vc );
     if ( vc.empty() ) {
         mOutput << "# " << vp.size() << " vertex positions" << endl;
         for ( const aiVector3D& v : vp ) {
@@ -253,7 +259,7 @@ void ObjExporter::WriteGeometryFile() {
     mOutput << endl;
 
     // write uv coordinates
-    vtMap.getVectors(vt);
+    mVtMap.getVectors(vt);
     mOutput << "# " << vt.size() << " UV coordinates" << endl;
     for(const aiVector3D& v : vt) {
         mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl;
@@ -261,7 +267,7 @@ void ObjExporter::WriteGeometryFile() {
     mOutput << endl;
 
     // write vertex normals
-    vnMap.getVectors(vn);
+    mVnMap.getVectors(vn);
     mOutput << "# " << vn.size() << " vertex normals" << endl;
     for(const aiVector3D& v : vn) {
         mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl;
@@ -269,7 +275,7 @@ void ObjExporter::WriteGeometryFile() {
     mOutput << endl;
 
     // now write all mesh instances
-    for(const MeshInstance& m : meshes) {
+    for(const MeshInstance& m : mMeshes) {
         mOutput << "# Mesh \'" << m.name << "\' with " << m.faces.size() << " faces" << endl;
         if (!m.name.empty()) {
             mOutput << "g " << m.name << endl;
@@ -345,8 +351,8 @@ void ObjExporter::colIndexMap::getColors( std::vector<aiColor4D> &colors ) {
 
 // ------------------------------------------------------------------------------------------------
 void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) {
-    meshes.push_back(MeshInstance());
-    MeshInstance& mesh = meshes.back();
+    mMeshes.push_back(MeshInstance());
+    MeshInstance& mesh = mMeshes.back();
 
     mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_" + std::string(m->mName.data,m->mName.length) : "");
     mesh.matname = GetMaterialName(m->mMaterialIndex);
@@ -373,24 +379,24 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
             const unsigned int idx = f.mIndices[a];
 
             aiVector3D vert = mat * m->mVertices[idx];
-            face.indices[a].vp = vpMap.getIndex(vert);
+            face.indices[a].vp = mVpMap.getIndex(vert);
 
             if (m->mNormals) {
                 aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
-                face.indices[a].vn = vnMap.getIndex(norm);
+                face.indices[a].vn = mVnMap.getIndex(norm);
             } else {
                 face.indices[a].vn = 0;
             }
 
             if ( nullptr != m->mColors[ 0 ] ) {
                 aiColor4D col4 = m->mColors[ 0 ][ idx ];
-                face.indices[ a ].vc = vcMap.getIndex( col4 );
+                face.indices[ a ].vc = mVcMap.getIndex( col4 );
             } else {
                 face.indices[ a ].vc = 0;
             }
 
             if ( m->mTextureCoords[ 0 ] ) {
-                face.indices[a].vt = vtMap.getIndex(m->mTextureCoords[0][idx]);
+                face.indices[a].vt = mVtMap.getIndex(m->mTextureCoords[0][idx]);
             } else {
                 face.indices[a].vt = 0;
             }

+ 3 - 3
code/ObjExporter.h

@@ -163,9 +163,9 @@ private:
         void getColors( std::vector<aiColor4D> &colors );
     };
 
-    vecIndexMap vpMap, vnMap, vtMap;
-    colIndexMap vcMap;
-    std::vector<MeshInstance> meshes;
+    vecIndexMap mVpMap, mVnMap, mVtMap;
+    colIndexMap mVcMap;
+    std::vector<MeshInstance> mMeshes;
 
     // this endl() doesn't flush() the stream
     const std::string endl;

+ 24 - 27
code/ObjFileMtlImporter.cpp

@@ -55,20 +55,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp    {
 
-// Material specific token
-static const std::string DiffuseTexture      = "map_Kd";
-static const std::string AmbientTexture      = "map_Ka";
-static const std::string SpecularTexture     = "map_Ks";
-static const std::string OpacityTexture      = "map_d";
-static const std::string EmissiveTexture    = "map_emissive";
-static const std::string EmissiveTexture_1  = "map_Ke";
-static const std::string BumpTexture1        = "map_bump";
-static const std::string BumpTexture2        = "map_Bump";
-static const std::string BumpTexture3        = "bump";
-static const std::string NormalTexture       = "map_Kn";
-static const std::string ReflectionTexture   = "refl";
-static const std::string DisplacementTexture = "disp";
-static const std::string SpecularityTexture  = "map_ns";
+// Material specific token (case insensitive compare)
+static const std::string DiffuseTexture       = "map_Kd";
+static const std::string AmbientTexture       = "map_Ka";
+static const std::string SpecularTexture      = "map_Ks";
+static const std::string OpacityTexture       = "map_d";
+static const std::string EmissiveTexture1     = "map_emissive";
+static const std::string EmissiveTexture2     = "map_Ke";
+static const std::string BumpTexture1         = "map_bump";
+static const std::string BumpTexture2         = "bump";
+static const std::string NormalTexture        = "map_Kn";
+static const std::string ReflectionTexture    = "refl";
+static const std::string DisplacementTexture1 = "map_disp";
+static const std::string DisplacementTexture2 = "disp";
+static const std::string SpecularityTexture   = "map_ns";
 
 // texture option specific token
 static const std::string BlendUOption       = "-blendu";
@@ -304,7 +304,7 @@ void ObjFileMtlImporter::createMaterial()
         m_pModel->m_pCurrentMaterial = new ObjFile::Material();
         m_pModel->m_pCurrentMaterial->MaterialName.Set( name );
         if (m_pModel->m_pCurrentMesh) {
-            m_pModel->m_pCurrentMesh->m_uiMaterialIndex = m_pModel->m_MaterialLib.size() - 1;
+            m_pModel->m_pCurrentMesh->m_uiMaterialIndex = static_cast<unsigned int>(m_pModel->m_MaterialLib.size() - 1);
         }
         m_pModel->m_MaterialLib.push_back( name );
         m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial;
@@ -329,7 +329,7 @@ void ObjFileMtlImporter::getTexture() {
         // Ambient texture
         out = & m_pModel->m_pCurrentMaterial->textureAmbient;
         clampIndex = ObjFile::Material::TextureAmbientType;
-    } else if (!ASSIMP_strincmp( pPtr, SpecularTexture.c_str(), static_cast<unsigned int>(SpecularTexture.size()) ) ) {
+    } else if ( !ASSIMP_strincmp( pPtr, SpecularTexture.c_str(), static_cast<unsigned int>(SpecularTexture.size()) ) ) {
         // Specular texture
         out = & m_pModel->m_pCurrentMaterial->textureSpecular;
         clampIndex = ObjFile::Material::TextureSpecularType;
@@ -337,33 +337,30 @@ void ObjFileMtlImporter::getTexture() {
         // Opacity texture
         out = & m_pModel->m_pCurrentMaterial->textureOpacity;
         clampIndex = ObjFile::Material::TextureOpacityType;
-    } else if (!ASSIMP_strincmp( pPtr, EmissiveTexture.c_str(), static_cast<unsigned int>(EmissiveTexture.size()) ) ) {
+    } else if ( !ASSIMP_strincmp( pPtr, EmissiveTexture1.c_str(), static_cast<unsigned int>(EmissiveTexture1.size()) ) ||
+                !ASSIMP_strincmp( pPtr, EmissiveTexture2.c_str(), static_cast<unsigned int>(EmissiveTexture2.size()) ) ) {
         // Emissive texture
         out = & m_pModel->m_pCurrentMaterial->textureEmissive;
         clampIndex = ObjFile::Material::TextureEmissiveType;
-    } else if ( !ASSIMP_strincmp( pPtr, EmissiveTexture_1.c_str(), static_cast<unsigned int>(EmissiveTexture_1.size()) ) ) {
-        // Emissive texture
-        out = &m_pModel->m_pCurrentMaterial->textureEmissive;
-        clampIndex = ObjFile::Material::TextureEmissiveType;
     } else if ( !ASSIMP_strincmp( pPtr, BumpTexture1.c_str(), static_cast<unsigned int>(BumpTexture1.size()) ) ||
-                !ASSIMP_strincmp( pPtr, BumpTexture2.c_str(), static_cast<unsigned int>(BumpTexture2.size()) ) ||
-                !ASSIMP_strincmp( pPtr, BumpTexture3.c_str(), static_cast<unsigned int>(BumpTexture3.size()) ) ) {
+                !ASSIMP_strincmp( pPtr, BumpTexture2.c_str(), static_cast<unsigned int>(BumpTexture2.size()) ) ) {
         // Bump texture
         out = & m_pModel->m_pCurrentMaterial->textureBump;
         clampIndex = ObjFile::Material::TextureBumpType;
-    } else if (!ASSIMP_strincmp( pPtr,NormalTexture.c_str(), static_cast<unsigned int>(NormalTexture.size()) ) ) {
+    } else if ( !ASSIMP_strincmp( pPtr,NormalTexture.c_str(), static_cast<unsigned int>(NormalTexture.size()) ) ) {
         // Normal map
         out = & m_pModel->m_pCurrentMaterial->textureNormal;
         clampIndex = ObjFile::Material::TextureNormalType;
-    } else if(!ASSIMP_strincmp( pPtr, ReflectionTexture.c_str(), static_cast<unsigned int>(ReflectionTexture.size()) ) ) {
+    } else if( !ASSIMP_strincmp( pPtr, ReflectionTexture.c_str(), static_cast<unsigned int>(ReflectionTexture.size()) ) ) {
         // Reflection texture(s)
         //Do nothing here
         return;
-    } else if (!ASSIMP_strincmp( pPtr, DisplacementTexture.c_str(), static_cast<unsigned int>(DisplacementTexture.size()) ) ) {
+    } else if ( !ASSIMP_strincmp( pPtr, DisplacementTexture1.c_str(), static_cast<unsigned int>(DisplacementTexture1.size()) ) ||
+                !ASSIMP_strincmp( pPtr, DisplacementTexture2.c_str(), static_cast<unsigned int>(DisplacementTexture2.size()) ) ) {
         // Displacement texture
         out = &m_pModel->m_pCurrentMaterial->textureDisp;
         clampIndex = ObjFile::Material::TextureDispType;
-    } else if (!ASSIMP_strincmp( pPtr, SpecularityTexture.c_str(), static_cast<unsigned int>(SpecularityTexture.size()) ) ) {
+    } else if ( !ASSIMP_strincmp( pPtr, SpecularityTexture.c_str(), static_cast<unsigned int>(SpecularityTexture.size()) ) ) {
         // Specularity scaling (glossiness)
         out = & m_pModel->m_pCurrentMaterial->textureSpecularity;
         clampIndex = ObjFile::Material::TextureSpecularityType;

+ 9 - 12
code/ObjFileParser.cpp

@@ -359,8 +359,6 @@ void ObjFileParser::getHomogeneousVector3( std::vector<aiVector3D> &point3d_arra
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
 
-// -------------------------------------------------------------------
-//  Get values for two 3D vectors on the same line
 void ObjFileParser::getTwoVectors3( std::vector<aiVector3D> &point3d_array_a, std::vector<aiVector3D> &point3d_array_b ) {
     ai_real x, y, z;
     copyNextWord(m_buffer, Buffersize);
@@ -388,8 +386,6 @@ void ObjFileParser::getTwoVectors3( std::vector<aiVector3D> &point3d_array_a, st
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
 
-// -------------------------------------------------------------------
-//  Get values for a new 2D vector instance
 void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) {
     ai_real x, y;
     copyNextWord(m_buffer, Buffersize);
@@ -405,8 +401,6 @@ void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) {
 
 static const std::string DefaultObjName = "defaultobject";
 
-// -------------------------------------------------------------------
-//  Get values for a new face instance
 void ObjFileParser::getFace( aiPrimitiveType type ) {
     m_DataIt = getNextToken<DataArrayIt>( m_DataIt, m_DataItEnd );
     if ( m_DataIt == m_DataItEnd || *m_DataIt == '\0' ) {
@@ -522,8 +516,6 @@ void ObjFileParser::getFace( aiPrimitiveType type ) {
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
 
-// -------------------------------------------------------------------
-//  Get values for a new material description
 void ObjFileParser::getMaterialDesc() {
     // Get next data for material data
     m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
@@ -555,10 +547,15 @@ void ObjFileParser::getMaterialDesc() {
         // Search for material
         std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find(strName);
         if (it == m_pModel->m_MaterialMap.end()) {
-            // Not found, use default material
-            m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
-            DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", skipping");
-            strName = m_pModel->m_pDefaultMaterial->MaterialName.C_Str();
+			// Not found, so we don't know anything about the material except for its name.
+			// This may be the case if the material library is missing. We don't want to lose all
+			// materials if that happens, so create a new named material instead of discarding it
+			// completely.
+			DefaultLogger::get()->error("OBJ: failed to locate material " + strName + ", creating new material");
+			m_pModel->m_pCurrentMaterial = new ObjFile::Material();
+			m_pModel->m_pCurrentMaterial->MaterialName.Set(strName);
+			m_pModel->m_MaterialLib.push_back(strName);
+			m_pModel->m_MaterialMap[strName] = m_pModel->m_pCurrentMaterial;
         } else {
             // Found, using detected material
             m_pModel->m_pCurrentMaterial = (*it).second;

+ 45 - 12
code/OpenGEXImporter.cpp

@@ -261,6 +261,7 @@ OpenGEXImporter::OpenGEXImporter()
 , m_nodeChildMap()
 , m_meshCache()
 , m_mesh2refMap()
+, m_material2refMap()
 , m_ctx( nullptr )
 , m_metrics()
 , m_currentNode( nullptr )
@@ -306,6 +307,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce
 
     std::vector<char> buffer;
     TextFileToBuffer( file, buffer );
+    pIOHandler->Close( file );
 
     OpenDDLParser myParser;
     myParser.setBuffer( &buffer[ 0 ], buffer.size() );
@@ -320,6 +322,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce
     copyMeshes( pScene );
     copyCameras( pScene );
     copyLights( pScene );
+    copyMaterials( pScene );
     resolveReferences();
     createNodeTree( pScene );
 }
@@ -482,7 +485,10 @@ void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene *pScene ) {
                 || m_tokenType == Grammar::CameraNodeToken ) {
             m_currentNode->mName.Set( name.c_str() );
         } else if( m_tokenType == Grammar::MaterialToken ) {
-
+            aiString aiName;
+            aiName.Set( name );
+            m_currentMaterial->AddProperty( &aiName, AI_MATKEY_NAME );
+            m_material2refMap[ name ] = m_materialCache.size() - 1;
         }
     }
 }
@@ -520,7 +526,7 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) {
         m_currentNode->mNumMeshes = static_cast<unsigned int>(objRefNames.size());
         m_currentNode->mMeshes = new unsigned int[ objRefNames.size() ];
         if ( !objRefNames.empty() ) {
-            m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) );
+            m_unresolvedRefStack.push_back( std::unique_ptr<RefInfo>( new RefInfo( m_currentNode, RefInfo::MeshRef, objRefNames ) ) );
         }
     } else if ( m_tokenType == Grammar::LightNodeToken ) {
         // TODO!
@@ -539,7 +545,7 @@ void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene
     std::vector<std::string> matRefNames;
     getRefNames( node, matRefNames );
     if( !matRefNames.empty() ) {
-        m_unresolvedRefStack.push_back( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) );
+        m_unresolvedRefStack.push_back( std::unique_ptr<RefInfo>( new RefInfo( m_currentNode, RefInfo::MaterialRef, matRefNames ) ) );
     }
 }
 
@@ -1050,7 +1056,6 @@ void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene *pSc
                     m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) );
                 } else if( prop->m_value->getString() == Grammar::SpecularPowerTextureToken ) {
                     m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR( 0 ) );
-
                 } else if( prop->m_value->getString() == Grammar::EmissionTextureToken ) {
                     m_currentMaterial->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE( 0 ) );
                 } else if( prop->m_value->getString() == Grammar::OpacyTextureToken ) {
@@ -1154,6 +1159,19 @@ void OpenGEXImporter::copyLights( aiScene *pScene ) {
     std::copy( m_lightCache.begin(), m_lightCache.end(), pScene->mLights );
 }
 
+//------------------------------------------------------------------------------------------------
+void OpenGEXImporter::copyMaterials( aiScene *pScene ) {
+    ai_assert( nullptr != pScene );
+
+    if ( m_materialCache.empty() ) {
+        return;
+    }
+
+    pScene->mNumMaterials = static_cast<unsigned int>(m_materialCache.size());
+    pScene->mMaterials = new aiMaterial*[ pScene->mNumMaterials ];
+    std::copy( m_materialCache.begin(), m_materialCache.end(), pScene->mMaterials );
+}
+
 //------------------------------------------------------------------------------------------------
 void OpenGEXImporter::resolveReferences() {
     if( m_unresolvedRefStack.empty() ) {
@@ -1161,12 +1179,12 @@ void OpenGEXImporter::resolveReferences() {
     }
 
     RefInfo *currentRefInfo( nullptr );
-    for( std::vector<RefInfo*>::iterator it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) {
-        currentRefInfo = *it;
+    for( auto it = m_unresolvedRefStack.begin(); it != m_unresolvedRefStack.end(); ++it ) {
+        currentRefInfo = it->get();
         if( nullptr != currentRefInfo ) {
             aiNode *node( currentRefInfo->m_node );
             if( RefInfo::MeshRef == currentRefInfo->m_type ) {
-                for( size_t i = 0; i < currentRefInfo->m_Names.size(); i++ ) {
+                for( size_t i = 0; i < currentRefInfo->m_Names.size(); ++i ) {
                     const std::string &name( currentRefInfo->m_Names[ i ] );
                     ReferenceMap::const_iterator it( m_mesh2refMap.find( name ) );
                     if( m_mesh2refMap.end() != it ) {
@@ -1175,7 +1193,22 @@ void OpenGEXImporter::resolveReferences() {
                     }
                 }
             } else if( RefInfo::MaterialRef == currentRefInfo->m_type ) {
-                // ToDo!
+                for ( size_t i = 0; i < currentRefInfo->m_Names.size(); ++i ) {
+                    const std::string name( currentRefInfo->m_Names[ i ] );
+                    ReferenceMap::const_iterator it( m_material2refMap.find( name ) );
+                    if ( m_material2refMap.end() != it ) {
+                        if ( nullptr != m_currentMesh ) {
+                            unsigned int matIdx = static_cast< unsigned int >( m_material2refMap[ name ] );
+                            if ( m_currentMesh->mMaterialIndex != 0 ) {
+                                DefaultLogger::get()->warn( "Override of material reference in current mesh by material reference." );
+                            }
+                            m_currentMesh->mMaterialIndex = matIdx;
+                        }  else {
+                            DefaultLogger::get()->warn( "Cannot resolve material reference, because no current mesh is there." );
+
+                        }
+                    }
+                }
             } else {
                 throw DeadlyImportError( "Unknown reference info to resolve." );
             }
@@ -1213,9 +1246,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) {
         if( m_nodeChildMap.end() == it ) {
             info = new ChildInfo;
             m_root = info;
-            m_nodeChildMap[ node->mParent ] = info;
+            m_nodeChildMap[ node->mParent ] = std::unique_ptr<ChildInfo>(info);
         } else {
-            info = it->second;
+            info = it->second.get();
         }
         info->m_children.push_back( node );
     } else {
@@ -1225,9 +1258,9 @@ void OpenGEXImporter::pushNode( aiNode *node, aiScene *pScene ) {
         NodeChildMap::iterator it( m_nodeChildMap.find( node->mParent ) );
         if( m_nodeChildMap.end() == it ) {
             info = new ChildInfo;
-            m_nodeChildMap[ node->mParent ] = info;
+            m_nodeChildMap[ node->mParent ] = std::unique_ptr<ChildInfo>(info);
         } else {
-            info = it->second;
+            info = it->second.get();
         }
         info->m_children.push_back( node );
     }

+ 5 - 2
code/OpenGEXImporter.h

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 #include <list>
 #include <map>
+#include <memory>
 
 namespace ODDLParser {
     class DDLNode;
@@ -132,6 +133,7 @@ protected:
     void copyMeshes( aiScene *pScene );
     void copyCameras( aiScene *pScene );
     void copyLights( aiScene *pScene );
+    void copyMaterials( aiScene *pScene );
     void resolveReferences();
     void pushNode( aiNode *node, aiScene *pScene );
     aiNode *popNode();
@@ -179,12 +181,13 @@ private:
         std::list<aiNode*> m_children;
     };
     ChildInfo *m_root;
-    typedef std::map<aiNode*, ChildInfo*> NodeChildMap;
+    typedef std::map<aiNode*, std::unique_ptr<ChildInfo> > NodeChildMap;
     NodeChildMap m_nodeChildMap;
 
     std::vector<aiMesh*> m_meshCache;
     typedef std::map<std::string, size_t> ReferenceMap;
     std::map<std::string, size_t> m_mesh2refMap;
+    std::map<std::string, size_t> m_material2refMap;
 
     ODDLParser::Context *m_ctx;
     MetricInfo m_metrics[ MetricInfo::Max ];
@@ -199,7 +202,7 @@ private:
     std::vector<aiCamera*> m_cameraCache;
     std::vector<aiLight*> m_lightCache;
     std::vector<aiNode*> m_nodeStack;
-    std::vector<RefInfo*> m_unresolvedRefStack;
+    std::vector<std::unique_ptr<RefInfo> > m_unresolvedRefStack;
 };
 
 } // Namespace OpenGEX

+ 1 - 4
code/OptimizeMeshes.cpp

@@ -181,11 +181,8 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
                     verts += mScene->mMeshes[am]->mNumVertices;
                     faces += mScene->mMeshes[am]->mNumFaces;
 
+                    pNode->mMeshes[a] = pNode->mMeshes[pNode->mNumMeshes - 1];
                     --pNode->mNumMeshes;
-                    for( unsigned int n = a; n < pNode->mNumMeshes; ++n ) {
-                        pNode->mMeshes[ n ] = pNode->mMeshes[ n + 1 ];
-                    }
-
                     --a;
                 }
             }

+ 177 - 223
code/PlyLoader.cpp

@@ -170,9 +170,10 @@ void PLYImporter::InternReadFile(const std::string& pFile,
   std::vector<char> headerCheck;
   streamedBuffer.getNextLine(headerCheck);
 
-  if ((headerCheck.size() >= 3) && (headerCheck[0] != 'P' && headerCheck[0] != 'p') ||
-    (headerCheck[1] != 'L' && headerCheck[1] != 'l') ||
-    (headerCheck[2] != 'Y' && headerCheck[2] != 'y'))
+  if ((headerCheck.size() < 3) ||
+      (headerCheck[0] != 'P' && headerCheck[0] != 'p') ||
+      (headerCheck[1] != 'L' && headerCheck[1] != 'l') ||
+      (headerCheck[2] != 'Y' && headerCheck[2] != 'y') )
   {
     streamedBuffer.close();
     throw DeadlyImportError("Invalid .ply file: Magic number \'ply\' is no there");
@@ -299,252 +300,205 @@ void PLYImporter::InternReadFile(const std::string& pFile,
   }
 }
 
-void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementInstance* instElement, unsigned int pos)
-{
-  ai_assert(NULL != pcElement);
-  ai_assert(NULL != instElement);
+void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementInstance* instElement, unsigned int pos) {
+    ai_assert(NULL != pcElement);
+    ai_assert(NULL != instElement);
 
-  ai_uint aiPositions[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
-  PLY::EDataType aiTypes[3] = { EDT_Char, EDT_Char, EDT_Char };
+    ai_uint aiPositions[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+    PLY::EDataType aiTypes[3] = { EDT_Char, EDT_Char, EDT_Char };
 
-  ai_uint aiNormal[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
-  PLY::EDataType aiNormalTypes[3] = { EDT_Char, EDT_Char, EDT_Char };
+    ai_uint aiNormal[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+    PLY::EDataType aiNormalTypes[3] = { EDT_Char, EDT_Char, EDT_Char };
 
-  unsigned int aiColors[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
-  PLY::EDataType aiColorsTypes[4] = { EDT_Char, EDT_Char, EDT_Char, EDT_Char };
+    unsigned int aiColors[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+    PLY::EDataType aiColorsTypes[4] = { EDT_Char, EDT_Char, EDT_Char, EDT_Char };
 
-  unsigned int aiTexcoord[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
-  PLY::EDataType aiTexcoordTypes[2] = { EDT_Char, EDT_Char };
-
-  unsigned int cnt = 0;
-
-  // now check whether which normal components are available
-  unsigned int _a = 0;
-  for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
-    a != pcElement->alProperties.end(); ++a, ++_a)
-  {
-    if ((*a).bIsList)continue;
+    unsigned int aiTexcoord[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
+    PLY::EDataType aiTexcoordTypes[2] = { EDT_Char, EDT_Char };
 
-    // Positions
-    if (PLY::EST_XCoord == (*a).Semantic)
-    {
-      cnt++;
-      aiPositions[0] = _a;
-      aiTypes[0] = (*a).eType;
-    }
-    else if (PLY::EST_YCoord == (*a).Semantic)
-    {
-      cnt++;
-      aiPositions[1] = _a;
-      aiTypes[1] = (*a).eType;
-    }
-    else if (PLY::EST_ZCoord == (*a).Semantic)
-    {
-      cnt++;
-      aiPositions[2] = _a;
-      aiTypes[2] = (*a).eType;
-    }
+    // now check whether which normal components are available
+    unsigned int _a( 0 ), cnt( 0 );
+    for ( std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
+            a != pcElement->alProperties.end(); ++a, ++_a) {
+        if ((*a).bIsList) {
+            continue;
+        }
 
-    // Normals
-    else if (PLY::EST_XNormal == (*a).Semantic)
-    {
-      cnt++;
-      aiNormal[0] = _a;
-      aiNormalTypes[0] = (*a).eType;
-    }
-    else if (PLY::EST_YNormal == (*a).Semantic)
-    {
-      cnt++;
-      aiNormal[1] = _a;
-      aiNormalTypes[1] = (*a).eType;
-    }
-    else if (PLY::EST_ZNormal == (*a).Semantic)
-    {
-      cnt++;
-      aiNormal[2] = _a;
-      aiNormalTypes[2] = (*a).eType;
-    }
-    // Colors
-    else if (PLY::EST_Red == (*a).Semantic)
-    {
-      cnt++;
-      aiColors[0] = _a;
-      aiColorsTypes[0] = (*a).eType;
-    }
-    else if (PLY::EST_Green == (*a).Semantic)
-    {
-      cnt++;
-      aiColors[1] = _a;
-      aiColorsTypes[1] = (*a).eType;
-    }
-    else if (PLY::EST_Blue == (*a).Semantic)
-    {
-      cnt++;
-      aiColors[2] = _a;
-      aiColorsTypes[2] = (*a).eType;
-    }
-    else if (PLY::EST_Alpha == (*a).Semantic)
-    {
-      cnt++;
-      aiColors[3] = _a;
-      aiColorsTypes[3] = (*a).eType;
-    }
-    // Texture coordinates
-    else if (PLY::EST_UTextureCoord == (*a).Semantic)
-    {
-      cnt++;
-      aiTexcoord[0] = _a;
-      aiTexcoordTypes[0] = (*a).eType;
-    }
-    else if (PLY::EST_VTextureCoord == (*a).Semantic)
-    {
-      cnt++;
-      aiTexcoord[1] = _a;
-      aiTexcoordTypes[1] = (*a).eType;
+        // Positions
+        if (PLY::EST_XCoord == (*a).Semantic) {
+            ++cnt;
+            aiPositions[0] = _a;
+            aiTypes[0] = (*a).eType;
+        } else if (PLY::EST_YCoord == (*a).Semantic) {
+            ++cnt;
+            aiPositions[1] = _a;
+            aiTypes[1] = (*a).eType;
+        } else if (PLY::EST_ZCoord == (*a).Semantic) {
+            ++cnt;
+            aiPositions[2] = _a;
+            aiTypes[2] = (*a).eType;
+        } else if (PLY::EST_XNormal == (*a).Semantic) {
+            // Normals
+            ++cnt;
+            aiNormal[0] = _a;
+            aiNormalTypes[0] = (*a).eType;
+        } else if (PLY::EST_YNormal == (*a).Semantic) {
+            ++cnt;
+            aiNormal[1] = _a;
+            aiNormalTypes[1] = (*a).eType;
+        } else if (PLY::EST_ZNormal == (*a).Semantic) {
+            ++cnt;
+            aiNormal[2] = _a;
+            aiNormalTypes[2] = (*a).eType;
+        } else if (PLY::EST_Red == (*a).Semantic) {
+            // Colors
+            ++cnt;
+            aiColors[0] = _a;
+            aiColorsTypes[0] = (*a).eType;
+        } else if (PLY::EST_Green == (*a).Semantic) {
+            ++cnt;
+            aiColors[1] = _a;
+            aiColorsTypes[1] = (*a).eType;
+        } else if (PLY::EST_Blue == (*a).Semantic) {
+            ++cnt;
+            aiColors[2] = _a;
+            aiColorsTypes[2] = (*a).eType;
+        } else if (PLY::EST_Alpha == (*a).Semantic) {
+            ++cnt;
+            aiColors[3] = _a;
+            aiColorsTypes[3] = (*a).eType;
+        } else if (PLY::EST_UTextureCoord == (*a).Semantic) {
+            // Texture coordinates
+            ++cnt;
+            aiTexcoord[0] = _a;
+            aiTexcoordTypes[0] = (*a).eType;
+        } else if (PLY::EST_VTextureCoord == (*a).Semantic) {
+            ++cnt;
+            aiTexcoord[1] = _a;
+            aiTexcoordTypes[1] = (*a).eType;
+        }
     }
-  }
 
-  // check whether we have a valid source for the vertex data
-  if (0 != cnt)
-  {
-    // Position
-    aiVector3D vOut;
-    if (0xFFFFFFFF != aiPositions[0])
-    {
-      vOut.x = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiPositions[0]).avList.front(), aiTypes[0]);
-    }
+    // check whether we have a valid source for the vertex data
+    if (0 != cnt) {
+        // Position
+        aiVector3D vOut;
+        if (0xFFFFFFFF != aiPositions[0]) {
+            vOut.x = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiPositions[0]).avList.front(), aiTypes[0]);
+        }
 
-    if (0xFFFFFFFF != aiPositions[1])
-    {
-      vOut.y = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiPositions[1]).avList.front(), aiTypes[1]);
-    }
+        if (0xFFFFFFFF != aiPositions[1]) {
+            vOut.y = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiPositions[1]).avList.front(), aiTypes[1]);
+        }
 
-    if (0xFFFFFFFF != aiPositions[2])
-    {
-      vOut.z = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiPositions[2]).avList.front(), aiTypes[2]);
-    }
+        if (0xFFFFFFFF != aiPositions[2]) {
+            vOut.z = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiPositions[2]).avList.front(), aiTypes[2]);
+        }
 
-    // Normals
-    aiVector3D nOut;
-    bool haveNormal = false;
-    if (0xFFFFFFFF != aiNormal[0])
-    {
-      nOut.x = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiNormal[0]).avList.front(), aiNormalTypes[0]);
-      haveNormal = true;
-    }
+        // Normals
+        aiVector3D nOut;
+        bool haveNormal = false;
+        if (0xFFFFFFFF != aiNormal[0]) {
+            nOut.x = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiNormal[0]).avList.front(), aiNormalTypes[0]);
+            haveNormal = true;
+        }
 
-    if (0xFFFFFFFF != aiNormal[1])
-    {
-      nOut.y = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiNormal[1]).avList.front(), aiNormalTypes[1]);
-      haveNormal = true;
-    }
+        if (0xFFFFFFFF != aiNormal[1]) {
+            nOut.y = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiNormal[1]).avList.front(), aiNormalTypes[1]);
+            haveNormal = true;
+        }
 
-    if (0xFFFFFFFF != aiNormal[2])
-    {
-      nOut.z = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiNormal[2]).avList.front(), aiNormalTypes[2]);
-      haveNormal = true;
-    }
+        if (0xFFFFFFFF != aiNormal[2]) {
+            nOut.z = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiNormal[2]).avList.front(), aiNormalTypes[2]);
+            haveNormal = true;
+        }
 
-    //Colors
-    aiColor4D cOut;
-    bool haveColor = false;
-    if (0xFFFFFFFF != aiColors[0])
-    {
-      cOut.r = NormalizeColorValue(GetProperty(instElement->alProperties,
-        aiColors[0]).avList.front(), aiColorsTypes[0]);
-      haveColor = true;
-    }
+        //Colors
+        aiColor4D cOut;
+        bool haveColor = false;
+        if (0xFFFFFFFF != aiColors[0]) {
+            cOut.r = NormalizeColorValue(GetProperty(instElement->alProperties,
+                aiColors[0]).avList.front(), aiColorsTypes[0]);
+            haveColor = true;
+        }
 
-    if (0xFFFFFFFF != aiColors[1])
-    {
-      cOut.g = NormalizeColorValue(GetProperty(instElement->alProperties,
-        aiColors[1]).avList.front(), aiColorsTypes[1]);
-      haveColor = true;
-    }
+        if (0xFFFFFFFF != aiColors[1]) {
+            cOut.g = NormalizeColorValue(GetProperty(instElement->alProperties,
+                aiColors[1]).avList.front(), aiColorsTypes[1]);
+            haveColor = true;
+        }
 
-    if (0xFFFFFFFF != aiColors[2])
-    {
-      cOut.b = NormalizeColorValue(GetProperty(instElement->alProperties,
-        aiColors[2]).avList.front(), aiColorsTypes[2]);
-      haveColor = true;
-    }
+        if (0xFFFFFFFF != aiColors[2]) {
+            cOut.b = NormalizeColorValue(GetProperty(instElement->alProperties,
+                aiColors[2]).avList.front(), aiColorsTypes[2]);
+            haveColor = true;
+        }
 
-    // assume 1.0 for the alpha channel ifit is not set
-    if (0xFFFFFFFF == aiColors[3])
-    {
-      cOut.a = 1.0;
-    }
-    else
-    {
-      cOut.a = NormalizeColorValue(GetProperty(instElement->alProperties,
-        aiColors[3]).avList.front(), aiColorsTypes[3]);
+        // assume 1.0 for the alpha channel ifit is not set
+        if (0xFFFFFFFF == aiColors[3]) {
+            cOut.a = 1.0;
+        } else {
+            cOut.a = NormalizeColorValue(GetProperty(instElement->alProperties,
+                aiColors[3]).avList.front(), aiColorsTypes[3]);
 
-      haveColor = true;
-    }
+            haveColor = true;
+        }
 
-    //Texture coordinates
-    aiVector3D tOut;
-    tOut.z = 0;
-    bool haveTextureCoords = false;
-    if (0xFFFFFFFF != aiTexcoord[0])
-    {
-      tOut.x = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiTexcoord[0]).avList.front(), aiTexcoordTypes[0]);
-      haveTextureCoords = true;
-    }
+        //Texture coordinates
+        aiVector3D tOut;
+        tOut.z = 0;
+        bool haveTextureCoords = false;
+        if (0xFFFFFFFF != aiTexcoord[0]) {
+            tOut.x = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiTexcoord[0]).avList.front(), aiTexcoordTypes[0]);
+            haveTextureCoords = true;
+        }
 
-    if (0xFFFFFFFF != aiTexcoord[1])
-    {
-      tOut.y = PLY::PropertyInstance::ConvertTo<ai_real>(
-        GetProperty(instElement->alProperties, aiTexcoord[1]).avList.front(), aiTexcoordTypes[1]);
-      haveTextureCoords = true;
-    }
+        if (0xFFFFFFFF != aiTexcoord[1]) {
+            tOut.y = PLY::PropertyInstance::ConvertTo<ai_real>(
+                GetProperty(instElement->alProperties, aiTexcoord[1]).avList.front(), aiTexcoordTypes[1]);
+            haveTextureCoords = true;
+        }
 
-    //create aiMesh if needed
-    if (mGeneratedMesh == NULL)
-    {
-      mGeneratedMesh = new aiMesh();
-      mGeneratedMesh->mMaterialIndex = 0;
-    }
+        //create aiMesh if needed
+        if ( nullptr == mGeneratedMesh ) {
+            mGeneratedMesh = new aiMesh();
+            mGeneratedMesh->mMaterialIndex = 0;
+        }
 
-    if (mGeneratedMesh->mVertices == NULL)
-    {
-      mGeneratedMesh->mNumVertices = pcElement->NumOccur;
-      mGeneratedMesh->mVertices = new aiVector3D[mGeneratedMesh->mNumVertices];
-    }
+        if (nullptr == mGeneratedMesh->mVertices) {
+            mGeneratedMesh->mNumVertices = pcElement->NumOccur;
+            mGeneratedMesh->mVertices = new aiVector3D[mGeneratedMesh->mNumVertices];
+        }
 
-    mGeneratedMesh->mVertices[pos] = vOut;
+        mGeneratedMesh->mVertices[pos] = vOut;
 
-    if (haveNormal)
-    {
-      if (mGeneratedMesh->mNormals == NULL)
-        mGeneratedMesh->mNormals = new aiVector3D[mGeneratedMesh->mNumVertices];
-      mGeneratedMesh->mNormals[pos] = nOut;
-    }
+        if (haveNormal) {
+            if (nullptr == mGeneratedMesh->mNormals)
+                mGeneratedMesh->mNormals = new aiVector3D[mGeneratedMesh->mNumVertices];
+            mGeneratedMesh->mNormals[pos] = nOut;
+        }
 
-    if (haveColor)
-    {
-      if (mGeneratedMesh->mColors[0] == NULL)
-        mGeneratedMesh->mColors[0] = new aiColor4D[mGeneratedMesh->mNumVertices];
-      mGeneratedMesh->mColors[0][pos] = cOut;
-    }
+        if (haveColor) {
+            if (nullptr == mGeneratedMesh->mColors[0])
+                mGeneratedMesh->mColors[0] = new aiColor4D[mGeneratedMesh->mNumVertices];
+            mGeneratedMesh->mColors[0][pos] = cOut;
+        }
 
-    if (haveTextureCoords)
-    {
-      if (mGeneratedMesh->mTextureCoords[0] == NULL)
-      {
-        mGeneratedMesh->mNumUVComponents[0] = 2;
-        mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices];
-      }
-      mGeneratedMesh->mTextureCoords[0][pos] = tOut;
+        if (haveTextureCoords) {
+            if (nullptr == mGeneratedMesh->mTextureCoords[0]) {
+                mGeneratedMesh->mNumUVComponents[0] = 2;
+                mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices];
+            }
+            mGeneratedMesh->mTextureCoords[0][pos] = tOut;
+        }
     }
-  }
 }
 
 
@@ -585,7 +539,7 @@ void PLYImporter::LoadFace(const PLY::Element* pcElement, const PLY::ElementInst
   ai_assert(NULL != instElement);
 
   if (mGeneratedMesh == NULL)
-    throw DeadlyImportError("Invalid .ply file: Vertices shoud be declared before faces");
+    throw DeadlyImportError("Invalid .ply file: Vertices should be declared before faces");
 
   bool bOne = false;
 

+ 2 - 2
code/PlyParser.cpp

@@ -618,7 +618,7 @@ bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, DOM* p_pc
   }
 
   streamBuffer.getNextBlock(buffer);
-  unsigned int bufferSize = buffer.size();
+  unsigned int bufferSize = static_cast<unsigned int>(buffer.size());
   const char* pCur = (char*)&buffer[0];
   if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE))
   {
@@ -1025,7 +1025,7 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
       buffer = std::vector<char>(buffer.end() - bufferSize, buffer.end());
       buffer.insert(buffer.end(), nbuffer.begin(), nbuffer.end());
       nbuffer.clear();
-      bufferSize = buffer.size();
+      bufferSize = static_cast<unsigned int>(buffer.size());
       pCur = (char*)&buffer[0];
     }
     else

+ 10 - 4
code/PretransformVertices.cpp

@@ -104,7 +104,7 @@ unsigned int PretransformVertices::CountNodes( aiNode* pcNode )
 
 // ------------------------------------------------------------------------------------------------
 // Get a bitwise combination identifying the vertex format of a mesh
-unsigned int PretransformVertices::GetMeshVFormat(aiMesh* pcMesh)
+unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh )
 {
     // the vertex format is stored in aiMesh::mBones for later retrieval.
     // there isn't a good reason to compute it a few hundred times
@@ -160,6 +160,11 @@ void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsign
             unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
             ai_assert(0 != num_ref);
             --num_ref;
+            // Save the name of the last mesh
+            if (num_ref==0)
+            {
+                pcMeshOut->mName = pcMesh->mName;
+            }
 
             if (identity)   {
                 // copy positions without modifying them
@@ -626,9 +631,10 @@ void PretransformVertices::Execute( aiScene* pScene)
 
         // now delete all nodes in the scene and build a new
         // flat node graph with a root node and some level 1 children
+        aiNode* newRoot = new aiNode();
+        newRoot->mName = pScene->mRootNode->mName;
         delete pScene->mRootNode;
-        pScene->mRootNode = new aiNode();
-        pScene->mRootNode->mName.Set("<dummy_root>");
+        pScene->mRootNode = newRoot;
 
         if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
         {
@@ -646,7 +652,7 @@ void PretransformVertices::Execute( aiScene* pScene)
             {
                 aiNode* pcNode = *nodes = new aiNode();
                 pcNode->mParent = pScene->mRootNode;
-                pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"mesh_%u",i);
+                pcNode->mName = pScene->mMeshes[i]->mName;
 
                 // setup mesh indices
                 pcNode->mNumMeshes = 1;

+ 9 - 11
code/Profiler.h

@@ -52,21 +52,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <map>
 
 namespace Assimp {
-    namespace Profiling {
-
-        using namespace Formatter;
+namespace Profiling {
 
+using namespace Formatter;
 
 // ------------------------------------------------------------------------------------------------
 /** Simple wrapper around boost::timer to simplify reporting. Timings are automatically
  *  dumped to the log file.
  */
-class Profiler
-{
-
+class Profiler {
 public:
-
-    Profiler() {}
+    Profiler() {
+        // empty
+    }
 
 public:
 
@@ -84,17 +82,17 @@ public:
             return;
         }
 
-        auto elapsedSeconds = std::chrono::system_clock::now() - regions[region];
+        std::chrono::duration<double> elapsedSeconds = std::chrono::system_clock::now() - regions[region];
         DefaultLogger::get()->debug((format("END   `"),region,"`, dt= ", elapsedSeconds.count()," s"));
     }
 
 private:
-
     typedef std::map<std::string,std::chrono::time_point<std::chrono::system_clock>> RegionMap;
     RegionMap regions;
 };
 
-    }
+}
 }
 
 #endif
+

+ 0 - 5
code/Q3BSPZipArchive.cpp

@@ -39,14 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-
-
 #ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
 
 #include "Q3BSPZipArchive.h"
-#include <algorithm>
 #include <cassert>
-#include <cstdlib>
 #include <assimp/ai_assert.h>
 
 namespace Assimp {
@@ -137,7 +133,6 @@ zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
     return mapping;
 }
 
-// ------------------------------------------------------------------------------------------------
 ZipFile::ZipFile(size_t size) : m_Size(size) {
     ai_assert(m_Size != 0);
 

+ 0 - 1
code/Q3BSPZipArchive.h

@@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <contrib/unzip/unzip.h>
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
-#include <string>
 #include <vector>
 #include <map>
 #include <cassert>

+ 24 - 31
code/SIBImporter.cpp

@@ -176,36 +176,29 @@ static void UnknownChunk(StreamReaderLE* stream, const SIBChunk& chunk)
 }
 
 // Reads a UTF-16LE string and returns it at UTF-8.
-static aiString ReadString(StreamReaderLE* stream, uint32_t numWChars)
-{
-    if ( 0 == numWChars ) {
+static aiString ReadString(StreamReaderLE *stream, uint32_t numWChars) {
+    if ( nullptr == stream || 0 == numWChars ) {
         static const aiString empty;
         return empty;
     }
+
     // Allocate buffers (max expansion is 1 byte -> 4 bytes for UTF-8)
-    //UTF16* temp = new UTF16[numWChars];
     std::vector<unsigned char> str;
-    str.reserve(numWChars * 4 + 1);
-    //unsigned char* str = new unsigned char[numWChars * 4 + 1];
-    uint16_t *temp = new uint16_t[numWChars];
-    for (uint32_t n=0;n<numWChars;n++)
-        temp[n] = stream->GetU2();
+    str.reserve( numWChars * 4 + 1 );
+    uint16_t *temp = new uint16_t[ numWChars ];
+    for ( uint32_t n = 0; n < numWChars; ++n ) {
+        temp[ n ] = stream->GetU2();
+    }
 
     // Convert it and NUL-terminate.
-    //const UTF16 *start = temp, *end = temp + numWChars;
+    const uint16_t *start( temp ), *end( temp + numWChars );
+    utf8::utf16to8( start, end, back_inserter( str ) );
+    str[ str.size() - 1 ] = '\0';
 
-    const uint16_t *start = temp, *end = temp + numWChars;
-    utf8::utf16to8(start, end, back_inserter(str));
-
-    //UTF8 *dest = str, *limit = str + numWChars*4;
-    //ConvertUTF16toUTF8(&start, end, &dest, limit, lenientConversion);
-    //*dest = '\0';
-
-    str[str.size()-1] = '\0';
     // Return the final string.
     aiString result = aiString((const char *)&str[0]);
-    //delete[] str;
     delete[] temp;
+
     return result;
 }
 
@@ -223,26 +216,26 @@ SIBImporter::~SIBImporter() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
-{
+bool SIBImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const {
     return SimpleExtensionCheck(pFile, "sib");
 }
 
 // ------------------------------------------------------------------------------------------------
-const aiImporterDesc* SIBImporter::GetInfo () const
-{
+const aiImporterDesc* SIBImporter::GetInfo () const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
-static void ReadVerts(SIBMesh* mesh, StreamReaderLE* stream, uint32_t count)
-{
-    mesh->pos.resize(count);
+static void ReadVerts(SIBMesh* mesh, StreamReaderLE* stream, uint32_t count) {
+    if ( nullptr == mesh || nullptr == stream ) {
+        return;
+    }
 
-    for (uint32_t n=0;n<count;n++) {
-        mesh->pos[n].x = stream->GetF4();
-        mesh->pos[n].y = stream->GetF4();
-        mesh->pos[n].z = stream->GetF4();
+    mesh->pos.resize(count);
+    for ( uint32_t n=0; n<count; ++n ) {
+        mesh->pos[ n ].x = stream->GetF4();
+        mesh->pos[ n ].y = stream->GetF4();
+        mesh->pos[ n ].z = stream->GetF4();
     }
 }
 
@@ -827,7 +820,7 @@ static void ReadInstance(SIB* sib, StreamReaderLE* stream)
 static void CheckVersion(StreamReaderLE* stream)
 {
     uint32_t version = stream->GetU4();
-    if ( version != 1 ) {
+    if ( version < 1 || version > 2 ) {
         throw DeadlyImportError( "SIB: Unsupported file version." );
     }
 }

+ 7 - 3
code/SMDLoader.cpp

@@ -450,7 +450,9 @@ void SMDImporter::CreateOutputMeshes()
 // add bone child nodes
 void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent)
 {
-    ai_assert(NULL != pcNode && 0 == pcNode->mNumChildren && NULL == pcNode->mChildren);
+    ai_assert( NULL != pcNode );
+    ai_assert( 0 == pcNode->mNumChildren );
+    ai_assert( NULL == pcNode->mChildren);
 
     // first count ...
     for (unsigned int i = 0; i < asBones.size();++i)
@@ -648,12 +650,14 @@ void SMDImporter::ComputeAbsoluteBoneTransformations()
 // create output materials
 void SMDImporter::CreateOutputMaterials()
 {
+    ai_assert( nullptr != pScene );
+
     pScene->mNumMaterials = (unsigned int)aszTextures.size();
     pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)];
 
-    for (unsigned int iMat = 0; iMat < pScene->mNumMaterials;++iMat)
-    {
+    for (unsigned int iMat = 0; iMat < pScene->mNumMaterials; ++iMat) {
         aiMaterial* pcMat = new aiMaterial();
+        ai_assert( nullptr != pcMat );
         pScene->mMaterials[iMat] = pcMat;
 
         aiString szName;

+ 12 - 4
code/SceneCombiner.cpp

@@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "time.h"
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/scene.h>
+#include <assimp/mesh.h>
 #include <stdio.h>
 #include "ScenePrivate.h"
 
@@ -757,7 +758,7 @@ void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator
 
 // ------------------------------------------------------------------------------------------------
 // Merge a list of meshes
-void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/,
+void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/,
     std::vector<aiMesh*>::const_iterator begin,
     std::vector<aiMesh*>::const_iterator end)
 {
@@ -772,8 +773,14 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/,
     aiMesh* out = *_out = new aiMesh();
     out->mMaterialIndex = (*begin)->mMaterialIndex;
 
+    std::string name;
     // Find out how much output storage we'll need
-    for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)   {
+    for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it) {
+        const char *meshName( (*it)->mName.C_Str() );
+        name += std::string( meshName );
+        if ( it != end - 1 ) {
+            name += ".";
+        }
         out->mNumVertices   += (*it)->mNumVertices;
         out->mNumFaces      += (*it)->mNumFaces;
         out->mNumBones      += (*it)->mNumBones;
@@ -781,6 +788,7 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/,
         // combine primitive type flags
         out->mPrimitiveTypes |= (*it)->mPrimitiveTypes;
     }
+    out->mName.Set( name.c_str() );
 
     if (out->mNumVertices) {
         aiVector3D* pv2;
@@ -789,7 +797,7 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/,
         if ((**begin).HasPositions())   {
 
             pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
-            for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)   {
+            for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it)  {
                 if ((*it)->mVertices)   {
                     ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
                 }
@@ -809,7 +817,7 @@ void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/,
                 pv2 += (*it)->mNumVertices;
             }
         }
-        // copy tangents and bitangents
+        // copy tangents and bi-tangents
         if ((**begin).HasTangentsAndBitangents())   {
 
             pv2 = out->mTangents = new aiVector3D[out->mNumVertices];

+ 2 - 6
code/StreamReader.h

@@ -291,15 +291,11 @@ private:
             throw DeadlyImportError("End of file or stream limit was reached");
         }
 
-///*#ifdef __arm__
         T f;
         ::memcpy (&f, current, sizeof(T));
-//#else*/
-//        T f = *((const T*)current);
-//#endif
-        Intern :: Getter<SwapEndianess,T,RuntimeSwitch>() (&f,le);
-
+        Intern::Getter<SwapEndianess,T,RuntimeSwitch>() (&f,le);
         current += sizeof(T);
+
         return f;
     }
 

+ 2 - 2
code/UnrealLoader.cpp

@@ -157,7 +157,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile,
     DefaultLogger::get()->debug("UNREAL: uc file is "   + uc_path);
 
     // and open the files ... we can't live without them
-    IOStream* p = pIOHandler->Open(d_path);
+    std::unique_ptr<IOStream> p(pIOHandler->Open(d_path));
     if (!p)
         throw DeadlyImportError("UNREAL: Unable to open _d file");
     StreamReaderLE d_reader(pIOHandler->Open(d_path));
@@ -203,7 +203,7 @@ void UnrealImporter::InternReadFile( const std::string& pFile,
         d_reader.IncPtr(1);
     }
 
-    p = pIOHandler->Open(a_path);
+    p.reset(pIOHandler->Open(a_path));
     if (!p)
         throw DeadlyImportError("UNREAL: Unable to open _a file");
     StreamReaderLE a_reader(pIOHandler->Open(a_path));

+ 3 - 5
code/ValidateDataStructure.cpp

@@ -89,9 +89,7 @@ AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...)
     ai_assert(iLen > 0);
 
     va_end(args);
-#ifdef ASSIMP_BUILD_DEBUG
-    ai_assert( false );
-#endif
+
     throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen));
 }
 // ------------------------------------------------------------------------------------------------
@@ -409,12 +407,12 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
             // the MSB flag is temporarily used by the extra verbose
             // mode to tell us that the JoinVerticesProcess might have
             // been executed already.
-			if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) &&
+			/*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) &&
 				abRefList[face.mIndices[a]])
             {
                 ReportError("aiMesh::mVertices[%i] is referenced twice - second "
                     "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);
-            }
+            }*/
             abRefList[face.mIndices[a]] = true;
         }
     }

+ 3 - 4
code/Version.cpp

@@ -107,8 +107,7 @@ ASSIMP_API unsigned int aiGetCompileFlags ()    {
 #include "revision.h"
 
 // ------------------------------------------------------------------------------------------------
-ASSIMP_API unsigned int aiGetVersionRevision ()
-{
+ASSIMP_API unsigned int aiGetVersionRevision() {
     return GitVersion;
 }
 
@@ -133,8 +132,7 @@ ASSIMP_API aiScene::aiScene()
 }
 
 // ------------------------------------------------------------------------------------------------
-ASSIMP_API aiScene::~aiScene()
-{
+ASSIMP_API aiScene::~aiScene() {
     // delete all sub-objects recursively
     delete mRootNode;
 
@@ -173,3 +171,4 @@ ASSIMP_API aiScene::~aiScene()
 
     delete static_cast<Assimp::ScenePrivateData*>( mPrivate );
 }
+

+ 3 - 3
code/X3DImporter.cpp

@@ -95,8 +95,8 @@ struct WordIterator: public std::iterator<std::input_iterator_tag, const char*>
         end_ = other.end_;
         return *this;
     }
-    bool operator==(WordIterator &other) const { return start_ == other.start_; }
-    bool operator!=(WordIterator &other) const { return start_ != other.start_; }
+    bool operator==(const WordIterator &other) const { return start_ == other.start_; }
+    bool operator!=(const WordIterator &other) const { return start_ != other.start_; }
     WordIterator &operator++() {
         start_ += strcspn(start_, whitespace);
         start_ += strspn(start_, whitespace);
@@ -558,7 +558,7 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector
 
         WordIterator wordItBegin(val, val + strlen(val));
         WordIterator wordItEnd;
-        std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return atof(match); });
+        std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return static_cast<float>(atof(match)); });
     }
 }
 

+ 4 - 1
code/XFileExporter.h

@@ -93,7 +93,10 @@ protected:
     void PushTag() { startstr.append( "  "); }
 
     /// Leaves an element, decreasing the indentation
-    void PopTag() { ai_assert( startstr.length() > 1); startstr.erase( startstr.length() - 2); }
+    void PopTag() { 
+        ai_assert( startstr.length() > 1); 
+        startstr.erase( startstr.length() - 2); 
+    }
 
 public:
     /// Stringstream to write all output into

+ 5 - 2
code/XGLLoader.cpp

@@ -83,8 +83,11 @@ struct free_it
 };
 
 namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp
-template<> const std::string LogFunctions<XGLImporter>::log_prefix = "XGL: ";
-
+    template<> const char* LogFunctions<XGLImporter>::Prefix()
+    {
+        static auto prefix = "XGL: ";
+        return prefix;
+    }
 }
 
 static const aiImporterDesc desc = {

+ 1108 - 0
code/glTF2Asset.h

@@ -0,0 +1,1108 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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 glTFAsset.h
+ * Declares a glTF class to handle gltf/glb files
+ *
+ * glTF Extensions Support:
+ *   KHR_materials_pbrSpecularGlossiness full
+ */
+#ifndef GLTF2ASSET_H_INC
+#define GLTF2ASSET_H_INC
+
+#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#include <map>
+#include <string>
+#include <list>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+
+#define RAPIDJSON_HAS_STDSTRING 1
+#include <rapidjson/rapidjson.h>
+#include <rapidjson/document.h>
+#include <rapidjson/error/en.h>
+
+#ifdef ASSIMP_API
+#   include <memory>
+#   include <assimp/DefaultIOSystem.h>
+#   include "ByteSwapper.h"
+#else
+#   include <memory>
+#   define AI_SWAP4(p)
+#   define ai_assert
+#endif
+
+
+#if _MSC_VER > 1500 || (defined __GNUC___)
+#       define ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
+#   else
+#       define gltf_unordered_map map
+#endif
+
+#ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
+#   include <unordered_map>
+#   if _MSC_VER > 1600
+#       define gltf_unordered_map unordered_map
+#   else
+#       define gltf_unordered_map tr1::unordered_map
+#   endif
+#endif
+
+namespace glTF2
+{
+#ifdef ASSIMP_API
+    using Assimp::IOStream;
+    using Assimp::IOSystem;
+    using std::shared_ptr;
+#else
+    using std::shared_ptr;
+
+    typedef std::runtime_error DeadlyImportError;
+    typedef std::runtime_error DeadlyExportError;
+
+    enum aiOrigin { aiOrigin_SET = 0, aiOrigin_CUR = 1, aiOrigin_END = 2 };
+    class IOSystem;
+    class IOStream
+    {
+        FILE* f;
+    public:
+        IOStream(FILE* file) : f(file) {}
+        ~IOStream() { fclose(f); f = 0; }
+
+        size_t Read(void* b, size_t sz, size_t n) { return fread(b, sz, n, f); }
+        size_t Write(const void* b, size_t sz, size_t n) { return fwrite(b, sz, n, f); }
+        int    Seek(size_t off, aiOrigin orig) { return fseek(f, off, int(orig)); }
+        size_t Tell() const { return ftell(f); }
+
+        size_t FileSize() {
+            long p = Tell(), len = (Seek(0, aiOrigin_END), Tell());
+            return size_t((Seek(p, aiOrigin_SET), len));
+        }
+    };
+#endif
+
+    using rapidjson::Value;
+    using rapidjson::Document;
+
+    class Asset;
+    class AssetWriter;
+
+    struct BufferView; // here due to cross-reference
+    struct Texture;
+    struct Skin;
+
+    // Vec/matrix types, as raw float arrays
+    typedef float (vec3)[3];
+    typedef float (vec4)[4];
+	typedef float (mat4)[16];
+
+    namespace Util
+    {
+        void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out);
+
+        size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
+
+        inline size_t DecodeBase64(const char* in, uint8_t*& out)
+        {
+            return DecodeBase64(in, strlen(in), out);
+        }
+
+        struct DataURI
+        {
+            const char* mediaType;
+            const char* charset;
+            bool base64;
+            const char* data;
+            size_t dataLength;
+        };
+
+        //! Check if a uri is a data URI
+        inline bool ParseDataURI(const char* uri, size_t uriLen, DataURI& out);
+    }
+
+
+    //! Magic number for GLB files
+	#define AI_GLB_MAGIC_NUMBER "glTF"
+
+	#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0
+	#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0
+	#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0
+	#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0
+	#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0
+	#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0
+	#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR "$clr.diffuse", 0, 1
+	#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR "$clr.specular", 0, 1
+	#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0
+	#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE aiTextureType_DIFFUSE, 1
+	#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE aiTextureType_UNKNOWN, 1
+
+	#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord"
+	#define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname"
+	#define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid"
+	#define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag"
+	#define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin"
+
+	#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N
+	#define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N
+	#define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N
+	#define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N
+	#define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N
+
+    #ifdef ASSIMP_API
+        #include "./../include/assimp/Compiler/pushpack1.h"
+    #endif
+
+    #ifdef ASSIMP_API
+        #include "./../include/assimp/Compiler/poppack1.h"
+    #endif
+
+
+    //! Values for the GLB_Header::sceneFormat field
+    enum SceneFormat
+    {
+        SceneFormat_JSON = 0
+    };
+
+    //! Values for the mesh primitive modes
+    enum PrimitiveMode
+    {
+        PrimitiveMode_POINTS = 0,
+        PrimitiveMode_LINES = 1,
+        PrimitiveMode_LINE_LOOP = 2,
+        PrimitiveMode_LINE_STRIP = 3,
+        PrimitiveMode_TRIANGLES = 4,
+        PrimitiveMode_TRIANGLE_STRIP = 5,
+        PrimitiveMode_TRIANGLE_FAN = 6
+    };
+
+    //! Values for the Accessor::componentType field
+    enum ComponentType
+    {
+        ComponentType_BYTE = 5120,
+        ComponentType_UNSIGNED_BYTE = 5121,
+        ComponentType_SHORT = 5122,
+        ComponentType_UNSIGNED_SHORT = 5123,
+        ComponentType_UNSIGNED_INT = 5125,
+        ComponentType_FLOAT = 5126
+    };
+
+    inline unsigned int ComponentTypeSize(ComponentType t)
+    {
+        switch (t) {
+            case ComponentType_SHORT:
+            case ComponentType_UNSIGNED_SHORT:
+                return 2;
+
+            case ComponentType_UNSIGNED_INT:
+            case ComponentType_FLOAT:
+                return 4;
+
+            case ComponentType_BYTE:
+            case ComponentType_UNSIGNED_BYTE:
+                return 1;
+            default:
+                throw DeadlyImportError("GLTF: Unsupported Component Type " + std::to_string(t));
+        }
+    }
+
+    //! Values for the BufferView::target field
+    enum BufferViewTarget
+    {
+        BufferViewTarget_ARRAY_BUFFER = 34962,
+        BufferViewTarget_ELEMENT_ARRAY_BUFFER = 34963
+    };
+
+    //! Values for the Sampler::magFilter field
+    enum class SamplerMagFilter: unsigned int
+    {
+        UNSET = 0,
+        SamplerMagFilter_Nearest = 9728,
+        SamplerMagFilter_Linear = 9729
+    };
+
+    //! Values for the Sampler::minFilter field
+    enum class SamplerMinFilter: unsigned int
+    {
+        UNSET = 0,
+        SamplerMinFilter_Nearest = 9728,
+        SamplerMinFilter_Linear = 9729,
+        SamplerMinFilter_Nearest_Mipmap_Nearest = 9984,
+        SamplerMinFilter_Linear_Mipmap_Nearest = 9985,
+        SamplerMinFilter_Nearest_Mipmap_Linear = 9986,
+        SamplerMinFilter_Linear_Mipmap_Linear = 9987
+    };
+
+    //! Values for the Sampler::wrapS and Sampler::wrapT field
+    enum class SamplerWrap: unsigned int
+    {
+        UNSET = 0,
+        Clamp_To_Edge = 33071,
+        Mirrored_Repeat = 33648,
+        Repeat = 10497
+    };
+
+    //! Values for the Texture::format and Texture::internalFormat fields
+    enum TextureFormat
+    {
+        TextureFormat_ALPHA = 6406,
+        TextureFormat_RGB = 6407,
+        TextureFormat_RGBA = 6408,
+        TextureFormat_LUMINANCE = 6409,
+        TextureFormat_LUMINANCE_ALPHA = 6410
+    };
+
+    //! Values for the Texture::target field
+    enum TextureTarget
+    {
+        TextureTarget_TEXTURE_2D = 3553
+    };
+
+    //! Values for the Texture::type field
+    enum TextureType
+    {
+        TextureType_UNSIGNED_BYTE = 5121,
+        TextureType_UNSIGNED_SHORT_5_6_5 = 33635,
+        TextureType_UNSIGNED_SHORT_4_4_4_4 = 32819,
+        TextureType_UNSIGNED_SHORT_5_5_5_1 = 32820
+    };
+
+
+    //! Values for the Accessor::type field (helper class)
+    class AttribType
+    {
+    public:
+        enum Value
+            { SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4 };
+
+    private:
+        static const size_t NUM_VALUES = static_cast<size_t>(MAT4)+1;
+
+        struct Info
+            { const char* name; unsigned int numComponents; };
+
+        template<int N> struct data
+            { static const Info infos[NUM_VALUES]; };
+
+    public:
+        inline static Value FromString(const char* str)
+        {
+            for (size_t i = 0; i < NUM_VALUES; ++i) {
+                if (strcmp(data<0>::infos[i].name, str) == 0) {
+                    return static_cast<Value>(i);
+                }
+            }
+            return SCALAR;
+        }
+
+        inline static const char* ToString(Value type)
+        {
+            return data<0>::infos[static_cast<size_t>(type)].name;
+        }
+
+        inline static unsigned int GetNumComponents(Value type)
+        {
+            return data<0>::infos[static_cast<size_t>(type)].numComponents;
+        }
+    };
+
+    // must match the order of the AttribTypeTraits::Value enum!
+    template<int N> const AttribType::Info
+    AttribType::data<N>::infos[AttribType::NUM_VALUES] = {
+        { "SCALAR", 1 }, { "VEC2", 2 }, { "VEC3", 3 }, { "VEC4", 4 }, { "MAT2", 4 }, { "MAT3", 9 }, { "MAT4", 16 }
+    };
+
+
+
+    //! A reference to one top-level object, which is valid
+    //! until the Asset instance is destroyed
+    template<class T>
+    class Ref
+    {
+        std::vector<T*>* vector;
+        unsigned int index;
+
+    public:
+        Ref() : vector(0), index(0) {}
+        Ref(std::vector<T*>& vec, unsigned int idx) : vector(&vec), index(idx) {}
+
+        inline unsigned int GetIndex() const
+            { return index; }
+
+        operator bool() const
+            { return vector != 0; }
+
+        T* operator->()
+            { return (*vector)[index]; }
+
+        T& operator*()
+            { return *((*vector)[index]); }
+    };
+
+    //! Helper struct to represent values that might not be present
+    template<class T>
+    struct Nullable
+    {
+        T value;
+        bool isPresent;
+
+        Nullable() : isPresent(false) {}
+        Nullable(T& val) : value(val), isPresent(true) {}
+    };
+
+
+    //! Base classe for all glTF top-level objects
+    struct Object
+    {
+        int index;        //!< The index of this object within its property container
+        int oIndex;       //!< The original index of this object defined in the JSON
+        std::string id;   //!< The globally unique ID used to reference this object
+        std::string name; //!< The user-defined name of this object
+
+        //! Objects marked as special are not exported (used to emulate the binary body buffer)
+        virtual bool IsSpecial() const
+            { return false; }
+
+        virtual ~Object() {}
+
+        //! Maps special IDs to another ID, where needed. Subclasses may override it (statically)
+        static const char* TranslateId(Asset& r, const char* id)
+            { return id; }
+    };
+
+    //
+    // Classes for each glTF top-level object type
+    //
+
+    //! A typed view into a BufferView. A BufferView contains raw binary data.
+    //! An accessor provides a typed view into a BufferView or a subset of a BufferView
+    //! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer.
+    struct Accessor : public Object
+    {
+        Ref<BufferView> bufferView;  //!< The ID of the bufferView. (required)
+        unsigned int byteOffset;     //!< The offset relative to the start of the bufferView in bytes. (required)
+        unsigned int byteStride;     //!< The stride, in bytes, between attributes referenced by this accessor. (default: 0)
+        ComponentType componentType; //!< The datatype of components in the attribute. (required)
+        unsigned int count;          //!< The number of attributes referenced by this accessor. (required)
+        AttribType::Value type;      //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
+        std::vector<float> max;      //!< Maximum value of each component in this attribute.
+        std::vector<float> min;      //!< Minimum value of each component in this attribute.
+
+        unsigned int GetNumComponents();
+        unsigned int GetBytesPerComponent();
+        unsigned int GetElementSize();
+
+        inline uint8_t* GetPointer();
+
+        template<class T>
+        bool ExtractData(T*& outData);
+
+        void WriteData(size_t count, const void* src_buffer, size_t src_stride);
+
+        //! Helper class to iterate the data
+        class Indexer
+        {
+            friend struct Accessor;
+
+            Accessor& accessor;
+            uint8_t* data;
+            size_t elemSize, stride;
+
+            Indexer(Accessor& acc);
+
+        public:
+
+            //! Accesses the i-th value as defined by the accessor
+            template<class T>
+            T GetValue(int i);
+
+            //! Accesses the i-th value as defined by the accessor
+            inline unsigned int GetUInt(int i)
+            {
+                return GetValue<unsigned int>(i);
+            }
+
+            inline bool IsValid() const
+            {
+                return data != 0;
+            }
+        };
+
+        inline Indexer GetIndexer()
+        {
+            return Indexer(*this);
+        }
+
+        Accessor() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    //! A buffer points to binary geometry, animation, or skins.
+    struct Buffer : public Object
+	{
+		/********************* Types *********************/
+	public:
+
+		enum Type
+		{
+			Type_arraybuffer,
+			Type_text
+		};
+
+		/// \struct SEncodedRegion
+		/// Descriptor of encoded region in "bufferView".
+		struct SEncodedRegion
+		{
+			const size_t Offset;///< Offset from begin of "bufferView" to encoded region, in bytes.
+			const size_t EncodedData_Length;///< Size of encoded region, in bytes.
+			uint8_t* const DecodedData;///< Cached encoded data.
+			const size_t DecodedData_Length;///< Size of decoded region, in bytes.
+			const std::string ID;///< ID of the region.
+
+			/// \fn SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID)
+			/// Constructor.
+			/// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes.
+			/// \param [in] pEncodedData_Length - size of encoded region, in bytes.
+			/// \param [in] pDecodedData - pointer to decoded data array.
+			/// \param [in] pDecodedData_Length - size of encoded region, in bytes.
+			/// \param [in] pID - ID of the region.
+			SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID)
+				: Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID)
+			{}
+
+			/// \fn ~SEncodedRegion()
+			/// Destructor.
+			~SEncodedRegion() { delete[] DecodedData; }
+		};
+
+		/******************* Variables *******************/
+
+		//std::string uri; //!< The uri of the buffer. Can be a filepath, a data uri, etc. (required)
+		size_t byteLength; //!< The length of the buffer in bytes. (default: 0)
+		//std::string type; //!< XMLHttpRequest responseType (default: "arraybuffer")
+
+		Type type;
+
+		/// \var EncodedRegion_Current
+		/// Pointer to currently active encoded region.
+		/// Why not decoding all regions at once and not to set one buffer with decoded data?
+		/// Yes, why not? Even "accessor" point to decoded data. I mean that fields "byteOffset", "byteStride" and "count" has values which describes decoded
+		/// data array. But only in range of mesh while is active parameters from "compressedData". For another mesh accessors point to decoded data too. But
+		/// offset is counted for another regions is encoded.
+		/// Example. You have two meshes. For every of it you have 4 bytes of data. That data compressed to 2 bytes. So, you have buffer with encoded data:
+		/// M1_E0, M1_E1, M2_E0, M2_E1.
+		/// After decoding you'll get:
+		/// M1_D0, M1_D1, M1_D2, M1_D3, M2_D0, M2_D1, M2_D2, M2_D3.
+		/// "accessors" must to use values that point to decoded data - obviously. So, you'll expect "accessors" like
+		/// "accessor_0" : { byteOffset: 0, byteLength: 4}, "accessor_1" : { byteOffset: 4, byteLength: 4}
+		/// but in real life you'll get:
+		/// "accessor_0" : { byteOffset: 0, byteLength: 4}, "accessor_1" : { byteOffset: 2, byteLength: 4}
+		/// Yes, accessor of next mesh has offset and length which mean: current mesh data is decoded, all other data is encoded.
+		/// And when before you start to read data of current mesh (with encoded data ofcourse) you must decode region of "bufferView", after read finished
+		/// delete encoding mark. And after that you can repeat process: decode data of mesh, read, delete decoded data.
+		///
+		/// Remark. Encoding all data at once is good in world with computers which do not has RAM limitation. So, you must use step by step encoding in
+		/// exporter and importer. And, thanks to such way, there is no need to load whole file into memory.
+		SEncodedRegion* EncodedRegion_Current;
+
+	private:
+
+		shared_ptr<uint8_t> mData; //!< Pointer to the data
+		bool mIsSpecial; //!< Set to true for special cases (e.g. the body buffer)
+
+		/// \var EncodedRegion_List
+		/// List of encoded regions.
+		std::list<SEncodedRegion*> EncodedRegion_List;
+
+		/******************* Functions *******************/
+
+	public:
+
+		Buffer();
+		~Buffer();
+
+		void Read(Value& obj, Asset& r);
+
+        bool LoadFromStream(IOStream& stream, size_t length = 0, size_t baseOffset = 0);
+
+		/// \fn void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID)
+		/// Mark region of "bufferView" as encoded. When data is request from such region then "bufferView" use decoded data.
+		/// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes.
+		/// \param [in] pEncodedData_Length - size of encoded region, in bytes.
+		/// \param [in] pDecodedData - pointer to decoded data array.
+		/// \param [in] pDecodedData_Length - size of encoded region, in bytes.
+		/// \param [in] pID - ID of the region.
+		void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID);
+
+		/// \fn void EncodedRegion_SetCurrent(const std::string& pID)
+		/// Select current encoded region by ID. \sa EncodedRegion_Current.
+		/// \param [in] pID - ID of the region.
+		void EncodedRegion_SetCurrent(const std::string& pID);
+
+		/// \fn bool ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count)
+		/// Replace part of buffer data. Pay attention that function work with original array of data (\ref mData) not with encoded regions.
+		/// \param [in] pBufferData_Offset - index of first element in buffer from which new data will be placed.
+		/// \param [in] pBufferData_Count - count of bytes in buffer which will be replaced.
+		/// \param [in] pReplace_Data - pointer to array with new data for buffer.
+		/// \param [in] pReplace_Count - count of bytes in new data.
+		/// \return true - if successfully replaced, false if input arguments is out of range.
+		bool ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count);
+
+        size_t AppendData(uint8_t* data, size_t length);
+        void Grow(size_t amount);
+
+        uint8_t* GetPointer()
+            { return mData.get(); }
+
+        void MarkAsSpecial()
+            { mIsSpecial = true; }
+
+        bool IsSpecial() const
+            { return mIsSpecial; }
+
+        std::string GetURI()
+            { return std::string(this->id) + ".bin"; }
+
+        static const char* TranslateId(Asset& r, const char* id);
+    };
+
+    //! A view into a buffer generally representing a subset of the buffer.
+    struct BufferView : public Object
+    {
+        Ref<Buffer> buffer; //! The ID of the buffer. (required)
+        size_t byteOffset; //! The offset into the buffer in bytes. (required)
+        size_t byteLength; //! The length of the bufferView in bytes. (default: 0)
+
+        BufferViewTarget target; //! The target that the WebGL buffer should be bound to.
+
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Camera : public Object
+    {
+        enum Type
+        {
+            Perspective,
+            Orthographic
+        };
+
+        Type type;
+
+        union
+        {
+            struct {
+                float aspectRatio; //!<The floating - point aspect ratio of the field of view. (0 = undefined = use the canvas one)
+                float yfov;  //!<The floating - point vertical field of view in radians. (required)
+                float zfar;  //!<The floating - point distance to the far clipping plane. (required)
+                float znear; //!< The floating - point distance to the near clipping plane. (required)
+            } perspective;
+
+            struct {
+                float xmag;  //! The floating-point horizontal magnification of the view. (required)
+                float ymag;  //! The floating-point vertical magnification of the view. (required)
+                float zfar;  //! The floating-point distance to the far clipping plane. (required)
+                float znear; //! The floating-point distance to the near clipping plane. (required)
+            } ortographic;
+        } cameraProperties;
+
+        Camera() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+
+    //! Image data used to create a texture.
+    struct Image : public Object
+    {
+        std::string uri; //! The uri of the image, that can be a file path, a data URI, etc.. (required)
+
+        Ref<BufferView> bufferView;
+
+        std::string mimeType;
+
+        int width, height;
+
+    private:
+        uint8_t* mData;
+        size_t mDataLength;
+
+    public:
+
+        Image();
+        void Read(Value& obj, Asset& r);
+
+        inline bool HasData() const
+            { return mDataLength > 0; }
+
+        inline size_t GetDataLength() const
+            { return mDataLength; }
+
+        inline const uint8_t* GetData() const
+            { return mData; }
+
+        inline uint8_t* StealData();
+
+        inline void SetData(uint8_t* data, size_t length, Asset& r);
+	};
+
+	const vec4 defaultBaseColor = {1, 1, 1, 1};
+	const vec3 defaultEmissiveFactor = {0, 0, 0};
+	const vec4 defaultDiffuseFactor = {1, 1, 1, 1};
+	const vec3 defaultSpecularFactor = {1, 1, 1};
+
+    struct TextureInfo
+    {
+        Ref<Texture> texture;
+        unsigned int index;
+        unsigned int texCoord = 0;
+    };
+
+    struct NormalTextureInfo : TextureInfo
+    {
+        float scale = 1;
+    };
+
+    struct OcclusionTextureInfo : TextureInfo
+    {
+        float strength = 1;
+    };
+
+    struct PbrMetallicRoughness
+    {
+        vec4 baseColorFactor;
+        TextureInfo baseColorTexture;
+        TextureInfo metallicRoughnessTexture;
+        float metallicFactor;
+        float roughnessFactor;
+    };
+
+    struct PbrSpecularGlossiness
+    {
+        vec4 diffuseFactor;
+        vec3 specularFactor;
+        float glossinessFactor;
+        TextureInfo diffuseTexture;
+		TextureInfo specularGlossinessTexture;
+
+		PbrSpecularGlossiness() { SetDefaults(); }
+		void SetDefaults();
+    };
+
+    //! The material appearance of a primitive.
+    struct Material : public Object
+    {
+        //PBR metallic roughness properties
+        PbrMetallicRoughness pbrMetallicRoughness;
+
+        //other basic material properties
+        NormalTextureInfo normalTexture;
+        OcclusionTextureInfo occlusionTexture;
+        TextureInfo emissiveTexture;
+        vec3 emissiveFactor;
+        std::string alphaMode;
+        float alphaCutoff;
+        bool doubleSided;
+
+        //extension: KHR_materials_pbrSpecularGlossiness
+        Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness;
+
+        Material() { SetDefaults(); }
+        void Read(Value& obj, Asset& r);
+        void SetDefaults();
+    };
+
+    //! A set of primitives to be rendered. A node can contain one or more meshes. A node's transform places the mesh in the scene.
+    struct Mesh : public Object
+    {
+        typedef std::vector< Ref<Accessor> > AccessorList;
+
+        struct Primitive
+        {
+            PrimitiveMode mode;
+
+            struct Attributes {
+                AccessorList position, normal, texcoord, color, joint, jointmatrix, weight;
+            } attributes;
+
+            Ref<Accessor> indices;
+
+            Ref<Material> material;
+        };
+
+        std::vector<Primitive> primitives;
+
+        Mesh() {}
+
+		/// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root)
+		/// Get mesh data from JSON-object and place them to root asset.
+		/// \param [in] pJSON_Object - reference to pJSON-object from which data are read.
+		/// \param [out] pAsset_Root - reference to root assed where data will be stored.
+		void Read(Value& pJSON_Object, Asset& pAsset_Root);
+    };
+
+    struct Node : public Object
+    {
+        std::vector< Ref<Node> > children;
+        std::vector< Ref<Mesh> > meshes;
+
+        Nullable<mat4> matrix;
+        Nullable<vec3> translation;
+        Nullable<vec4> rotation;
+        Nullable<vec3> scale;
+
+        Ref<Camera> camera;
+
+        std::vector< Ref<Node> > skeletons;       //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy.
+        Ref<Skin> skin;                           //!< The ID of the skin referenced by this node.
+        std::string jointName;                    //!< Name used when this node is a joint in a skin.
+
+        Ref<Node> parent;                         //!< This is not part of the glTF specification. Used as a helper.
+
+        Node() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Program : public Object
+    {
+        Program() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+
+    struct Sampler : public Object
+    {
+        SamplerMagFilter magFilter; //!< The texture magnification filter.
+        SamplerMinFilter minFilter; //!< The texture minification filter.
+        SamplerWrap wrapS;          //!< The texture wrapping in the S direction.
+        SamplerWrap wrapT;          //!< The texture wrapping in the T direction.
+
+        Sampler() { SetDefaults(); }
+        void Read(Value& obj, Asset& r);
+        void SetDefaults();
+    };
+
+    struct Scene : public Object
+    {
+        std::vector< Ref<Node> > nodes;
+
+        Scene() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Shader : public Object
+    {
+        Shader() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Skin : public Object
+    {
+        Nullable<mat4> bindShapeMatrix;       //!< Floating-point 4x4 transformation matrix stored in column-major order.
+        Ref<Accessor> inverseBindMatrices;    //!< The ID of the accessor containing the floating-point 4x4 inverse-bind matrices.
+        std::vector<Ref<Node>> jointNames;    //!< Joint names of the joints (nodes with a jointName property) in this skin.
+        std::string name;                     //!< The user-defined name of this object.
+
+        Skin() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    //! A texture and its sampler.
+    struct Texture : public Object
+    {
+        Ref<Sampler> sampler; //!< The ID of the sampler used by this texture. (required)
+        Ref<Image> source;    //!< The ID of the image used by this texture. (required)
+
+        //TextureFormat format; //!< The texture's format. (default: TextureFormat_RGBA)
+        //TextureFormat internalFormat; //!< The texture's internal format. (default: TextureFormat_RGBA)
+
+        //TextureTarget target; //!< The target that the WebGL texture should be bound to. (default: TextureTarget_TEXTURE_2D)
+        //TextureType type; //!< Texel datatype. (default: TextureType_UNSIGNED_BYTE)
+
+        Texture() {}
+        void Read(Value& obj, Asset& r);
+    };
+
+    struct Animation : public Object
+    {
+        struct AnimSampler {
+            std::string id;               //!< The ID of this sampler.
+            std::string input;            //!< The ID of a parameter in this animation to use as key-frame input.
+            std::string interpolation;    //!< Type of interpolation algorithm to use between key-frames.
+            std::string output;           //!< The ID of a parameter in this animation to use as key-frame output.
+        };
+
+        struct AnimChannel {
+            int sampler;                 //!< The index of a sampler in the containing animation's samplers property.
+
+            struct AnimTarget {
+                Ref<Node> node;          //!< The node to animate.
+                std::string path;        //!< The name of property of the node to animate ("translation", "rotation", or "scale").
+            } target;
+        };
+
+        struct AnimParameters {
+            Ref<Accessor> TIME;           //!< Accessor reference to a buffer storing a array of floating point scalar values.
+            Ref<Accessor> rotation;       //!< Accessor reference to a buffer storing a array of four-component floating-point vectors.
+            Ref<Accessor> scale;          //!< Accessor reference to a buffer storing a array of three-component floating-point vectors.
+            Ref<Accessor> translation;    //!< Accessor reference to a buffer storing a array of three-component floating-point vectors.
+        };
+
+        // AnimChannel Channels[3];            //!< Connect the output values of the key-frame animation to a specific node in the hierarchy.
+        // AnimParameters Parameters;          //!< The samplers that interpolate between the key-frames.
+        // AnimSampler Samplers[3];            //!< The parameterized inputs representing the key-frame data.
+
+        std::vector<AnimChannel> Channels;            //!< Connect the output values of the key-frame animation to a specific node in the hierarchy.
+        AnimParameters Parameters;                    //!< The samplers that interpolate between the key-frames.
+        std::vector<AnimSampler> Samplers;         //!< The parameterized inputs representing the key-frame data.
+
+        Animation() {}
+        void Read(Value& obj, Asset& r);
+
+        //! Get accessor given an animation parameter name.
+        Ref<Accessor> GetAccessor(std::string name) {
+            if (name == "TIME") {
+                return Parameters.TIME;
+            } else if (name == "rotation") {
+                return Parameters.rotation;
+            } else if (name == "scale") {
+                return Parameters.scale;
+            } else if (name == "translation") {
+                return Parameters.translation;
+            }
+            return Ref<Accessor>();
+        }
+    };
+
+
+    //! Base class for LazyDict that acts as an interface
+    class LazyDictBase
+    {
+    public:
+        virtual ~LazyDictBase() {}
+
+        virtual void AttachToDocument(Document& doc) = 0;
+        virtual void DetachFromDocument() = 0;
+
+        virtual void WriteObjects(AssetWriter& writer) = 0;
+    };
+
+
+    template<class T>
+    class LazyDict;
+
+    //! (Implemented in glTFAssetWriter.h)
+    template<class T>
+    void WriteLazyDict(LazyDict<T>& d, AssetWriter& w);
+
+
+    //! Manages lazy loading of the glTF top-level objects, and keeps a reference to them by ID
+    //! It is the owner the loaded objects, so when it is destroyed it also deletes them
+    template<class T>
+    class LazyDict : public LazyDictBase
+    {
+        friend class Asset;
+        friend class AssetWriter;
+
+        typedef typename std::gltf_unordered_map< unsigned int, unsigned int > Dict;
+        typedef typename std::gltf_unordered_map< std::string, unsigned int > IdDict;
+
+        std::vector<T*>     mObjs;         //! The read objects
+        Dict                mObjsByOIndex; //! The read objects accessible by original index
+        IdDict              mObjsById;     //! The read objects accessible by id
+        const char*         mDictId;       //! ID of the dictionary object
+        const char*         mExtId;        //! ID of the extension defining the dictionary
+        Value*              mDict;         //! JSON dictionary object
+        Asset&              mAsset;        //! The asset instance
+
+        void AttachToDocument(Document& doc);
+        void DetachFromDocument();
+
+        void WriteObjects(AssetWriter& writer)
+            { WriteLazyDict<T>(*this, writer); }
+
+        Ref<T> Add(T* obj);
+
+    public:
+        LazyDict(Asset& asset, const char* dictId, const char* extId = 0);
+        ~LazyDict();
+
+        Ref<T> Retrieve(unsigned int i);
+
+        Ref<T> Get(unsigned int i);
+        Ref<T> Get(const char* id);
+
+        Ref<T> Create(const char* id);
+        Ref<T> Create(const std::string& id)
+            { return Create(id.c_str()); }
+
+        inline unsigned int Size() const
+            { return unsigned(mObjs.size()); }
+
+        inline T& operator[](size_t i)
+            { return *mObjs[i]; }
+
+    };
+
+
+    struct AssetMetadata
+    {
+        std::string copyright; //!< A copyright message suitable for display to credit the content creator.
+        std::string generator; //!< Tool that generated this glTF model.Useful for debugging.
+
+        struct {
+            std::string api;     //!< Specifies the target rendering API (default: "WebGL")
+            std::string version; //!< Specifies the target rendering API (default: "1.0.3")
+        } profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {})
+
+        std::string version; //!< The glTF format version
+
+        void Read(Document& doc);
+
+        AssetMetadata() : version("") {}
+    };
+
+    //
+    // glTF Asset class
+    //
+
+    //! Root object for a glTF asset
+    class Asset
+    {
+        typedef std::gltf_unordered_map<std::string, int> IdMap;
+
+        template<class T>
+        friend class LazyDict;
+
+        friend struct Buffer; // To access OpenFile
+
+        friend class AssetWriter;
+
+    private:
+        IOSystem* mIOSystem;
+
+        std::string mCurrentAssetDir;
+
+        size_t mSceneLength;
+        size_t mBodyOffset, mBodyLength;
+
+        std::vector<LazyDictBase*> mDicts;
+
+        IdMap mUsedIds;
+
+        Ref<Buffer> mBodyBuffer;
+
+        Asset(Asset&);
+        Asset& operator=(const Asset&);
+
+    public:
+
+        //! Keeps info about the enabled extensions
+        struct Extensions
+        {
+            bool KHR_materials_pbrSpecularGlossiness;
+
+        } extensionsUsed;
+
+        AssetMetadata asset;
+
+
+        // Dictionaries for each type of object
+
+        LazyDict<Accessor>    accessors;
+        LazyDict<Animation>   animations;
+        LazyDict<Buffer>      buffers;
+        LazyDict<BufferView>  bufferViews;
+        LazyDict<Camera>      cameras;
+        LazyDict<Image>       images;
+        LazyDict<Material>    materials;
+        LazyDict<Mesh>        meshes;
+        LazyDict<Node>        nodes;
+        LazyDict<Sampler>     samplers;
+        LazyDict<Scene>       scenes;
+        LazyDict<Skin>        skins;
+        LazyDict<Texture>     textures;
+
+        Ref<Scene> scene;
+
+    public:
+        Asset(IOSystem* io = 0)
+            : mIOSystem(io)
+            , asset()
+            , accessors     (*this, "accessors")
+            , animations    (*this, "animations")
+            , buffers       (*this, "buffers")
+            , bufferViews   (*this, "bufferViews")
+            , cameras       (*this, "cameras")
+            , images        (*this, "images")
+            , materials     (*this, "materials")
+            , meshes        (*this, "meshes")
+            , nodes         (*this, "nodes")
+            , samplers      (*this, "samplers")
+            , scenes        (*this, "scenes")
+            , skins         (*this, "skins")
+            , textures      (*this, "textures")
+        {
+            memset(&extensionsUsed, 0, sizeof(extensionsUsed));
+        }
+
+        //! Main function
+        void Load(const std::string& file);
+
+        //! Search for an available name, starting from the given strings
+        std::string FindUniqueID(const std::string& str, const char* suffix);
+
+        Ref<Buffer> GetBodyBuffer()
+            { return mBodyBuffer; }
+
+    private:
+        void ReadExtensionsUsed(Document& doc);
+
+        IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);
+    };
+
+}
+
+// Include the implementation of the methods
+#include "glTF2Asset.inl"
+
+#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#endif // GLTF2ASSET_H_INC

+ 1319 - 0
code/glTF2Asset.inl

@@ -0,0 +1,1319 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "StringUtils.h"
+
+// Header files, Assimp
+#include <assimp/DefaultLogger.hpp>
+
+using namespace Assimp;
+
+namespace glTF2 {
+
+namespace {
+
+    //
+    // JSON Value reading helpers
+    //
+
+    template<class T>
+    struct ReadHelper { static bool Read(Value& val, T& out) {
+        return val.IsInt() ? out = static_cast<T>(val.GetInt()), true : false;
+    }};
+
+    template<> struct ReadHelper<bool> { static bool Read(Value& val, bool& out) {
+        return val.IsBool() ? out = val.GetBool(), true : false;
+    }};
+
+    template<> struct ReadHelper<float> { static bool Read(Value& val, float& out) {
+        return val.IsNumber() ? out = static_cast<float>(val.GetDouble()), true : false;
+    }};
+
+    template<unsigned int N> struct ReadHelper<float[N]> { static bool Read(Value& val, float (&out)[N]) {
+        if (!val.IsArray() || val.Size() != N) return false;
+        for (unsigned int i = 0; i < N; ++i) {
+            if (val[i].IsNumber())
+                out[i] = static_cast<float>(val[i].GetDouble());
+        }
+        return true;
+    }};
+
+    template<> struct ReadHelper<const char*> { static bool Read(Value& val, const char*& out) {
+        return val.IsString() ? (out = val.GetString(), true) : false;
+    }};
+
+    template<> struct ReadHelper<std::string> { static bool Read(Value& val, std::string& out) {
+        return val.IsString() ? (out = std::string(val.GetString(), val.GetStringLength()), true) : false;
+    }};
+
+    template<class T> struct ReadHelper< Nullable<T> > { static bool Read(Value& val, Nullable<T>& out) {
+        return out.isPresent = ReadHelper<T>::Read(val, out.value);
+    }};
+
+    template<class T>
+    inline static bool ReadValue(Value& val, T& out)
+    {
+        return ReadHelper<T>::Read(val, out);
+    }
+
+    template<class T>
+    inline static bool ReadMember(Value& obj, const char* id, T& out)
+    {
+        Value::MemberIterator it = obj.FindMember(id);
+        if (it != obj.MemberEnd()) {
+            return ReadHelper<T>::Read(it->value, out);
+        }
+        return false;
+    }
+
+    template<class T>
+    inline static T MemberOrDefault(Value& obj, const char* id, T defaultValue)
+    {
+        T out;
+        return ReadMember(obj, id, out) ? out : defaultValue;
+    }
+
+    inline Value* FindMember(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd()) ? &it->value : 0;
+    }
+
+    inline Value* FindString(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0;
+    }
+
+    inline Value* FindNumber(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0;
+    }
+
+    inline Value* FindUInt(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsUint()) ? &it->value : 0;
+    }
+
+    inline Value* FindArray(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsArray()) ? &it->value : 0;
+    }
+
+    inline Value* FindObject(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsObject()) ? &it->value : 0;
+    }
+}
+
+//
+// LazyDict methods
+//
+
+template<class T>
+inline LazyDict<T>::LazyDict(Asset& asset, const char* dictId, const char* extId)
+    : mDictId(dictId), mExtId(extId), mDict(0), mAsset(asset)
+{
+    asset.mDicts.push_back(this); // register to the list of dictionaries
+}
+
+template<class T>
+inline LazyDict<T>::~LazyDict()
+{
+    for (size_t i = 0; i < mObjs.size(); ++i) {
+        delete mObjs[i];
+    }
+}
+
+
+template<class T>
+inline void LazyDict<T>::AttachToDocument(Document& doc)
+{
+    Value* container = 0;
+
+    if (mExtId) {
+        if (Value* exts = FindObject(doc, "extensions")) {
+            container = FindObject(*exts, mExtId);
+        }
+    }
+    else {
+        container = &doc;
+    }
+
+    if (container) {
+        mDict = FindArray(*container, mDictId);
+    }
+}
+
+template<class T>
+inline void LazyDict<T>::DetachFromDocument()
+{
+    mDict = 0;
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Retrieve(unsigned int i)
+{
+
+    typename Dict::iterator it = mObjsByOIndex.find(i);
+    if (it != mObjsByOIndex.end()) {// already created?
+        return Ref<T>(mObjs, it->second);
+    }
+
+    // read it from the JSON object
+    if (!mDict) {
+        throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\"");
+    }
+
+    if (!mDict->IsArray()) {
+        throw DeadlyImportError("GLTF: Field is not an array \"" + std::string(mDictId) + "\"");
+    }
+
+    Value &obj = (*mDict)[i];
+
+    if (!obj.IsObject()) {
+        throw DeadlyImportError("GLTF: Object at index \"" + std::to_string(i) + "\" is not a JSON object");
+    }
+
+    T* inst = new T();
+    inst->id = std::string(mDictId) + "_" + std::to_string(i);
+    inst->oIndex = i;
+    ReadMember(obj, "name", inst->name);
+    inst->Read(obj, mAsset);
+
+    return Add(inst);
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Get(unsigned int i)
+{
+
+    return Ref<T>(mObjs, i);
+
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Get(const char* id)
+{
+    id = T::TranslateId(mAsset, id);
+
+    typename IdDict::iterator it = mObjsById.find(id);
+    if (it != mObjsById.end()) { // already created?
+        return Ref<T>(mObjs, it->second);
+    }
+
+    return Ref<T>();
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Add(T* obj)
+{
+    unsigned int idx = unsigned(mObjs.size());
+    mObjs.push_back(obj);
+    mObjsByOIndex[obj->oIndex] = idx;
+    mObjsById[obj->id] = idx;
+    mAsset.mUsedIds[obj->id] = true;
+    return Ref<T>(mObjs, idx);
+}
+
+template<class T>
+Ref<T> LazyDict<T>::Create(const char* id)
+{
+    Asset::IdMap::iterator it = mAsset.mUsedIds.find(id);
+    if (it != mAsset.mUsedIds.end()) {
+        throw DeadlyImportError("GLTF: two objects with the same ID exist");
+    }
+    T* inst = new T();
+    unsigned int idx = unsigned(mObjs.size());
+    inst->id = id;
+    inst->index = idx;
+    inst->oIndex = idx;
+    return Add(inst);
+}
+
+
+//
+// glTF dictionary objects methods
+//
+
+
+inline Buffer::Buffer()
+	: byteLength(0), type(Type_arraybuffer), EncodedRegion_Current(nullptr), mIsSpecial(false)
+{ }
+
+inline Buffer::~Buffer()
+{
+	for(SEncodedRegion* reg : EncodedRegion_List) delete reg;
+}
+
+inline const char* Buffer::TranslateId(Asset& r, const char* id)
+{
+    return id;
+}
+
+inline void Buffer::Read(Value& obj, Asset& r)
+{
+    size_t statedLength = MemberOrDefault<size_t>(obj, "byteLength", 0);
+    byteLength = statedLength;
+
+    Value* it = FindString(obj, "uri");
+    if (!it) {
+        if (statedLength > 0) {
+            throw DeadlyImportError("GLTF: buffer with non-zero length missing the \"uri\" attribute");
+        }
+        return;
+    }
+
+    const char* uri = it->GetString();
+
+    Util::DataURI dataURI;
+    if (ParseDataURI(uri, it->GetStringLength(), dataURI)) {
+        if (dataURI.base64) {
+            uint8_t* data = 0;
+            this->byteLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, data);
+            this->mData.reset(data, std::default_delete<uint8_t[]>());
+
+            if (statedLength > 0 && this->byteLength != statedLength) {
+                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
+                    " bytes, but found " + to_string(dataURI.dataLength));
+            }
+        }
+        else { // assume raw data
+            if (statedLength != dataURI.dataLength) {
+                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
+                                        " bytes, but found " + to_string(dataURI.dataLength));
+            }
+
+            this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
+            memcpy( this->mData.get(), dataURI.data, dataURI.dataLength );
+        }
+    }
+    else { // Local file
+        if (byteLength > 0) {
+            std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : "";
+
+            IOStream* file = r.OpenFile(dir + uri, "rb");
+            if (file) {
+                bool ok = LoadFromStream(*file, byteLength);
+                delete file;
+
+                if (!ok)
+                    throw DeadlyImportError("GLTF: error while reading referenced file \"" + std::string(uri) + "\"" );
+            }
+            else {
+                throw DeadlyImportError("GLTF: could not open referenced file \"" + std::string(uri) + "\"");
+            }
+        }
+    }
+}
+
+inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseOffset)
+{
+    byteLength = length ? length : stream.FileSize();
+
+    if (baseOffset) {
+        stream.Seek(baseOffset, aiOrigin_SET);
+    }
+
+    mData.reset(new uint8_t[byteLength], std::default_delete<uint8_t[]>());
+
+    if (stream.Read(mData.get(), byteLength, 1) != 1) {
+        return false;
+    }
+    return true;
+}
+
+inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID)
+{
+	// Check pointer to data
+	if(pDecodedData == nullptr) throw DeadlyImportError("GLTF: for marking encoded region pointer to decoded data must be provided.");
+
+	// Check offset
+	if(pOffset > byteLength)
+	{
+		const uint8_t val_size = 32;
+
+		char val[val_size];
+
+		ai_snprintf(val, val_size, "%llu", (long long)pOffset);
+		throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
+	}
+
+	// Check length
+	if((pOffset + pEncodedData_Length) > byteLength)
+	{
+		const uint8_t val_size = 64;
+
+		char val[val_size];
+
+		ai_snprintf(val, val_size, "%llu, %llu", (long long)pOffset, (long long)pEncodedData_Length);
+		throw DeadlyImportError(std::string("GLTF: encoded region with offset/length (") + val + ") is out of range.");
+	}
+
+	// Add new region
+	EncodedRegion_List.push_back(new SEncodedRegion(pOffset, pEncodedData_Length, pDecodedData, pDecodedData_Length, pID));
+	// And set new value for "byteLength"
+	byteLength += (pDecodedData_Length - pEncodedData_Length);
+}
+
+inline void Buffer::EncodedRegion_SetCurrent(const std::string& pID)
+{
+	if((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return;
+
+	for(SEncodedRegion* reg : EncodedRegion_List)
+	{
+		if(reg->ID == pID)
+		{
+			EncodedRegion_Current = reg;
+
+			return;
+		}
+
+	}
+
+	throw DeadlyImportError("GLTF: EncodedRegion with ID: \"" + pID + "\" not found.");
+}
+
+inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count)
+{
+const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count;
+
+uint8_t* new_data;
+
+	if((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) return false;
+
+	new_data = new uint8_t[new_data_size];
+	// Copy data which place before replacing part.
+	memcpy(new_data, mData.get(), pBufferData_Offset);
+	// Copy new data.
+	memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count);
+	// Copy data which place after replacing part.
+	memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset);
+	// Apply new data
+	mData.reset(new_data, std::default_delete<uint8_t[]>());
+	byteLength = new_data_size;
+
+	return true;
+}
+
+inline size_t Buffer::AppendData(uint8_t* data, size_t length)
+{
+    size_t offset = this->byteLength;
+    Grow(length);
+    memcpy(mData.get() + offset, data, length);
+    return offset;
+}
+
+inline void Buffer::Grow(size_t amount)
+{
+    if (amount <= 0) return;
+    uint8_t* b = new uint8_t[byteLength + amount];
+    if (mData) memcpy(b, mData.get(), byteLength);
+    mData.reset(b, std::default_delete<uint8_t[]>());
+    byteLength += amount;
+}
+
+//
+// struct BufferView
+//
+
+inline void BufferView::Read(Value& obj, Asset& r)
+{
+
+    if (Value* bufferVal = FindUInt(obj, "buffer")) {
+        buffer = r.buffers.Retrieve(bufferVal->GetUint());
+    }
+
+    byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
+    byteLength = MemberOrDefault(obj, "byteLength", 0u);
+}
+
+//
+// struct Accessor
+//
+
+inline void Accessor::Read(Value& obj, Asset& r)
+{
+
+    if (Value* bufferViewVal = FindUInt(obj, "bufferView")) {
+        bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint());
+    }
+
+    byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
+    byteStride = MemberOrDefault(obj, "byteStride", 0u);
+    componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE);
+    count = MemberOrDefault(obj, "count", 0u);
+
+    const char* typestr;
+    type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR;
+}
+
+inline unsigned int Accessor::GetNumComponents()
+{
+    return AttribType::GetNumComponents(type);
+}
+
+inline unsigned int Accessor::GetBytesPerComponent()
+{
+    return int(ComponentTypeSize(componentType));
+}
+
+inline unsigned int Accessor::GetElementSize()
+{
+    return GetNumComponents() * GetBytesPerComponent();
+}
+
+inline uint8_t* Accessor::GetPointer()
+{
+    if (!bufferView || !bufferView->buffer) return 0;
+    uint8_t* basePtr = bufferView->buffer->GetPointer();
+    if (!basePtr) return 0;
+
+    size_t offset = byteOffset + bufferView->byteOffset;
+
+	// Check if region is encoded.
+	if(bufferView->buffer->EncodedRegion_Current != nullptr)
+	{
+		const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset;
+		const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length;
+
+		if((offset >= begin) && (offset < end))
+			return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin];
+	}
+
+	return basePtr + offset;
+}
+
+namespace {
+    inline void CopyData(size_t count,
+            const uint8_t* src, size_t src_stride,
+                  uint8_t* dst, size_t dst_stride)
+    {
+        if (src_stride == dst_stride) {
+            memcpy(dst, src, count * src_stride);
+        }
+        else {
+            size_t sz = std::min(src_stride, dst_stride);
+            for (size_t i = 0; i < count; ++i) {
+                memcpy(dst, src, sz);
+                if (sz < dst_stride) {
+                    memset(dst + sz, 0, dst_stride - sz);
+                }
+                src += src_stride;
+                dst += dst_stride;
+            }
+        }
+    }
+}
+
+template<class T>
+bool Accessor::ExtractData(T*& outData)
+{
+    uint8_t* data = GetPointer();
+    if (!data) return false;
+
+    const size_t elemSize = GetElementSize();
+    const size_t totalSize = elemSize * count;
+
+    const size_t stride = byteStride ? byteStride : elemSize;
+
+    const size_t targetElemSize = sizeof(T);
+    ai_assert(elemSize <= targetElemSize);
+
+    ai_assert(count*stride <= bufferView->byteLength);
+
+    outData = new T[count];
+    if (stride == elemSize && targetElemSize == elemSize) {
+        memcpy(outData, data, totalSize);
+    }
+    else {
+        for (size_t i = 0; i < count; ++i) {
+            memcpy(outData + i, data + i*stride, elemSize);
+        }
+    }
+
+    return true;
+}
+
+inline void Accessor::WriteData(size_t count, const void* src_buffer, size_t src_stride)
+{
+    uint8_t* buffer_ptr = bufferView->buffer->GetPointer();
+    size_t offset = byteOffset + bufferView->byteOffset;
+
+    size_t dst_stride = GetNumComponents() * GetBytesPerComponent();
+
+    const uint8_t* src = reinterpret_cast<const uint8_t*>(src_buffer);
+    uint8_t*       dst = reinterpret_cast<      uint8_t*>(buffer_ptr + offset);
+
+    ai_assert(dst + count*dst_stride <= buffer_ptr + bufferView->buffer->byteLength);
+    CopyData(count, src, src_stride, dst, dst_stride);
+}
+
+
+
+inline Accessor::Indexer::Indexer(Accessor& acc)
+    : accessor(acc)
+    , data(acc.GetPointer())
+    , elemSize(acc.GetElementSize())
+    , stride(acc.byteStride ? acc.byteStride : elemSize)
+{
+
+}
+
+//! Accesses the i-th value as defined by the accessor
+template<class T>
+T Accessor::Indexer::GetValue(int i)
+{
+    ai_assert(data);
+    ai_assert(i*stride < accessor.bufferView->byteLength);
+    T value = T();
+    memcpy(&value, data + i*stride, elemSize);
+    //value >>= 8 * (sizeof(T) - elemSize);
+    return value;
+}
+
+inline Image::Image()
+    : width(0)
+    , height(0)
+    , mData(0)
+    , mDataLength(0)
+{
+
+}
+
+inline void Image::Read(Value& obj, Asset& r)
+{
+    if (!mDataLength) {
+        if (Value* uri = FindString(obj, "uri")) {
+            const char* uristr = uri->GetString();
+
+            Util::DataURI dataURI;
+            if (ParseDataURI(uristr, uri->GetStringLength(), dataURI)) {
+                mimeType = dataURI.mediaType;
+                if (dataURI.base64) {
+                    mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, mData);
+                }
+            }
+            else {
+                this->uri = uristr;
+            }
+        }
+    }
+}
+
+inline uint8_t* Image::StealData()
+{
+    uint8_t* data = mData;
+    mDataLength = 0;
+    mData = 0;
+    return data;
+}
+
+inline void Image::SetData(uint8_t* data, size_t length, Asset& r)
+{
+    Ref<Buffer> b = r.GetBodyBuffer();
+    if (b) { // binary file: append to body
+        std::string bvId = r.FindUniqueID(this->id, "imgdata");
+        bufferView = r.bufferViews.Create(bvId);
+
+        bufferView->buffer = b;
+        bufferView->byteLength = length;
+        bufferView->byteOffset = b->AppendData(data, length);
+    }
+    else { // text file: will be stored as a data uri
+        this->mData = data;
+        this->mDataLength = length;
+    }
+}
+
+inline void Sampler::Read(Value& obj, Asset& r)
+{
+    SetDefaults();
+
+    ReadMember(obj, "name", name);
+    ReadMember(obj, "magFilter", magFilter);
+    ReadMember(obj, "minFilter", minFilter);
+    ReadMember(obj, "wrapS", wrapS);
+    ReadMember(obj, "wrapT", wrapT);
+}
+
+inline void Sampler::SetDefaults()
+{
+    //only wrapping modes have defaults
+    wrapS = SamplerWrap::Repeat;
+    wrapT = SamplerWrap::Repeat;
+    magFilter = SamplerMagFilter::UNSET;
+    minFilter = SamplerMinFilter::UNSET;
+}
+
+inline void Texture::Read(Value& obj, Asset& r)
+{
+    if (Value* sourceVal = FindUInt(obj, "source")) {
+        source = r.images.Retrieve(sourceVal->GetUint());
+    }
+
+    if (Value* samplerVal = FindUInt(obj, "sampler")) {
+        sampler = r.samplers.Retrieve(samplerVal->GetUint());
+    }
+}
+
+namespace {
+    inline void SetTextureProperties(Asset& r, Value* prop, TextureInfo& out)
+    {
+        if (Value* index = FindUInt(*prop, "index")) {
+            out.texture = r.textures.Retrieve(index->GetUint());
+        }
+
+        if (Value* texcoord = FindUInt(*prop, "texCoord")) {
+            out.texCoord = texcoord->GetUint();
+        }
+    }
+
+    inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, TextureInfo& out)
+    {
+        if (Value* prop = FindMember(vals, propName)) {
+            SetTextureProperties(r, prop, out);
+        }
+    }
+
+    inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, NormalTextureInfo& out)
+    {
+        if (Value* prop = FindMember(vals, propName)) {
+            SetTextureProperties(r, prop, out);
+
+            if (Value* scale = FindNumber(*prop, "scale")) {
+                out.scale = static_cast<float>(scale->GetDouble());
+            }
+        }
+    }
+
+    inline void ReadTextureProperty(Asset& r, Value& vals, const char* propName, OcclusionTextureInfo& out)
+    {
+        if (Value* prop = FindMember(vals, propName)) {
+            SetTextureProperties(r, prop, out);
+
+            if (Value* strength = FindNumber(*prop, "strength")) {
+                out.strength = static_cast<float>(strength->GetDouble());
+            }
+        }
+    }
+}
+
+inline void Material::Read(Value& material, Asset& r)
+{
+    SetDefaults();
+
+    if (Value* pbrMetallicRoughness = FindObject(material, "pbrMetallicRoughness")) {
+        ReadMember(*pbrMetallicRoughness, "baseColorFactor", this->pbrMetallicRoughness.baseColorFactor);
+        ReadTextureProperty(r, *pbrMetallicRoughness, "baseColorTexture", this->pbrMetallicRoughness.baseColorTexture);
+        ReadTextureProperty(r, *pbrMetallicRoughness, "metallicRoughnessTexture", this->pbrMetallicRoughness.metallicRoughnessTexture);
+        ReadMember(*pbrMetallicRoughness, "metallicFactor", this->pbrMetallicRoughness.metallicFactor);
+        ReadMember(*pbrMetallicRoughness, "roughnessFactor", this->pbrMetallicRoughness.roughnessFactor);
+    }
+
+    ReadTextureProperty(r, material, "normalTexture", this->normalTexture);
+    ReadTextureProperty(r, material, "occlusionTexture", this->occlusionTexture);
+    ReadTextureProperty(r, material, "emissiveTexture", this->emissiveTexture);
+    ReadMember(material, "emissiveFactor", this->emissiveFactor);
+
+    ReadMember(material, "doubleSided", this->doubleSided);
+    ReadMember(material, "alphaMode", this->alphaMode);
+    ReadMember(material, "alphaCutoff", this->alphaCutoff);
+
+    if (Value* extensions = FindObject(material, "extensions")) {
+        if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
+            if (Value* pbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) {
+                PbrSpecularGlossiness pbrSG;
+
+                ReadMember(*pbrSpecularGlossiness, "diffuseFactor", pbrSG.diffuseFactor);
+                ReadTextureProperty(r, *pbrSpecularGlossiness, "diffuseTexture", pbrSG.diffuseTexture);
+                ReadTextureProperty(r, *pbrSpecularGlossiness, "specularGlossinessTexture", pbrSG.specularGlossinessTexture);
+                ReadMember(*pbrSpecularGlossiness, "specularFactor", pbrSG.specularFactor);
+                ReadMember(*pbrSpecularGlossiness, "glossinessFactor", pbrSG.glossinessFactor);
+
+                this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
+            }
+        }
+    }
+}
+
+namespace {
+    void SetVector(vec4& v, const float(&in)[4])
+        { v[0] = in[0]; v[1] = in[1]; v[2] = in[2]; v[3] = in[3]; }
+
+    void SetVector(vec3& v, const float(&in)[3])
+        { v[0] = in[0]; v[1] = in[1]; v[2] = in[2]; }
+}
+
+inline void Material::SetDefaults()
+{
+    //pbr materials
+    SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor);
+    pbrMetallicRoughness.metallicFactor = 1.0;
+    pbrMetallicRoughness.roughnessFactor = 1.0;
+
+    SetVector(emissiveFactor, defaultEmissiveFactor);
+    alphaMode = "OPAQUE";
+    alphaCutoff = 0.5;
+    doubleSided = false;
+}
+
+inline void PbrSpecularGlossiness::SetDefaults()
+{
+    //pbrSpecularGlossiness properties
+    SetVector(diffuseFactor, defaultDiffuseFactor);
+    SetVector(specularFactor, defaultSpecularFactor);
+    glossinessFactor = 1.0;
+}
+
+namespace {
+
+    template<int N>
+    inline int Compare(const char* attr, const char (&str)[N]) {
+        return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0;
+    }
+
+    inline bool GetAttribVector(Mesh::Primitive& p, const char* attr, Mesh::AccessorList*& v, int& pos)
+    {
+        if ((pos = Compare(attr, "POSITION"))) {
+            v = &(p.attributes.position);
+        }
+        else if ((pos = Compare(attr, "NORMAL"))) {
+            v = &(p.attributes.normal);
+        }
+        else if ((pos = Compare(attr, "TEXCOORD"))) {
+            v = &(p.attributes.texcoord);
+        }
+        else if ((pos = Compare(attr, "COLOR"))) {
+            v = &(p.attributes.color);
+        }
+        else if ((pos = Compare(attr, "JOINT"))) {
+            v = &(p.attributes.joint);
+        }
+        else if ((pos = Compare(attr, "JOINTMATRIX"))) {
+            v = &(p.attributes.jointmatrix);
+        }
+        else if ((pos = Compare(attr, "WEIGHT"))) {
+            v = &(p.attributes.weight);
+        }
+        else return false;
+        return true;
+    }
+}
+
+inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
+{
+    if (Value* name = FindMember(pJSON_Object, "name")) {
+        this->name = name->GetString();
+    }
+
+	/****************** Mesh primitives ******************/
+	if (Value* primitives = FindArray(pJSON_Object, "primitives")) {
+        this->primitives.resize(primitives->Size());
+        for (unsigned int i = 0; i < primitives->Size(); ++i) {
+            Value& primitive = (*primitives)[i];
+
+            Primitive& prim = this->primitives[i];
+            prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES);
+
+            if (Value* attrs = FindObject(primitive, "attributes")) {
+                for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
+                    if (!it->value.IsUint()) continue;
+                    const char* attr = it->name.GetString();
+                    // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX,
+                    // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc.
+
+                    int undPos = 0;
+                    Mesh::AccessorList* vec = 0;
+                    if (GetAttribVector(prim, attr, vec, undPos)) {
+                        size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
+                        if ((*vec).size() <= idx) (*vec).resize(idx + 1);
+						(*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
+                    }
+                }
+            }
+
+            if (Value* indices = FindUInt(primitive, "indices")) {
+				prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
+            }
+
+            if (Value* material = FindUInt(primitive, "material")) {
+				prim.material = pAsset_Root.materials.Retrieve(material->GetUint());
+            }
+        }
+    }
+}
+
+inline void Camera::Read(Value& obj, Asset& r)
+{
+    type = MemberOrDefault(obj, "type", Camera::Perspective);
+
+    const char* subobjId = (type == Camera::Orthographic) ? "ortographic" : "perspective";
+
+    Value* it = FindObject(obj, subobjId);
+    if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters");
+
+    if (type == Camera::Perspective) {
+        cameraProperties.perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f);
+        cameraProperties.perspective.yfov        = MemberOrDefault(*it, "yfov", 3.1415f/2.f);
+        cameraProperties.perspective.zfar        = MemberOrDefault(*it, "zfar", 100.f);
+        cameraProperties.perspective.znear       = MemberOrDefault(*it, "znear", 0.01f);
+    }
+    else {
+        cameraProperties.ortographic.xmag  = MemberOrDefault(obj, "xmag", 1.f);
+        cameraProperties.ortographic.ymag  = MemberOrDefault(obj, "ymag", 1.f);
+        cameraProperties.ortographic.zfar  = MemberOrDefault(obj, "zfar", 100.f);
+        cameraProperties.ortographic.znear = MemberOrDefault(obj, "znear", 0.01f);
+    }
+}
+
+inline void Node::Read(Value& obj, Asset& r)
+{
+
+    if (Value* children = FindArray(obj, "children")) {
+        this->children.reserve(children->Size());
+        for (unsigned int i = 0; i < children->Size(); ++i) {
+            Value& child = (*children)[i];
+            if (child.IsUint()) {
+                // get/create the child node
+                Ref<Node> chn = r.nodes.Retrieve(child.GetUint());
+                if (chn) this->children.push_back(chn);
+            }
+        }
+    }
+
+    if (Value* matrix = FindArray(obj, "matrix")) {
+        ReadValue(*matrix, this->matrix);
+    }
+    else {
+        ReadMember(obj, "translation", translation);
+        ReadMember(obj, "scale", scale);
+        ReadMember(obj, "rotation", rotation);
+    }
+
+    if (Value* mesh = FindUInt(obj, "mesh")) {
+        unsigned numMeshes = 1;
+
+        this->meshes.reserve(numMeshes);
+
+        Ref<Mesh> meshRef = r.meshes.Retrieve((*mesh).GetUint());
+
+        if (meshRef) this->meshes.push_back(meshRef);
+    }
+
+    if (Value* camera = FindUInt(obj, "camera")) {
+        this->camera = r.cameras.Retrieve(camera->GetUint());
+        if (this->camera)
+            this->camera->id = this->id;
+    }
+}
+
+inline void Scene::Read(Value& obj, Asset& r)
+{
+    if (Value* array = FindArray(obj, "nodes")) {
+        for (unsigned int i = 0; i < array->Size(); ++i) {
+            if (!(*array)[i].IsUint()) continue;
+            Ref<Node> node = r.nodes.Retrieve((*array)[i].GetUint());
+            if (node)
+                this->nodes.push_back(node);
+        }
+    }
+}
+
+inline void AssetMetadata::Read(Document& doc)
+{
+    if (Value* obj = FindObject(doc, "asset")) {
+        ReadMember(*obj, "copyright", copyright);
+        ReadMember(*obj, "generator", generator);
+
+        if (Value* versionString = FindString(*obj, "version")) {
+            version = versionString->GetString();
+        } else if (Value* versionNumber = FindNumber (*obj, "version")) {
+            char buf[4];
+
+            ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble());
+
+            version = buf;
+        }
+
+        if (Value* profile = FindObject(*obj, "profile")) {
+            ReadMember(*profile, "api",     this->profile.api);
+            ReadMember(*profile, "version", this->profile.version);
+        }
+    }
+
+    if (version.empty() || version[0] != '2') {
+        throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
+    }
+}
+
+//
+// Asset methods implementation
+//
+
+inline void Asset::Load(const std::string& pFile)
+{
+    mCurrentAssetDir.clear();
+    int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
+    if (pos != int(std::string::npos)) mCurrentAssetDir = pFile.substr(0, pos + 1);
+
+    shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
+    if (!stream) {
+        throw DeadlyImportError("GLTF: Could not open file for reading");
+    }
+
+    mSceneLength = stream->FileSize();
+    mBodyLength = 0;
+
+    // read the scene data
+
+    std::vector<char> sceneData(mSceneLength + 1);
+    sceneData[mSceneLength] = '\0';
+
+    if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
+        throw DeadlyImportError("GLTF: Could not read the file contents");
+    }
+
+
+    // parse the JSON document
+
+    Document doc;
+    doc.ParseInsitu(&sceneData[0]);
+
+    if (doc.HasParseError()) {
+        char buffer[32];
+        ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
+        throw DeadlyImportError(std::string("GLTF: JSON parse error, offset ") + buffer + ": "
+            + GetParseError_En(doc.GetParseError()));
+    }
+
+    if (!doc.IsObject()) {
+        throw DeadlyImportError("GLTF: JSON document root must be a JSON object");
+    }
+
+    // Fill the buffer instance for the current file embedded contents
+    if (mBodyLength > 0) {
+        if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) {
+            throw DeadlyImportError("GLTF: Unable to read gltf file");
+        }
+    }
+
+
+    // Load the metadata
+    asset.Read(doc);
+    ReadExtensionsUsed(doc);
+
+    // Prepare the dictionaries
+    for (size_t i = 0; i < mDicts.size(); ++i) {
+        mDicts[i]->AttachToDocument(doc);
+    }
+
+    // Read the "scene" property, which specifies which scene to load
+    // and recursively load everything referenced by it
+    if (Value* scene = FindUInt(doc, "scene")) {
+        unsigned int sceneIndex = scene->GetUint();
+
+        Ref<Scene> s = scenes.Retrieve(sceneIndex);
+
+        this->scene = s;
+    }
+
+    // Clean up
+    for (size_t i = 0; i < mDicts.size(); ++i) {
+        mDicts[i]->DetachFromDocument();
+    }
+}
+
+inline void Asset::ReadExtensionsUsed(Document& doc)
+{
+    Value* extsUsed = FindArray(doc, "extensionsUsed");
+    if (!extsUsed) return;
+
+    std::gltf_unordered_map<std::string, bool> exts;
+
+    for (unsigned int i = 0; i < extsUsed->Size(); ++i) {
+        if ((*extsUsed)[i].IsString()) {
+            exts[(*extsUsed)[i].GetString()] = true;
+        }
+    }
+
+    #define CHECK_EXT(EXT) \
+        if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true;
+
+    CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
+
+    #undef CHECK_EXT
+}
+
+inline IOStream* Asset::OpenFile(std::string path, const char* mode, bool absolute)
+{
+    #ifdef ASSIMP_API
+        return mIOSystem->Open(path, mode);
+    #else
+        if (path.size() < 2) return 0;
+        if (!absolute && path[1] != ':' && path[0] != '/') { // relative?
+            path = mCurrentAssetDir + path;
+        }
+        FILE* f = fopen(path.c_str(), mode);
+        return f ? new IOStream(f) : 0;
+    #endif
+}
+
+inline std::string Asset::FindUniqueID(const std::string& str, const char* suffix)
+{
+    std::string id = str;
+
+    if (!id.empty()) {
+        if (mUsedIds.find(id) == mUsedIds.end())
+            return id;
+
+        id += "_";
+    }
+
+    id += suffix;
+
+    Asset::IdMap::iterator it = mUsedIds.find(id);
+    if (it == mUsedIds.end())
+        return id;
+
+    std::vector<char> buffer;
+    buffer.resize(id.size() + 16);
+    int offset = ai_snprintf(buffer.data(), buffer.size(), "%s_", id.c_str());
+    for (int i = 0; it != mUsedIds.end(); ++i) {
+        ai_snprintf(buffer.data() + offset, buffer.size() - offset, "%d", i);
+        id = buffer.data();
+        it = mUsedIds.find(id);
+    }
+
+    return id;
+}
+
+namespace Util {
+
+    inline
+    bool ParseDataURI(const char* const_uri, size_t uriLen, DataURI& out) {
+        if ( NULL == const_uri ) {
+            return false;
+        }
+
+        if (const_uri[0] != 0x10) { // we already parsed this uri?
+            if (strncmp(const_uri, "data:", 5) != 0) // not a data uri?
+                return false;
+        }
+
+        // set defaults
+        out.mediaType = "text/plain";
+        out.charset = "US-ASCII";
+        out.base64 = false;
+
+        char* uri = const_cast<char*>(const_uri);
+        if (uri[0] != 0x10) {
+            uri[0] = 0x10;
+            uri[1] = uri[2] = uri[3] = uri[4] = 0;
+
+            size_t i = 5, j;
+            if (uri[i] != ';' && uri[i] != ',') { // has media type?
+                uri[1] = char(i);
+                for (; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
+                    // nothing to do!
+                }
+            }
+            while (uri[i] == ';' && i < uriLen) {
+                uri[i++] = '\0';
+                for (j = i; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
+                    // nothing to do!
+                }
+
+                if ( strncmp( uri + j, "charset=", 8 ) == 0 ) {
+                    uri[2] = char(j + 8);
+                } else if ( strncmp( uri + j, "base64", 6 ) == 0 ) {
+                    uri[3] = char(j);
+                }
+            }
+            if (i < uriLen) {
+                uri[i++] = '\0';
+                uri[4] = char(i);
+            } else {
+                uri[1] = uri[2] = uri[3] = 0;
+                uri[4] = 5;
+            }
+        }
+
+        if ( uri[ 1 ] != 0 ) {
+            out.mediaType = uri + uri[ 1 ];
+        }
+        if ( uri[ 2 ] != 0 ) {
+            out.charset = uri + uri[ 2 ];
+        }
+        if ( uri[ 3 ] != 0 ) {
+            out.base64 = true;
+        }
+        out.data = uri + uri[4];
+        out.dataLength = (uri + uriLen) - out.data;
+
+        return true;
+    }
+
+    template<bool B>
+    struct DATA
+    {
+        static const uint8_t tableDecodeBase64[128];
+    };
+
+    template<bool B>
+    const uint8_t DATA<B>::tableDecodeBase64[128] = {
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0, 64,  0,  0,
+         0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
+         0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0
+    };
+
+    inline char EncodeCharBase64(uint8_t b)
+    {
+        return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[size_t(b)];
+    }
+
+    inline uint8_t DecodeCharBase64(char c)
+    {
+        return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
+        /*if (c >= 'A' && c <= 'Z') return c - 'A';
+        if (c >= 'a' && c <= 'z') return c - 'a' + 26;
+        if (c >= '0' && c <= '9') return c - '0' + 52;
+        if (c == '+') return 62;
+        if (c == '/') return 63;
+        return 64; // '-' */
+    }
+
+    inline size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
+    {
+        ai_assert(inLength % 4 == 0);
+
+        if (inLength < 4) {
+            out = 0;
+            return 0;
+        }
+
+        int nEquals = int(in[inLength - 1] == '=') +
+                      int(in[inLength - 2] == '=');
+
+        size_t outLength = (inLength * 3) / 4 - nEquals;
+        out = new uint8_t[outLength];
+        memset(out, 0, outLength);
+
+        size_t i, j = 0;
+
+        for (i = 0; i + 4 < inLength; i += 4) {
+            uint8_t b0 = DecodeCharBase64(in[i]);
+            uint8_t b1 = DecodeCharBase64(in[i + 1]);
+            uint8_t b2 = DecodeCharBase64(in[i + 2]);
+            uint8_t b3 = DecodeCharBase64(in[i + 3]);
+
+            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
+            out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
+            out[j++] = (uint8_t)((b2 << 6) | b3);
+        }
+
+        {
+            uint8_t b0 = DecodeCharBase64(in[i]);
+            uint8_t b1 = DecodeCharBase64(in[i + 1]);
+            uint8_t b2 = DecodeCharBase64(in[i + 2]);
+            uint8_t b3 = DecodeCharBase64(in[i + 3]);
+
+            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
+            if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
+            if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3);
+        }
+
+        return outLength;
+    }
+
+
+
+    inline void EncodeBase64(
+        const uint8_t* in, size_t inLength,
+        std::string& out)
+    {
+        size_t outLength = ((inLength + 2) / 3) * 4;
+
+        size_t j = out.size();
+        out.resize(j + outLength);
+
+        for (size_t i = 0; i <  inLength; i += 3) {
+            uint8_t b = (in[i] & 0xFC) >> 2;
+            out[j++] = EncodeCharBase64(b);
+
+            b = (in[i] & 0x03) << 4;
+            if (i + 1 < inLength) {
+                b |= (in[i + 1] & 0xF0) >> 4;
+                out[j++] = EncodeCharBase64(b);
+
+                b = (in[i + 1] & 0x0F) << 2;
+                if (i + 2 < inLength) {
+                    b |= (in[i + 2] & 0xC0) >> 6;
+                    out[j++] = EncodeCharBase64(b);
+
+                    b = in[i + 2] & 0x3F;
+                    out[j++] = EncodeCharBase64(b);
+                }
+                else {
+                    out[j++] = EncodeCharBase64(b);
+                    out[j++] = '=';
+                }
+            }
+            else {
+                out[j++] = EncodeCharBase64(b);
+                out[j++] = '=';
+                out[j++] = '=';
+            }
+        }
+    }
+
+}
+
+} // ns glTF

+ 93 - 0
code/glTF2AssetWriter.h

@@ -0,0 +1,93 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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 glTFWriter.h
+ * Declares a class to write gltf/glb files
+ *
+ * glTF Extensions Support:
+ *   KHR_materials_pbrSpecularGlossiness: full
+ */
+#ifndef GLTF2ASSETWRITER_H_INC
+#define GLTF2ASSETWRITER_H_INC
+
+#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#include "glTF2Asset.h"
+
+namespace glTF2
+{
+
+using rapidjson::MemoryPoolAllocator;
+
+class AssetWriter
+{
+    template<class T>
+    friend void WriteLazyDict(LazyDict<T>& d, AssetWriter& w);
+
+private:
+
+    void WriteBinaryData(IOStream* outfile, size_t sceneLength);
+
+    void WriteMetadata();
+    void WriteExtensionsUsed();
+
+    template<class T>
+    void WriteObjects(LazyDict<T>& d);
+
+public:
+    Document mDoc;
+    Asset& mAsset;
+
+    MemoryPoolAllocator<>& mAl;
+
+    AssetWriter(Asset& asset);
+
+    void WriteFile(const char* path);
+};
+
+}
+
+// Include the implementation of the methods
+#include "glTF2AssetWriter.inl"
+
+#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#endif // GLTF2ASSETWRITER_H_INC

+ 639 - 0
code/glTF2AssetWriter.inl

@@ -0,0 +1,639 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/writer.h>
+#include <rapidjson/prettywriter.h>
+
+namespace glTF2 {
+
+    using rapidjson::StringBuffer;
+    using rapidjson::PrettyWriter;
+    using rapidjson::Writer;
+    using rapidjson::StringRef;
+    using rapidjson::StringRef;
+
+    namespace {
+
+        template<size_t N>
+        inline Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) {
+            val.SetArray();
+            val.Reserve(N, al);
+            for (decltype(N) i = 0; i < N; ++i) {
+                val.PushBack(r[i], al);
+            }
+            return val;
+        }
+
+        inline Value& MakeValue(Value& val, const std::vector<float> & r, MemoryPoolAllocator<>& al) {
+            val.SetArray();
+            val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
+            for (unsigned int i = 0; i < r.size(); ++i) {
+                val.PushBack(r[i], al);
+            }
+            return val;
+        }
+
+        inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& al) {
+            val.SetDouble(r);
+
+            return val;
+        }
+
+        template<class T>
+        inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref<T> >& v, MemoryPoolAllocator<>& al) {
+            if (v.empty()) return;
+            Value lst;
+            lst.SetArray();
+            lst.Reserve(unsigned(v.size()), al);
+            for (size_t i = 0; i < v.size(); ++i) {
+                lst.PushBack(v[i]->index, al);
+            }
+            obj.AddMember(StringRef(fieldId), lst, al);
+        }
+
+
+    }
+
+    inline void Write(Value& obj, Accessor& a, AssetWriter& w)
+    {
+        obj.AddMember("bufferView", a.bufferView->index, w.mAl);
+        obj.AddMember("byteOffset", a.byteOffset, w.mAl);
+
+        if (a.byteStride != 0) {
+            obj.AddMember("byteStride", a.byteStride, w.mAl);
+        }
+
+        obj.AddMember("componentType", int(a.componentType), w.mAl);
+        obj.AddMember("count", a.count, w.mAl);
+        obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
+
+        Value vTmpMax, vTmpMin;
+        obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
+        obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
+    }
+
+    inline void Write(Value& obj, Animation& a, AssetWriter& w)
+    {
+        /****************** Channels *******************/
+        Value channels;
+        channels.SetArray();
+        channels.Reserve(unsigned(a.Channels.size()), w.mAl);
+
+        for (size_t i = 0; i < unsigned(a.Channels.size()); ++i) {
+            Animation::AnimChannel& c = a.Channels[i];
+            Value valChannel;
+            valChannel.SetObject();
+            {
+                valChannel.AddMember("sampler", c.sampler, w.mAl);
+
+                Value valTarget;
+                valTarget.SetObject();
+                {
+                    valTarget.AddMember("node", c.target.node->index, w.mAl);
+                    valTarget.AddMember("path", c.target.path, w.mAl);
+                }
+                valChannel.AddMember("target", valTarget, w.mAl);
+            }
+            channels.PushBack(valChannel, w.mAl);
+        }
+        obj.AddMember("channels", channels, w.mAl);
+
+        /****************** Samplers *******************/
+        Value valSamplers;
+        valSamplers.SetArray();
+
+        for (size_t i = 0; i < unsigned(a.Samplers.size()); ++i) {
+            Animation::AnimSampler& s = a.Samplers[i];
+            Value valSampler;
+            valSampler.SetObject();
+            {
+                Ref<Accessor> inputAccessor = a.GetAccessor(s.input);
+                Ref<Accessor> outputAccessor = a.GetAccessor(s.output);
+                valSampler.AddMember("input", inputAccessor->index, w.mAl);
+                valSampler.AddMember("interpolation", s.interpolation, w.mAl);
+                valSampler.AddMember("output", outputAccessor->index, w.mAl);
+            }
+            valSamplers.PushBack(valSampler, w.mAl);
+        }
+        obj.AddMember("samplers", valSamplers, w.mAl);
+    }
+
+    inline void Write(Value& obj, Buffer& b, AssetWriter& w)
+    {
+        obj.AddMember("byteLength", static_cast<uint64_t>(b.byteLength), w.mAl);
+        obj.AddMember("uri", Value(b.GetURI(), w.mAl).Move(), w.mAl);
+    }
+
+    inline void Write(Value& obj, BufferView& bv, AssetWriter& w)
+    {
+        obj.AddMember("buffer", bv.buffer->index, w.mAl);
+        obj.AddMember("byteOffset", static_cast<uint64_t>(bv.byteOffset), w.mAl);
+        obj.AddMember("byteLength", static_cast<uint64_t>(bv.byteLength), w.mAl);
+        obj.AddMember("target", int(bv.target), w.mAl);
+    }
+
+    inline void Write(Value& obj, Camera& c, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Image& img, AssetWriter& w)
+    {
+        std::string uri;
+        if (img.HasData()) {
+            uri = "data:" + (img.mimeType.empty() ? "application/octet-stream" : img.mimeType);
+            uri += ";base64,";
+            Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri);
+        }
+        else {
+            uri = img.uri;
+        }
+
+        obj.AddMember("uri", Value(uri, w.mAl).Move(), w.mAl);
+    }
+
+    namespace {
+        inline void SetTexBasic(TextureInfo t, Value& tex, MemoryPoolAllocator<>& al)
+        {
+            tex.SetObject();
+            tex.AddMember("index", t.texture->index, al);
+
+            if (t.texCoord != 0) {
+                tex.AddMember("texCoord", t.texCoord, al);
+            }
+        }
+
+        inline void WriteTex(Value& obj, TextureInfo t, const char* propName, MemoryPoolAllocator<>& al)
+        {
+
+            if (t.texture) {
+                Value tex;
+
+                SetTexBasic(t, tex, al);
+
+                obj.AddMember(StringRef(propName), tex, al);
+            }
+        }
+
+        inline void WriteTex(Value& obj, NormalTextureInfo t, const char* propName, MemoryPoolAllocator<>& al)
+        {
+
+            if (t.texture) {
+                Value tex;
+
+                SetTexBasic(t, tex, al);
+
+                if (t.scale != 1) {
+                    tex.AddMember("scale", t.scale, al);
+                }
+
+                obj.AddMember(StringRef(propName), tex, al);
+            }
+        }
+
+        inline void WriteTex(Value& obj, OcclusionTextureInfo t, const char* propName, MemoryPoolAllocator<>& al)
+        {
+
+            if (t.texture) {
+                Value tex;
+
+                SetTexBasic(t, tex, al);
+
+                if (t.strength != 1) {
+                    tex.AddMember("strength", t.strength, al);
+                }
+
+                obj.AddMember(StringRef(propName), tex, al);
+            }
+        }
+
+        template<size_t N>
+        inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, MemoryPoolAllocator<>& al)
+        {
+            Value arr;
+            obj.AddMember(StringRef(propName), MakeValue(arr, prop, al), al);
+        }
+
+        template<size_t N>
+        inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, const float(&defaultVal)[N], MemoryPoolAllocator<>& al)
+        {
+            if (!std::equal(std::begin(prop), std::end(prop), std::begin(defaultVal))) {
+                WriteVec(obj, prop, propName, al);
+            }
+        }
+
+        inline void WriteFloat(Value& obj, float prop, const char* propName, MemoryPoolAllocator<>& al)
+        {
+            Value num;
+            obj.AddMember(StringRef(propName), MakeValue(num, prop, al), al);
+        }
+    }
+
+    inline void Write(Value& obj, Material& m, AssetWriter& w)
+    {
+        Value pbrMetallicRoughness;
+        pbrMetallicRoughness.SetObject();
+        {
+            WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorTexture, "baseColorTexture", w.mAl);
+            WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicRoughnessTexture, "metallicRoughnessTexture", w.mAl);
+            WriteVec(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorFactor, "baseColorFactor", defaultBaseColor, w.mAl);
+
+            if (m.pbrMetallicRoughness.metallicFactor != 1) {
+                WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicFactor, "metallicFactor", w.mAl);
+            }
+
+            if (m.pbrMetallicRoughness.roughnessFactor != 1) {
+                WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.roughnessFactor, "roughnessFactor", w.mAl);
+            }
+        }
+
+        if (!pbrMetallicRoughness.ObjectEmpty()) {
+            obj.AddMember("pbrMetallicRoughness", pbrMetallicRoughness, w.mAl);
+        }
+
+        WriteTex(obj, m.normalTexture, "normalTexture", w.mAl);
+        WriteTex(obj, m.emissiveTexture, "emissiveTexture", w.mAl);
+        WriteTex(obj, m.occlusionTexture, "occlusionTexture", w.mAl);
+        WriteVec(obj, m.emissiveFactor, "emissiveFactor", defaultEmissiveFactor, w.mAl);
+
+        if (m.alphaCutoff != 0.5) {
+            WriteFloat(obj, m.alphaCutoff, "alphaCutoff", w.mAl);
+        }
+
+        if (m.alphaMode != "OPAQUE") {
+            obj.AddMember("alphaMode", Value(m.alphaMode, w.mAl).Move(), w.mAl);
+        }
+
+        if (m.doubleSided) {
+            obj.AddMember("doubleSided", m.doubleSided, w.mAl);
+        }
+
+        Value exts;
+        exts.SetObject();
+
+        if (m.pbrSpecularGlossiness.isPresent) {
+            Value pbrSpecularGlossiness;
+            pbrSpecularGlossiness.SetObject();
+
+            PbrSpecularGlossiness &pbrSG = m.pbrSpecularGlossiness.value;
+
+            //pbrSpecularGlossiness
+            WriteVec(pbrSpecularGlossiness, pbrSG.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl);
+            WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl);
+
+            if (pbrSG.glossinessFactor != 1) {
+                WriteFloat(obj, pbrSG.glossinessFactor, "glossinessFactor", w.mAl);
+            }
+
+            WriteTex(pbrSpecularGlossiness, pbrSG.diffuseTexture, "diffuseTexture", w.mAl);
+            WriteTex(pbrSpecularGlossiness, pbrSG.specularGlossinessTexture, "specularGlossinessTexture", w.mAl);
+
+            if (!pbrSpecularGlossiness.ObjectEmpty()) {
+                exts.AddMember("KHR_materials_pbrSpecularGlossiness", pbrSpecularGlossiness, w.mAl);
+            }
+        }
+
+        if (!exts.ObjectEmpty()) {
+            obj.AddMember("extensions", exts, w.mAl);
+        }
+    }
+
+    namespace {
+        inline void WriteAttrs(AssetWriter& w, Value& attrs, Mesh::AccessorList& lst,
+            const char* semantic, bool forceNumber = false)
+        {
+            if (lst.empty()) return;
+            if (lst.size() == 1 && !forceNumber) {
+                attrs.AddMember(StringRef(semantic), lst[0]->index, w.mAl);
+            }
+            else {
+                for (size_t i = 0; i < lst.size(); ++i) {
+                    char buffer[32];
+                    ai_snprintf(buffer, 32, "%s_%d", semantic, int(i));
+                    attrs.AddMember(Value(buffer, w.mAl).Move(), lst[i]->index, w.mAl);
+                }
+            }
+        }
+    }
+
+    inline void Write(Value& obj, Mesh& m, AssetWriter& w)
+    {
+		/****************** Primitives *******************/
+        Value primitives;
+        primitives.SetArray();
+        primitives.Reserve(unsigned(m.primitives.size()), w.mAl);
+
+        for (size_t i = 0; i < m.primitives.size(); ++i) {
+            Mesh::Primitive& p = m.primitives[i];
+            Value prim;
+            prim.SetObject();
+            {
+                prim.AddMember("mode", Value(int(p.mode)).Move(), w.mAl);
+
+                if (p.material)
+                    prim.AddMember("material", p.material->index, w.mAl);
+
+                if (p.indices)
+                    prim.AddMember("indices", p.indices->index, w.mAl);
+
+                Value attrs;
+                attrs.SetObject();
+                {
+                    WriteAttrs(w, attrs, p.attributes.position, "POSITION");
+                    WriteAttrs(w, attrs, p.attributes.normal, "NORMAL");
+                    WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD", true);
+                    WriteAttrs(w, attrs, p.attributes.color, "COLOR", true);
+                    WriteAttrs(w, attrs, p.attributes.joint, "JOINTS", true);
+                    WriteAttrs(w, attrs, p.attributes.weight, "WEIGHTS", true);
+                }
+                prim.AddMember("attributes", attrs, w.mAl);
+            }
+            primitives.PushBack(prim, w.mAl);
+        }
+
+        obj.AddMember("primitives", primitives, w.mAl);
+    }
+
+    inline void Write(Value& obj, Node& n, AssetWriter& w)
+    {
+
+        if (n.matrix.isPresent) {
+            Value val;
+            obj.AddMember("matrix", MakeValue(val, n.matrix.value, w.mAl).Move(), w.mAl);
+        }
+
+        if (n.translation.isPresent) {
+            Value val;
+            obj.AddMember("translation", MakeValue(val, n.translation.value, w.mAl).Move(), w.mAl);
+        }
+
+        if (n.scale.isPresent) {
+            Value val;
+            obj.AddMember("scale", MakeValue(val, n.scale.value, w.mAl).Move(), w.mAl);
+        }
+        if (n.rotation.isPresent) {
+            Value val;
+            obj.AddMember("rotation", MakeValue(val, n.rotation.value, w.mAl).Move(), w.mAl);
+        }
+
+        AddRefsVector(obj, "children", n.children, w.mAl);
+
+        if (!n.meshes.empty()) {
+            obj.AddMember("mesh", n.meshes[0]->index, w.mAl);
+        }
+
+        AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
+
+        if (n.skin) {
+            obj.AddMember("skin", n.skin->index, w.mAl);
+        }
+
+        if (!n.jointName.empty()) {
+          obj.AddMember("jointName", n.jointName, w.mAl);
+        }
+    }
+
+    inline void Write(Value& obj, Program& b, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Sampler& b, AssetWriter& w)
+    {
+        if (!b.name.empty()) {
+            obj.AddMember("name", b.name, w.mAl);
+        }
+
+        if (b.wrapS != SamplerWrap::UNSET && b.wrapS != SamplerWrap::Repeat) {
+            obj.AddMember("wrapS", static_cast<unsigned int>(b.wrapS), w.mAl);
+        }
+
+        if (b.wrapT != SamplerWrap::UNSET && b.wrapT != SamplerWrap::Repeat) {
+            obj.AddMember("wrapT", static_cast<unsigned int>(b.wrapT), w.mAl);
+        }
+
+        if (b.magFilter != SamplerMagFilter::UNSET) {
+            obj.AddMember("magFilter", static_cast<unsigned int>(b.magFilter), w.mAl);
+        }
+
+        if (b.minFilter != SamplerMinFilter::UNSET) {
+            obj.AddMember("minFilter", static_cast<unsigned int>(b.minFilter), w.mAl);
+        }
+    }
+
+    inline void Write(Value& scene, Scene& s, AssetWriter& w)
+    {
+        AddRefsVector(scene, "nodes", s.nodes, w.mAl);
+    }
+
+    inline void Write(Value& obj, Shader& b, AssetWriter& w)
+    {
+
+    }
+
+    inline void Write(Value& obj, Skin& b, AssetWriter& w)
+    {
+        /****************** jointNames *******************/
+        Value vJointNames;
+        vJointNames.SetArray();
+        vJointNames.Reserve(unsigned(b.jointNames.size()), w.mAl);
+
+        for (size_t i = 0; i < unsigned(b.jointNames.size()); ++i) {
+            vJointNames.PushBack(b.jointNames[i]->index, w.mAl);
+        }
+        obj.AddMember("joints", vJointNames, w.mAl);
+
+        if (b.bindShapeMatrix.isPresent) {
+            Value val;
+            obj.AddMember("bindShapeMatrix", MakeValue(val, b.bindShapeMatrix.value, w.mAl).Move(), w.mAl);
+        }
+
+        if (b.inverseBindMatrices) {
+            obj.AddMember("inverseBindMatrices", b.inverseBindMatrices->index, w.mAl);
+        }
+
+    }
+
+    inline void Write(Value& obj, Texture& tex, AssetWriter& w)
+    {
+        if (tex.source) {
+            obj.AddMember("source", tex.source->index, w.mAl);
+        }
+        if (tex.sampler) {
+            obj.AddMember("sampler", tex.sampler->index, w.mAl);
+        }
+    }
+
+
+    inline AssetWriter::AssetWriter(Asset& a)
+        : mDoc()
+        , mAsset(a)
+        , mAl(mDoc.GetAllocator())
+    {
+        mDoc.SetObject();
+
+        WriteMetadata();
+        WriteExtensionsUsed();
+
+        // Dump the contents of the dictionaries
+        for (size_t i = 0; i < a.mDicts.size(); ++i) {
+            a.mDicts[i]->WriteObjects(*this);
+        }
+
+        // Add the target scene field
+        if (mAsset.scene) {
+            mDoc.AddMember("scene", mAsset.scene->index, mAl);
+        }
+    }
+
+    inline void AssetWriter::WriteFile(const char* path)
+    {
+        std::unique_ptr<IOStream> jsonOutFile(mAsset.OpenFile(path, "wt", true));
+
+        if (jsonOutFile == 0) {
+            throw DeadlyExportError("Could not open output file: " + std::string(path));
+        }
+
+        StringBuffer docBuffer;
+
+        PrettyWriter<StringBuffer> writer(docBuffer);
+        mDoc.Accept(writer);
+
+        if (jsonOutFile->Write(docBuffer.GetString(), docBuffer.GetSize(), 1) != 1) {
+            throw DeadlyExportError("Failed to write scene data!");
+        }
+
+        // Write buffer data to separate .bin files
+        for (unsigned int i = 0; i < mAsset.buffers.Size(); ++i) {
+            Ref<Buffer> b = mAsset.buffers.Get(i);
+
+            std::string binPath = b->GetURI();
+
+            std::unique_ptr<IOStream> binOutFile(mAsset.OpenFile(binPath, "wb", true));
+
+            if (binOutFile == 0) {
+                throw DeadlyExportError("Could not open output file: " + binPath);
+            }
+
+            if (b->byteLength > 0) {
+                if (binOutFile->Write(b->GetPointer(), b->byteLength, 1) != 1) {
+                    throw DeadlyExportError("Failed to write binary file: " + binPath);
+                }
+            }
+        }
+    }
+
+    inline void AssetWriter::WriteMetadata()
+    {
+        Value asset;
+        asset.SetObject();
+        asset.AddMember("version", Value(mAsset.asset.version, mAl).Move(), mAl);
+        asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
+        mDoc.AddMember("asset", asset, mAl);
+    }
+
+    inline void AssetWriter::WriteExtensionsUsed()
+    {
+        Value exts;
+        exts.SetArray();
+        {
+            // This is used to export pbrSpecularGlossiness materials with GLTF 2.
+            if (this->mAsset.extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
+                exts.PushBack(StringRef("KHR_materials_pbrSpecularGlossiness"), mAl);
+            }
+        }
+
+        if (!exts.Empty())
+            mDoc.AddMember("extensionsUsed", exts, mAl);
+    }
+
+    template<class T>
+    void AssetWriter::WriteObjects(LazyDict<T>& d)
+    {
+        if (d.mObjs.empty()) return;
+
+        Value* container = &mDoc;
+
+        if (d.mExtId) {
+            Value* exts = FindObject(mDoc, "extensions");
+            if (!exts) {
+                mDoc.AddMember("extensions", Value().SetObject().Move(), mDoc.GetAllocator());
+                exts = FindObject(mDoc, "extensions");
+            }
+
+            if (!(container = FindObject(*exts, d.mExtId))) {
+                exts->AddMember(StringRef(d.mExtId), Value().SetObject().Move(), mDoc.GetAllocator());
+                container = FindObject(*exts, d.mExtId);
+            }
+        }
+
+        Value* dict;
+        if (!(dict = FindArray(*container, d.mDictId))) {
+            container->AddMember(StringRef(d.mDictId), Value().SetArray().Move(), mDoc.GetAllocator());
+            dict = FindArray(*container, d.mDictId);
+        }
+
+        for (size_t i = 0; i < d.mObjs.size(); ++i) {
+            if (d.mObjs[i]->IsSpecial()) continue;
+
+            Value obj;
+            obj.SetObject();
+
+            if (!d.mObjs[i]->name.empty()) {
+                obj.AddMember("name", StringRef(d.mObjs[i]->name.c_str()), mAl);
+            }
+
+            Write(obj, *d.mObjs[i], *this);
+
+            dict->PushBack(obj, mAl);
+        }
+    }
+
+    template<class T>
+    void WriteLazyDict(LazyDict<T>& d, AssetWriter& w)
+    {
+        w.WriteObjects(d);
+    }
+
+}
+
+

+ 1007 - 0
code/glTF2Exporter.cpp

@@ -0,0 +1,1007 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
+
+#include "glTF2Exporter.h"
+
+#include "Exceptional.h"
+#include "StringComparison.h"
+#include "ByteSwapper.h"
+
+#include "SplitLargeMeshes.h"
+
+#include <assimp/SceneCombiner.h>
+#include <assimp/version.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/material.h>
+#include <assimp/scene.h>
+
+// Header files, standart library.
+#include <memory>
+#include <inttypes.h>
+
+#include "glTF2AssetWriter.h"
+
+using namespace rapidjson;
+
+using namespace Assimp;
+using namespace glTF2;
+
+namespace Assimp {
+
+    // ------------------------------------------------------------------------------------------------
+    // Worker function for exporting a scene to GLTF. Prototyped and registered in Exporter.cpp
+    void ExportSceneGLTF2(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
+    {
+        // invoke the exporter
+        glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, false);
+    }
+
+} // end of namespace Assimp
+
+glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene,
+                           const ExportProperties* pProperties, bool isBinary)
+    : mFilename(filename)
+    , mIOSystem(pIOSystem)
+    , mProperties(pProperties)
+{
+    aiScene* sceneCopy_tmp;
+    SceneCombiner::CopyScene(&sceneCopy_tmp, pScene);
+    std::unique_ptr<aiScene> sceneCopy(sceneCopy_tmp);
+
+    SplitLargeMeshesProcess_Triangle tri_splitter;
+    tri_splitter.SetLimit(0xffff);
+    tri_splitter.Execute(sceneCopy.get());
+
+    SplitLargeMeshesProcess_Vertex vert_splitter;
+    vert_splitter.SetLimit(0xffff);
+    vert_splitter.Execute(sceneCopy.get());
+
+    mScene = sceneCopy.get();
+
+    mAsset.reset( new Asset( pIOSystem ) );
+
+    ExportMetadata();
+
+    ExportMaterials();
+
+    if (mScene->mRootNode) {
+        ExportNodeHierarchy(mScene->mRootNode);
+    }
+
+    ExportMeshes();
+    MergeMeshes();
+
+    ExportScene();
+
+    ExportAnimations();
+
+    AssetWriter writer(*mAsset);
+
+    writer.WriteFile(filename);
+}
+
+/*
+ * Copy a 4x4 matrix from struct aiMatrix to typedef mat4.
+ * Also converts from row-major to column-major storage.
+ */
+static void CopyValue(const aiMatrix4x4& v, mat4& o)
+{
+    o[ 0] = v.a1; o[ 1] = v.b1; o[ 2] = v.c1; o[ 3] = v.d1;
+    o[ 4] = v.a2; o[ 5] = v.b2; o[ 6] = v.c2; o[ 7] = v.d2;
+    o[ 8] = v.a3; o[ 9] = v.b3; o[10] = v.c3; o[11] = v.d3;
+    o[12] = v.a4; o[13] = v.b4; o[14] = v.c4; o[15] = v.d4;
+}
+
+static void CopyValue(const aiMatrix4x4& v, aiMatrix4x4& o)
+{
+    o.a1 = v.a1; o.a2 = v.a2; o.a3 = v.a3; o.a4 = v.a4;
+    o.b1 = v.b1; o.b2 = v.b2; o.b3 = v.b3; o.b4 = v.b4;
+    o.c1 = v.c1; o.c2 = v.c2; o.c3 = v.c3; o.c4 = v.c4;
+    o.d1 = v.d1; o.d2 = v.d2; o.d3 = v.d3; o.d4 = v.d4;
+}
+
+static void IdentityMatrix4(mat4& o)
+{
+    o[ 0] = 1; o[ 1] = 0; o[ 2] = 0; o[ 3] = 0;
+    o[ 4] = 0; o[ 5] = 1; o[ 6] = 0; o[ 7] = 0;
+    o[ 8] = 0; o[ 9] = 0; o[10] = 1; o[11] = 0;
+    o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1;
+}
+
+inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
+    unsigned int count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false)
+{
+    if (!count || !data) return Ref<Accessor>();
+
+    unsigned int numCompsIn = AttribType::GetNumComponents(typeIn);
+    unsigned int numCompsOut = AttribType::GetNumComponents(typeOut);
+    unsigned int bytesPerComp = ComponentTypeSize(compType);
+
+    size_t offset = buffer->byteLength;
+    // make sure offset is correctly byte-aligned, as required by spec
+    size_t padding = offset % bytesPerComp;
+    offset += padding;
+    size_t length = count * numCompsOut * bytesPerComp;
+    buffer->Grow(length + padding);
+
+    // bufferView
+    Ref<BufferView> bv = a.bufferViews.Create(a.FindUniqueID(meshName, "view"));
+    bv->buffer = buffer;
+    bv->byteOffset = unsigned(offset);
+    bv->byteLength = length; //! The target that the WebGL buffer should be bound to.
+    bv->target = isIndices ? BufferViewTarget_ELEMENT_ARRAY_BUFFER : BufferViewTarget_ARRAY_BUFFER;
+
+    // accessor
+    Ref<Accessor> acc = a.accessors.Create(a.FindUniqueID(meshName, "accessor"));
+    acc->bufferView = bv;
+    acc->byteOffset = 0;
+    acc->byteStride = 0;
+    acc->componentType = compType;
+    acc->count = count;
+    acc->type = typeOut;
+
+    // calculate min and max values
+    {
+        // Allocate and initialize with large values.
+        float float_MAX = 10000000000000.0f;
+        for (unsigned int i = 0 ; i < numCompsOut ; i++) {
+            acc->min.push_back( float_MAX);
+            acc->max.push_back(-float_MAX);
+        }
+
+        // Search and set extreme values.
+        float valueTmp;
+        for (unsigned int i = 0 ; i < count       ; i++) {
+            for (unsigned int j = 0 ; j < numCompsOut ; j++) {
+                if (numCompsOut == 1) {
+                  valueTmp = static_cast<unsigned short*>(data)[i];
+                } else {
+                  valueTmp = static_cast<aiVector3D*>(data)[i][j];
+                }
+
+                if (valueTmp < acc->min[j]) {
+                    acc->min[j] = valueTmp;
+                }
+                if (valueTmp > acc->max[j]) {
+                    acc->max[j] = valueTmp;
+                }
+            }
+        }
+    }
+
+    // copy the data
+    acc->WriteData(count, data, numCompsIn*bytesPerComp);
+
+    return acc;
+}
+
+inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map)
+{
+    switch (map) {
+        case aiTextureMapMode_Clamp:
+            wrap = SamplerWrap::Clamp_To_Edge;
+            break;
+        case aiTextureMapMode_Mirror:
+            wrap = SamplerWrap::Mirrored_Repeat;
+            break;
+        case aiTextureMapMode_Wrap:
+        case aiTextureMapMode_Decal:
+        default:
+            wrap = SamplerWrap::Repeat;
+            break;
+    };
+}
+
+void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref<Texture> texture, aiTextureType tt, unsigned int slot)
+{
+    aiString aId;
+    std::string id;
+    if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) {
+        id = aId.C_Str();
+    }
+
+    if (Ref<Sampler> ref = mAsset->samplers.Get(id.c_str())) {
+        texture->sampler = ref;
+    } else {
+        id = mAsset->FindUniqueID(id, "sampler");
+
+        texture->sampler = mAsset->samplers.Create(id.c_str());
+
+        aiTextureMapMode mapU, mapV;
+        SamplerMagFilter filterMag;
+        SamplerMinFilter filterMin;
+
+        if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) {
+            SetSamplerWrap(texture->sampler->wrapS, mapU);
+        }
+
+        if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) {
+            SetSamplerWrap(texture->sampler->wrapT, mapV);
+        }
+
+        if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) {
+            texture->sampler->magFilter = filterMag;
+        }
+
+        if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) {
+            texture->sampler->minFilter = filterMin;
+        }
+
+        aiString name;
+        if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) {
+            texture->sampler->name = name.C_Str();
+        }
+    }
+}
+
+void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot)
+{
+    std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
+
+    mat->Get(textureKey.c_str(), tt, slot, prop);
+}
+
+void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot)
+{
+    std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
+
+    mat->Get(textureKey.c_str(), tt, slot, prop);
+}
+
+void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTextureType tt, unsigned int slot = 0)
+{
+
+    if (mat->GetTextureCount(tt) > 0) {
+        aiString tex;
+
+        if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) {
+            std::string path = tex.C_Str();
+
+            if (path.size() > 0) {
+                if (path[0] != '*') {
+                    std::map<std::string, unsigned int>::iterator it = mTexturesByPath.find(path);
+                    if (it != mTexturesByPath.end()) {
+                        texture = mAsset->textures.Get(it->second);
+                    }
+                }
+
+                if (!texture) {
+                    std::string texId = mAsset->FindUniqueID("", "texture");
+                    texture = mAsset->textures.Create(texId);
+                    mTexturesByPath[path] = texture.GetIndex();
+
+                    std::string imgId = mAsset->FindUniqueID("", "image");
+                    texture->source = mAsset->images.Create(imgId);
+
+                    if (path[0] == '*') { // embedded
+                        aiTexture* tex = mScene->mTextures[atoi(&path[1])];
+
+                        uint8_t* data = reinterpret_cast<uint8_t*>(tex->pcData);
+                        texture->source->SetData(data, tex->mWidth, *mAsset);
+
+                        if (tex->achFormatHint[0]) {
+                            std::string mimeType = "image/";
+                            mimeType += (memcmp(tex->achFormatHint, "jpg", 3) == 0) ? "jpeg" : tex->achFormatHint;
+                            texture->source->mimeType = mimeType;
+                        }
+                    }
+                    else {
+                        texture->source->uri = path;
+                    }
+
+                    GetTexSampler(mat, texture, tt, slot);
+                }
+            }
+        }
+    }
+}
+
+void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
+{
+    Ref<Texture>& texture = prop.texture;
+
+    GetMatTex(mat, texture, tt, slot);
+
+    if (texture) {
+        GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
+    }
+}
+
+void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
+{
+    Ref<Texture>& texture = prop.texture;
+
+    GetMatTex(mat, texture, tt, slot);
+
+    if (texture) {
+        GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
+        GetMatTexProp(mat, prop.scale, "scale", tt, slot);
+    }
+}
+
+void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0)
+{
+    Ref<Texture>& texture = prop.texture;
+
+    GetMatTex(mat, texture, tt, slot);
+
+    if (texture) {
+        GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
+        GetMatTexProp(mat, prop.strength, "strength", tt, slot);
+    }
+}
+
+void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx)
+{
+    aiColor4D col;
+    if (mat->Get(propName, type, idx, col) == AI_SUCCESS) {
+        prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a;
+    }
+}
+
+void glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx)
+{
+    aiColor3D col;
+    if (mat->Get(propName, type, idx, col) == AI_SUCCESS) {
+        prop[0] = col.r; prop[1] = col.g; prop[2] = col.b;
+    }
+}
+
+void glTF2Exporter::ExportMaterials()
+{
+    aiString aiName;
+    for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
+        const aiMaterial* mat = mScene->mMaterials[i];
+
+        std::string id = "material_" + std::to_string(i);
+
+        Ref<Material> m = mAsset->materials.Create(id);
+
+        std::string name;
+        if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) {
+            name = aiName.C_Str();
+        }
+        name = mAsset->FindUniqueID(name, "material");
+
+        m->name = name;
+
+        GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE);
+        GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
+        GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
+
+        if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
+            //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
+            m->pbrMetallicRoughness.metallicFactor = 0;
+        }
+
+        mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor);
+
+        GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS);
+        GetMatTex(mat, m->occlusionTexture, aiTextureType_LIGHTMAP);
+        GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE);
+        GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE);
+
+        mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided);
+        mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff);
+
+        aiString alphaMode;
+
+        if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) {
+            m->alphaMode = alphaMode.C_Str();
+        } else {
+            float opacity;
+
+            if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) {
+                if (opacity < 1) {
+                    m->alphaMode = "MASK";
+                    m->pbrMetallicRoughness.baseColorFactor[3] *= opacity;
+                }
+            }
+        }
+
+        bool hasPbrSpecularGlossiness = false;
+        mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness);
+
+        if (hasPbrSpecularGlossiness) {
+
+            if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
+                mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
+            }
+
+            PbrSpecularGlossiness pbrSG;
+
+            GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR);
+            GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR);
+            mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor);
+            GetMatTex(mat, pbrSG.diffuseTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE);
+            GetMatTex(mat, pbrSG.specularGlossinessTexture, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE);
+
+            m->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
+        }
+    }
+}
+
+/*
+ * Search through node hierarchy and find the node containing the given meshID.
+ * Returns true on success, and false otherwise.
+ */
+bool FindMeshNode(Ref<Node>& nodeIn, Ref<Node>& meshNode, std::string meshID)
+{
+    for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) {
+        if (meshID.compare(nodeIn->meshes[i]->id) == 0) {
+            meshNode = nodeIn;
+            return true;
+        }
+    }
+
+    for (unsigned int i = 0; i < nodeIn->children.size(); ++i) {
+        if(FindMeshNode(nodeIn->children[i], meshNode, meshID)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/*
+ * Find the root joint of the skeleton.
+ * Starts will any joint node and traces up the tree,
+ * until a parent is found that does not have a jointName.
+ * Returns the first parent Ref<Node> found that does not have a jointName.
+ */
+Ref<Node> FindSkeletonRootJoint(Ref<Skin>& skinRef)
+{
+    Ref<Node> startNodeRef;
+    Ref<Node> parentNodeRef;
+
+    // Arbitrarily use the first joint to start the search.
+    startNodeRef = skinRef->jointNames[0];
+    parentNodeRef = skinRef->jointNames[0];
+
+    do {
+        startNodeRef = parentNodeRef;
+        parentNodeRef = startNodeRef->parent;
+    } while (!parentNodeRef->jointName.empty());
+
+    return parentNodeRef;
+}
+
+void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buffer>& bufferRef, Ref<Skin>& skinRef, std::vector<aiMatrix4x4>& inverseBindMatricesData)
+{
+    if (aimesh->mNumBones < 1) {
+        return;
+    }
+
+    // Store the vertex joint and weight data.
+    const size_t NumVerts( aimesh->mNumVertices );
+    vec4* vertexJointData = new vec4[ NumVerts ];
+    vec4* vertexWeightData = new vec4[ NumVerts ];
+    int* jointsPerVertex = new int[ NumVerts ];
+    for (size_t i = 0; i < NumVerts; ++i) {
+        jointsPerVertex[i] = 0;
+        for (size_t j = 0; j < 4; ++j) {
+            vertexJointData[i][j] = 0;
+            vertexWeightData[i][j] = 0;
+        }
+    }
+
+    for (unsigned int idx_bone = 0; idx_bone < aimesh->mNumBones; ++idx_bone) {
+        const aiBone* aib = aimesh->mBones[idx_bone];
+
+        // aib->mName   =====>  skinRef->jointNames
+        // Find the node with id = mName.
+        Ref<Node> nodeRef = mAsset.nodes.Get(aib->mName.C_Str());
+        nodeRef->jointName = nodeRef->name;
+
+        unsigned int jointNamesIndex = 0;
+        bool addJointToJointNames = true;
+        for ( unsigned int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) {
+            if (skinRef->jointNames[idx_joint]->jointName.compare(nodeRef->jointName) == 0) {
+                addJointToJointNames = false;
+                jointNamesIndex = idx_joint;
+            }
+        }
+
+        if (addJointToJointNames) {
+            skinRef->jointNames.push_back(nodeRef);
+
+            // aib->mOffsetMatrix   =====>  skinRef->inverseBindMatrices
+            aiMatrix4x4 tmpMatrix4;
+            CopyValue(aib->mOffsetMatrix, tmpMatrix4);
+            inverseBindMatricesData.push_back(tmpMatrix4);
+            jointNamesIndex = static_cast<unsigned int>(inverseBindMatricesData.size() - 1);
+        }
+
+        // aib->mWeights   =====>  vertexWeightData
+        for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights; ++idx_weights) {
+            unsigned int vertexId = aib->mWeights[idx_weights].mVertexId;
+            float vertWeight      = aib->mWeights[idx_weights].mWeight;
+
+            // A vertex can only have at most four joint weights. Ignore all others.
+            if (jointsPerVertex[vertexId] > 3) {
+                continue;
+            }
+
+            vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex);
+            vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
+
+            jointsPerVertex[vertexId] += 1;
+        }
+
+    } // End: for-loop mNumMeshes
+
+    Mesh::Primitive& p = meshRef->primitives.back();
+    Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices, vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
+    if ( vertexJointAccessor ) {
+        p.attributes.joint.push_back( vertexJointAccessor );
+    }
+
+    Ref<Accessor> vertexWeightAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices, vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
+    if ( vertexWeightAccessor ) {
+        p.attributes.weight.push_back( vertexWeightAccessor );
+    }
+    delete[] jointsPerVertex;
+    delete[] vertexWeightData;
+    delete[] vertexJointData;
+}
+
+void glTF2Exporter::ExportMeshes()
+{
+    // Not for
+    //     using IndicesType = decltype(aiFace::mNumIndices);
+    // But yes for
+    //     using IndicesType = unsigned short;
+    // because "ComponentType_UNSIGNED_SHORT" used for indices. And it's a maximal type according to glTF specification.
+    typedef unsigned short IndicesType;
+
+    std::string fname = std::string(mFilename);
+    std::string bufferIdPrefix = fname.substr(0, fname.rfind(".gltf"));
+    std::string bufferId = mAsset->FindUniqueID("", bufferIdPrefix.c_str());
+
+    Ref<Buffer> b = mAsset->GetBodyBuffer();
+    if (!b) {
+       b = mAsset->buffers.Create(bufferId);
+    }
+
+    //----------------------------------------
+    // Initialize variables for the skin
+    bool createSkin = false;
+    for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
+        const aiMesh* aim = mScene->mMeshes[idx_mesh];
+        if(aim->HasBones()) {
+            createSkin = true;
+            break;
+        }
+    }
+
+    Ref<Skin> skinRef;
+    std::string skinName = mAsset->FindUniqueID("skin", "skin");
+    std::vector<aiMatrix4x4> inverseBindMatricesData;
+    if(createSkin) {
+        skinRef = mAsset->skins.Create(skinName);
+        skinRef->name = skinName;
+    }
+    //----------------------------------------
+
+	for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
+		const aiMesh* aim = mScene->mMeshes[idx_mesh];
+
+        std::string name = aim->mName.C_Str();
+
+        std::string meshId = mAsset->FindUniqueID(name, "mesh");
+        Ref<Mesh> m = mAsset->meshes.Create(meshId);
+        m->primitives.resize(1);
+        Mesh::Primitive& p = m->primitives.back();
+
+        m->name = name;
+
+        p.material = mAsset->materials.Get(aim->mMaterialIndex);
+
+		/******************* Vertices ********************/
+        Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+		if (v) p.attributes.position.push_back(v);
+
+		/******************** Normals ********************/
+		Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+		if (n) p.attributes.normal.push_back(n);
+
+		/************** Texture coordinates **************/
+        for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+            // Flip UV y coords
+            if (aim -> mNumUVComponents[i] > 1) {
+                for (unsigned int j = 0; j < aim->mNumVertices; ++j) {
+                    aim->mTextureCoords[i][j].y = 1 - aim->mTextureCoords[i][j].y;
+                }
+            }
+
+            if (aim->mNumUVComponents[i] > 0) {
+                AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3;
+
+				Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i], AttribType::VEC3, type, ComponentType_FLOAT, false);
+				if (tc) p.attributes.texcoord.push_back(tc);
+			}
+		}
+
+		/*************** Vertices indices ****************/
+		if (aim->mNumFaces > 0) {
+			std::vector<IndicesType> indices;
+			unsigned int nIndicesPerFace = aim->mFaces[0].mNumIndices;
+            indices.resize(aim->mNumFaces * nIndicesPerFace);
+            for (size_t i = 0; i < aim->mNumFaces; ++i) {
+                for (size_t j = 0; j < nIndicesPerFace; ++j) {
+                    indices[i*nIndicesPerFace + j] = uint16_t(aim->mFaces[i].mIndices[j]);
+                }
+            }
+
+			p.indices = ExportData(*mAsset, meshId, b, unsigned(indices.size()), &indices[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_UNSIGNED_SHORT, true);
+		}
+
+        switch (aim->mPrimitiveTypes) {
+            case aiPrimitiveType_POLYGON:
+                p.mode = PrimitiveMode_TRIANGLES; break; // TODO implement this
+            case aiPrimitiveType_LINE:
+                p.mode = PrimitiveMode_LINES; break;
+            case aiPrimitiveType_POINT:
+                p.mode = PrimitiveMode_POINTS; break;
+            default: // aiPrimitiveType_TRIANGLE
+                p.mode = PrimitiveMode_TRIANGLES;
+        }
+
+        /*************** Skins ****************/
+        if(aim->HasBones()) {
+            ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
+        }
+    }
+
+    //----------------------------------------
+    // Finish the skin
+    // Create the Accessor for skinRef->inverseBindMatrices
+    if (createSkin) {
+        mat4* invBindMatrixData = new mat4[inverseBindMatricesData.size()];
+        for ( unsigned int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) {
+            CopyValue(inverseBindMatricesData[idx_joint], invBindMatrixData[idx_joint]);
+        }
+
+        Ref<Accessor> invBindMatrixAccessor = ExportData(*mAsset, skinName, b, static_cast<unsigned int>(inverseBindMatricesData.size()), invBindMatrixData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT);
+        if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor;
+
+        // Identity Matrix   =====>  skinRef->bindShapeMatrix
+        // Temporary. Hard-coded identity matrix here
+        skinRef->bindShapeMatrix.isPresent = true;
+        IdentityMatrix4(skinRef->bindShapeMatrix.value);
+
+        // Find nodes that contain a mesh with bones and add "skeletons" and "skin" attributes to those nodes.
+        Ref<Node> rootNode = mAsset->nodes.Get(unsigned(0));
+        Ref<Node> meshNode;
+        for (unsigned int meshIndex = 0; meshIndex < mAsset->meshes.Size(); ++meshIndex) {
+            Ref<Mesh> mesh = mAsset->meshes.Get(meshIndex);
+            bool hasBones = false;
+            for (unsigned int i = 0; i < mesh->primitives.size(); ++i) {
+                if (!mesh->primitives[i].attributes.weight.empty()) {
+                    hasBones = true;
+                    break;
+                }
+            }
+            if (!hasBones) {
+                continue;
+            }
+            std::string meshID = mesh->id;
+            FindMeshNode(rootNode, meshNode, meshID);
+            Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef);
+            meshNode->skeletons.push_back(rootJoint);
+            meshNode->skin = skinRef;
+        }
+    }
+}
+
+//merges a node's multiple meshes (with one primitive each) into one mesh with multiple primitives
+void glTF2Exporter::MergeMeshes()
+{
+    for (unsigned int n = 0; n < mAsset->nodes.Size(); ++n) {
+        Ref<Node> node = mAsset->nodes.Get(n);
+
+        unsigned int nMeshes = static_cast<unsigned int>(node->meshes.size());
+
+        //skip if it's 1 or less meshes per node
+        if (nMeshes > 1) {
+            Ref<Mesh> firstMesh = node->meshes.at(0);
+
+            //loop backwards to allow easy removal of a mesh from a node once it's merged
+            for (unsigned int m = nMeshes - 1; m >= 1; --m) {
+                Ref<Mesh> mesh = node->meshes.at(m);
+
+                firstMesh->primitives.insert(firstMesh->primitives.end(), mesh->primitives.begin(), mesh->primitives.end());
+
+                node->meshes.erase(node->meshes.begin() + m);
+            }
+
+            //since we were looping backwards, reverse the order of merged primitives to their original order
+            std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end());
+        }
+    }
+}
+
+/*
+ * Export the root node of the node hierarchy.
+ * Calls ExportNode for all children.
+ */
+unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode* n)
+{
+    Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
+
+    if (!n->mTransformation.IsIdentity()) {
+        node->matrix.isPresent = true;
+        CopyValue(n->mTransformation, node->matrix.value);
+    }
+
+    for (unsigned int i = 0; i < n->mNumMeshes; ++i) {
+        node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i]));
+    }
+
+    for (unsigned int i = 0; i < n->mNumChildren; ++i) {
+        unsigned int idx = ExportNode(n->mChildren[i], node);
+        node->children.push_back(mAsset->nodes.Get(idx));
+    }
+
+    return node.GetIndex();
+}
+
+/*
+ * Export node and recursively calls ExportNode for all children.
+ * Since these nodes are not the root node, we also export the parent Ref<Node>
+ */
+unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref<Node>& parent)
+{
+    std::string name = mAsset->FindUniqueID(n->mName.C_Str(), "node");
+    Ref<Node> node = mAsset->nodes.Create(name);
+
+    node->parent = parent;
+    node->name = name;
+
+    if (!n->mTransformation.IsIdentity()) {
+        node->matrix.isPresent = true;
+        CopyValue(n->mTransformation, node->matrix.value);
+    }
+
+    for (unsigned int i = 0; i < n->mNumMeshes; ++i) {
+        node->meshes.push_back(mAsset->meshes.Get(n->mMeshes[i]));
+    }
+
+    for (unsigned int i = 0; i < n->mNumChildren; ++i) {
+        unsigned int idx = ExportNode(n->mChildren[i], node);
+        node->children.push_back(mAsset->nodes.Get(idx));
+    }
+
+    return node.GetIndex();
+}
+
+
+void glTF2Exporter::ExportScene()
+{
+    const char* sceneName = "defaultScene";
+    Ref<Scene> scene = mAsset->scenes.Create(sceneName);
+
+    // root node will be the first one exported (idx 0)
+    if (mAsset->nodes.Size() > 0) {
+        scene->nodes.push_back(mAsset->nodes.Get(0u));
+    }
+
+    // set as the default scene
+    mAsset->scene = scene;
+}
+
+void glTF2Exporter::ExportMetadata()
+{
+    AssetMetadata& asset = mAsset->asset;
+    asset.version = "2.0";
+
+    char buffer[256];
+    ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%d)",
+        aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision());
+
+    asset.generator = buffer;
+}
+
+inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref<Animation>& animRef, Ref<Buffer>& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond)
+{
+    // Loop over the data and check to see if it exactly matches an existing buffer.
+    //    If yes, then reference the existing corresponding accessor.
+    //    Otherwise, add to the buffer and create a new accessor.
+
+    size_t counts[3] = {
+        nodeChannel->mNumPositionKeys,
+        nodeChannel->mNumScalingKeys,
+        nodeChannel->mNumRotationKeys,
+    };
+    size_t numKeyframes = 1;
+    for (int i = 0; i < 3; ++i) {
+        if (counts[i] > numKeyframes) {
+            numKeyframes = counts[i];
+        }
+    }
+
+    //-------------------------------------------------------
+    // Extract TIME parameter data.
+    // Check if the timeStamps are the same for mPositionKeys, mRotationKeys, and mScalingKeys.
+    if(nodeChannel->mNumPositionKeys > 0) {
+        typedef float TimeType;
+        std::vector<TimeType> timeData;
+        timeData.resize(numKeyframes);
+        for (size_t i = 0; i < numKeyframes; ++i) {
+            size_t frameIndex = i * nodeChannel->mNumPositionKeys / numKeyframes;
+            // mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
+            // Check if we have to cast type here. e.g. uint16_t()
+            timeData[i] = static_cast<float>(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond);
+        }
+
+        Ref<Accessor> timeAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT);
+        if (timeAccessor) animRef->Parameters.TIME = timeAccessor;
+    }
+
+    //-------------------------------------------------------
+    // Extract translation parameter data
+    if(nodeChannel->mNumPositionKeys > 0) {
+        C_STRUCT aiVector3D* translationData = new aiVector3D[numKeyframes];
+        for (size_t i = 0; i < numKeyframes; ++i) {
+            size_t frameIndex = i * nodeChannel->mNumPositionKeys / numKeyframes;
+            translationData[i] = nodeChannel->mPositionKeys[frameIndex].mValue;
+        }
+
+        Ref<Accessor> tranAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+        if ( tranAccessor ) {
+            animRef->Parameters.translation = tranAccessor;
+        }
+        delete[] translationData;
+    }
+
+    //-------------------------------------------------------
+    // Extract scale parameter data
+    if(nodeChannel->mNumScalingKeys > 0) {
+        C_STRUCT aiVector3D* scaleData = new aiVector3D[numKeyframes];
+        for (size_t i = 0; i < numKeyframes; ++i) {
+            size_t frameIndex = i * nodeChannel->mNumScalingKeys / numKeyframes;
+            scaleData[i] = nodeChannel->mScalingKeys[frameIndex].mValue;
+        }
+
+        Ref<Accessor> scaleAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+        if ( scaleAccessor ) {
+            animRef->Parameters.scale = scaleAccessor;
+        }
+        delete[] scaleData;
+    }
+
+    //-------------------------------------------------------
+    // Extract rotation parameter data
+    if(nodeChannel->mNumRotationKeys > 0) {
+        vec4* rotationData = new vec4[numKeyframes];
+        for (size_t i = 0; i < numKeyframes; ++i) {
+            size_t frameIndex = i * nodeChannel->mNumRotationKeys / numKeyframes;
+            rotationData[i][0] = nodeChannel->mRotationKeys[frameIndex].mValue.x;
+            rotationData[i][1] = nodeChannel->mRotationKeys[frameIndex].mValue.y;
+            rotationData[i][2] = nodeChannel->mRotationKeys[frameIndex].mValue.z;
+            rotationData[i][3] = nodeChannel->mRotationKeys[frameIndex].mValue.w;
+        }
+
+        Ref<Accessor> rotAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
+        if ( rotAccessor ) {
+            animRef->Parameters.rotation = rotAccessor;
+        }
+        delete[] rotationData;
+    }
+}
+
+void glTF2Exporter::ExportAnimations()
+{
+    Ref<Buffer> bufferRef = mAsset->buffers.Get(unsigned (0));
+
+    for (unsigned int i = 0; i < mScene->mNumAnimations; ++i) {
+        const aiAnimation* anim = mScene->mAnimations[i];
+
+        std::string nameAnim = "anim";
+        if (anim->mName.length > 0) {
+            nameAnim = anim->mName.C_Str();
+        }
+
+        for (unsigned int channelIndex = 0; channelIndex < anim->mNumChannels; ++channelIndex) {
+            const aiNodeAnim* nodeChannel = anim->mChannels[channelIndex];
+
+            // It appears that assimp stores this type of animation as multiple animations.
+            // where each aiNodeAnim in mChannels animates a specific node.
+            std::string name = nameAnim + "_" + to_string(channelIndex);
+            name = mAsset->FindUniqueID(name, "animation");
+            Ref<Animation> animRef = mAsset->animations.Create(name);
+
+            // Parameters
+            ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast<float>(anim->mTicksPerSecond));
+
+            for (unsigned int j = 0; j < 3; ++j) {
+                std::string channelType;
+                int channelSize;
+                switch (j) {
+                    case 0:
+                        channelType = "rotation";
+                        channelSize = nodeChannel->mNumRotationKeys;
+                        break;
+                    case 1:
+                        channelType = "scale";
+                        channelSize = nodeChannel->mNumScalingKeys;
+                        break;
+                    case 2:
+                        channelType = "translation";
+                        channelSize = nodeChannel->mNumPositionKeys;
+                        break;
+                }
+
+                if (channelSize < 1) { continue; }
+
+                Animation::AnimChannel tmpAnimChannel;
+                Animation::AnimSampler tmpAnimSampler;
+
+                tmpAnimChannel.sampler = static_cast<int>(animRef->Samplers.size());
+                tmpAnimChannel.target.path = channelType;
+                tmpAnimSampler.output = channelType;
+                tmpAnimSampler.id = name + "_" + channelType;
+
+                tmpAnimChannel.target.node = mAsset->nodes.Get(nodeChannel->mNodeName.C_Str());
+
+                tmpAnimSampler.input = "TIME";
+                tmpAnimSampler.interpolation = "LINEAR";
+
+                animRef->Channels.push_back(tmpAnimChannel);
+                animRef->Samplers.push_back(tmpAnimSampler);
+            }
+
+        }
+
+        // Assimp documentation staes this is not used (not implemented)
+        // for (unsigned int channelIndex = 0; channelIndex < anim->mNumMeshChannels; ++channelIndex) {
+        //     const aiMeshAnim* meshChannel = anim->mMeshChannels[channelIndex];
+        // }
+
+    } // End: for-loop mNumAnimations
+}
+
+
+#endif // ASSIMP_BUILD_NO_GLTF_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 134 - 0
code/glTF2Exporter.h

@@ -0,0 +1,134 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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 GltfExporter.h
+* Declares the exporter class to write a scene to a gltf/glb file
+*/
+#ifndef AI_GLTF2EXPORTER_H_INC
+#define AI_GLTF2EXPORTER_H_INC
+
+#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#include <assimp/types.h>
+#include <assimp/material.h>
+
+#include <sstream>
+#include <vector>
+#include <map>
+#include <memory>
+
+struct aiScene;
+struct aiNode;
+struct aiMaterial;
+
+namespace glTF2
+{
+    template<class T>
+    class Ref;
+
+    class Asset;
+    struct TexProperty;
+    struct TextureInfo;
+    struct NormalTextureInfo;
+    struct OcclusionTextureInfo;
+    struct Node;
+    struct Texture;
+
+    // Vec/matrix types, as raw float arrays
+    typedef float (vec3)[3];
+    typedef float (vec4)[4];
+}
+
+namespace Assimp
+{
+    class IOSystem;
+    class IOStream;
+    class ExportProperties;
+
+    // ------------------------------------------------------------------------------------------------
+    /** Helper class to export a given scene to an glTF file. */
+    // ------------------------------------------------------------------------------------------------
+    class glTF2Exporter
+    {
+    public:
+        /// Constructor for a specific scene to export
+        glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene,
+            const ExportProperties* pProperties, bool binary);
+
+    private:
+
+        const char* mFilename;
+        IOSystem* mIOSystem;
+        const aiScene* mScene;
+        const ExportProperties* mProperties;
+
+        std::map<std::string, unsigned int> mTexturesByPath;
+
+        std::shared_ptr<glTF2::Asset> mAsset;
+
+        std::vector<unsigned char> mBodyData;
+
+        void WriteBinaryData(IOStream* outfile, std::size_t sceneLength);
+
+        void GetTexSampler(const aiMaterial* mat, glTF2::Ref<glTF2::Texture> texture, aiTextureType tt, unsigned int slot);
+        void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx);
+        void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx);
+        void GetMatTex(const aiMaterial* mat, glTF2::Ref<glTF2::Texture>& texture, aiTextureType tt, unsigned int slot);
+        void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot);
+        void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot);
+        void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot);
+        void GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx);
+        void GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx);
+        void ExportMetadata();
+        void ExportMaterials();
+        void ExportMeshes();
+        void MergeMeshes();
+        unsigned int ExportNodeHierarchy(const aiNode* n);
+        unsigned int ExportNode(const aiNode* node, glTF2::Ref<glTF2::Node>& parent);
+        void ExportScene();
+        void ExportAnimations();
+    };
+
+}
+
+#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#endif // AI_GLTF2EXPORTER_H_INC

+ 654 - 0
code/glTF2Importer.cpp

@@ -0,0 +1,654 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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_GLTF_IMPORTER
+
+#include "glTF2Importer.h"
+#include "StringComparison.h"
+#include "StringUtils.h"
+
+#include <assimp/Importer.hpp>
+#include <assimp/scene.h>
+#include <assimp/ai_assert.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/importerdesc.h>
+
+#include <memory>
+
+#include "MakeVerboseFormat.h"
+
+#include "glTF2Asset.h"
+// This is included here so WriteLazyDict<T>'s definition is found.
+#include "glTF2AssetWriter.h"
+#include <rapidjson/document.h>
+#include <rapidjson/rapidjson.h>
+
+using namespace Assimp;
+using namespace glTF2;
+
+
+//
+// glTF2Importer
+//
+
+static const aiImporterDesc desc = {
+    "glTF2 Importer",
+    "",
+    "",
+    "",
+    aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+    0,
+    0,
+    0,
+    0,
+    "gltf glb"
+};
+
+glTF2Importer::glTF2Importer()
+: BaseImporter()
+, meshOffsets()
+, embeddedTexIdxs()
+, mScene( NULL ) {
+    // empty
+}
+
+glTF2Importer::~glTF2Importer() {
+    // empty
+}
+
+const aiImporterDesc* glTF2Importer::GetInfo() const
+{
+    return &desc;
+}
+
+bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+    const std::string &extension = GetExtension(pFile);
+
+    if (extension != "gltf") // We currently can't read glTF2 binary files (.glb), yet
+        return false;
+
+    if (checkSig && pIOHandler) {
+        glTF2::Asset asset(pIOHandler);
+        try {
+            asset.Load(pFile);
+            std::string version = asset.asset.version;
+            return !version.empty() && version[0] == '2';
+        } catch (...) {
+            return false;
+        }
+    }
+
+    return false;
+}
+
+
+//static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
+//{
+//    out.r = v[0]; out.g = v[1]; out.b = v[2];
+//}
+
+static void CopyValue(const glTF2::vec4& v, aiColor4D& out)
+{
+    out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3];
+}
+
+/*static void CopyValue(const glTF2::vec4& v, aiColor3D& out)
+{
+    out.r = v[0]; out.g = v[1]; out.b = v[2];
+}*/
+
+static void CopyValue(const glTF2::vec3& v, aiColor4D& out)
+{
+    out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = 1.0;
+}
+
+static void CopyValue(const glTF2::vec3& v, aiVector3D& out)
+{
+    out.x = v[0]; out.y = v[1]; out.z = v[2];
+}
+
+static void CopyValue(const glTF2::vec4& v, aiQuaternion& out)
+{
+    out.x = v[0]; out.y = v[1]; out.z = v[2]; out.w = v[3];
+}
+
+static void CopyValue(const glTF2::mat4& v, aiMatrix4x4& o)
+{
+    o.a1 = v[ 0]; o.b1 = v[ 1]; o.c1 = v[ 2]; o.d1 = v[ 3];
+    o.a2 = v[ 4]; o.b2 = v[ 5]; o.c2 = v[ 6]; o.d2 = v[ 7];
+    o.a3 = v[ 8]; o.b3 = v[ 9]; o.c3 = v[10]; o.d3 = v[11];
+    o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
+}
+
+inline void SetMaterialColorProperty(Asset& r, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
+{
+    aiColor4D col;
+    CopyValue(prop, col);
+    mat->AddProperty(&col, 1, pKey, type, idx);
+}
+
+inline void SetMaterialColorProperty(Asset& r, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
+{
+    aiColor4D col;
+    CopyValue(prop, col);
+    mat->AddProperty(&col, 1, pKey, type, idx);
+}
+
+inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& r, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0)
+{
+    if (prop.texture && prop.texture->source) {
+        aiString uri(prop.texture->source->uri);
+
+        int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()];
+        if (texIdx != -1) { // embedded
+            // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
+            uri.data[0] = '*';
+            uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx);
+        }
+
+        mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
+        mat->AddProperty(&prop.texCoord, 1, _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, texType, texSlot);
+
+        if (prop.texture->sampler) {
+            Ref<Sampler> sampler = prop.texture->sampler;
+
+            aiString name(sampler->name);
+            aiString id(sampler->id);
+
+            mat->AddProperty(&name, AI_MATKEY_GLTF_MAPPINGNAME(texType, texSlot));
+            mat->AddProperty(&id, AI_MATKEY_GLTF_MAPPINGID(texType, texSlot));
+
+            mat->AddProperty(&sampler->wrapS, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot));
+            mat->AddProperty(&sampler->wrapT, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot));
+
+            if (sampler->magFilter != SamplerMagFilter::UNSET) {
+                mat->AddProperty(&sampler->magFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(texType, texSlot));
+            }
+
+            if (sampler->minFilter != SamplerMinFilter::UNSET) {
+                mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot));
+            }
+        }
+    }
+}
+
+void glTF2Importer::ImportMaterials(glTF2::Asset& r)
+{
+    mScene->mNumMaterials = unsigned(r.materials.Size());
+    mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials];
+
+    for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
+        aiMaterial* aimat = mScene->mMaterials[i] = new aiMaterial();
+
+        Material& mat = r.materials[i];
+
+        if (!mat.name.empty()) {
+            aiString str(mat.name);
+
+            aimat->AddProperty(&str, AI_MATKEY_NAME);
+        }
+
+        SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE);
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
+        aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR);
+        aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR);
+
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS);
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP);
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.emissiveTexture, aimat, aiTextureType_EMISSIVE);
+        SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE);
+
+        aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED);
+
+        aiString alphaMode(mat.alphaMode);
+        aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
+        aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
+
+        //pbrSpecularGlossiness
+        if (mat.pbrSpecularGlossiness.isPresent) {
+            PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
+
+            aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS);
+            SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_FACTOR);
+            SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULAR_FACTOR);
+            aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR);
+            SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_DIFFUSE_TEXTURE);
+            SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_SPECULARGLOSSINESS_TEXTURE);
+        }
+    }
+}
+
+
+static inline void SetFace(aiFace& face, int a)
+{
+    face.mNumIndices = 1;
+    face.mIndices = new unsigned int[1];
+    face.mIndices[0] = a;
+}
+
+static inline void SetFace(aiFace& face, int a, int b)
+{
+    face.mNumIndices = 2;
+    face.mIndices = new unsigned int[2];
+    face.mIndices[0] = a;
+    face.mIndices[1] = b;
+}
+
+static inline void SetFace(aiFace& face, int a, int b, int c)
+{
+    face.mNumIndices = 3;
+    face.mIndices = new unsigned int[3];
+    face.mIndices[0] = a;
+    face.mIndices[1] = b;
+    face.mIndices[2] = c;
+}
+
+static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts)
+{
+    for (unsigned i = 0; i < nFaces; ++i) {
+        for (unsigned j = 0; j < faces[i].mNumIndices; ++j) {
+            unsigned idx = faces[i].mIndices[j];
+            if (idx >= nVerts)
+                return false;
+        }
+    }
+    return true;
+}
+
+void glTF2Importer::ImportMeshes(glTF2::Asset& r)
+{
+    std::vector<aiMesh*> meshes;
+
+    unsigned int k = 0;
+
+    for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
+        Mesh& mesh = r.meshes[m];
+
+        meshOffsets.push_back(k);
+        k += unsigned(mesh.primitives.size());
+
+        for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
+            Mesh::Primitive& prim = mesh.primitives[p];
+
+            aiMesh* aim = new aiMesh();
+            meshes.push_back(aim);
+
+            aim->mName = mesh.name.empty() ? mesh.id : mesh.name;
+
+            if (mesh.primitives.size() > 1) {
+                size_t& len = aim->mName.length;
+                aim->mName.data[len] = '-';
+                len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p);
+            }
+
+            switch (prim.mode) {
+                case PrimitiveMode_POINTS:
+                    aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
+                    break;
+
+                case PrimitiveMode_LINES:
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP:
+                    aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
+                    break;
+
+                case PrimitiveMode_TRIANGLES:
+                case PrimitiveMode_TRIANGLE_STRIP:
+                case PrimitiveMode_TRIANGLE_FAN:
+                    aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+                    break;
+
+            }
+
+            Mesh::Primitive::Attributes& attr = prim.attributes;
+
+            if (attr.position.size() > 0 && attr.position[0]) {
+                aim->mNumVertices = attr.position[0]->count;
+                attr.position[0]->ExtractData(aim->mVertices);
+            }
+
+            if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals);
+
+            for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
+                attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
+                aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
+
+                aiVector3D* values = aim->mTextureCoords[tc];
+                for (unsigned int i = 0; i < aim->mNumVertices; ++i) {
+                    values[i].y = 1 - values[i].y; // Flip Y coords
+                }
+            }
+
+
+            if (prim.indices) {
+                aiFace* faces = 0;
+                unsigned int nFaces = 0;
+
+                unsigned int count = prim.indices->count;
+
+                Accessor::Indexer data = prim.indices->GetIndexer();
+                ai_assert(data.IsValid());
+
+                switch (prim.mode) {
+                    case PrimitiveMode_POINTS: {
+                        nFaces = count;
+                        faces = new aiFace[nFaces];
+                        for (unsigned int i = 0; i < count; ++i) {
+                            SetFace(faces[i], data.GetUInt(i));
+                        }
+                        break;
+                    }
+
+                    case PrimitiveMode_LINES: {
+                        nFaces = count / 2;
+                        faces = new aiFace[nFaces];
+                        for (unsigned int i = 0; i < count; i += 2) {
+                            SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
+                        }
+                        break;
+                    }
+
+                    case PrimitiveMode_LINE_LOOP:
+                    case PrimitiveMode_LINE_STRIP: {
+                        nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
+                        faces = new aiFace[nFaces];
+                        SetFace(faces[0], data.GetUInt(0), data.GetUInt(1));
+                        for (unsigned int i = 2; i < count; ++i) {
+                            SetFace(faces[i - 1], faces[i - 2].mIndices[1], data.GetUInt(i));
+                        }
+                        if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
+                            SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]);
+                        }
+                        break;
+                    }
+
+                    case PrimitiveMode_TRIANGLES: {
+                        nFaces = count / 3;
+                        faces = new aiFace[nFaces];
+                        for (unsigned int i = 0; i < count; i += 3) {
+                            SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+                        }
+                        break;
+                    }
+                    case PrimitiveMode_TRIANGLE_STRIP: {
+                        nFaces = count - 2;
+                        faces = new aiFace[nFaces];
+                        SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
+                        for (unsigned int i = 3; i < count; ++i) {
+                            SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], data.GetUInt(i));
+                        }
+                        break;
+                    }
+                    case PrimitiveMode_TRIANGLE_FAN:
+                        nFaces = count - 2;
+                        faces = new aiFace[nFaces];
+                        SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
+                        for (unsigned int i = 3; i < count; ++i) {
+                            SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i));
+                        }
+                        break;
+                }
+
+                if (faces) {
+                    aim->mFaces = faces;
+                    aim->mNumFaces = nFaces;
+                    ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices));
+                }
+            }
+
+
+            if (prim.material) {
+                aim->mMaterialIndex = prim.material.GetIndex();
+            }
+        }
+    }
+
+    meshOffsets.push_back(k);
+
+    CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
+}
+
+void glTF2Importer::ImportCameras(glTF2::Asset& r)
+{
+    if (!r.cameras.Size()) return;
+
+    mScene->mNumCameras = r.cameras.Size();
+    mScene->mCameras = new aiCamera*[r.cameras.Size()];
+
+    for (size_t i = 0; i < r.cameras.Size(); ++i) {
+        Camera& cam = r.cameras[i];
+
+        aiCamera* aicam = mScene->mCameras[i] = new aiCamera();
+
+        if (cam.type == Camera::Perspective) {
+
+            aicam->mAspect        = cam.cameraProperties.perspective.aspectRatio;
+            aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * aicam->mAspect;
+            aicam->mClipPlaneFar  = cam.cameraProperties.perspective.zfar;
+            aicam->mClipPlaneNear = cam.cameraProperties.perspective.znear;
+        }
+        else {
+            // assimp does not support orthographic cameras
+        }
+    }
+}
+
+aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
+{
+    Node& node = *ptr;
+
+    aiNode* ainode = new aiNode(node.id);
+
+    if (!node.children.empty()) {
+        ainode->mNumChildren = unsigned(node.children.size());
+        ainode->mChildren = new aiNode*[ainode->mNumChildren];
+
+        for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
+            aiNode* child = ImportNode(pScene, r, meshOffsets, node.children[i]);
+            child->mParent = ainode;
+            ainode->mChildren[i] = child;
+        }
+    }
+
+    aiMatrix4x4& matrix = ainode->mTransformation;
+    if (node.matrix.isPresent) {
+        CopyValue(node.matrix.value, matrix);
+    }
+    else {
+        if (node.translation.isPresent) {
+            aiVector3D trans;
+            CopyValue(node.translation.value, trans);
+            aiMatrix4x4 t;
+            aiMatrix4x4::Translation(trans, t);
+            matrix = t * matrix;
+        }
+
+        if (node.scale.isPresent) {
+            aiVector3D scal(1.f);
+            CopyValue(node.scale.value, scal);
+            aiMatrix4x4 s;
+            aiMatrix4x4::Scaling(scal, s);
+            matrix = s * matrix;
+        }
+
+
+        if (node.rotation.isPresent) {
+            aiQuaternion rot;
+            CopyValue(node.rotation.value, rot);
+            matrix = aiMatrix4x4(rot.GetMatrix()) * matrix;
+        }
+    }
+
+    if (!node.meshes.empty()) {
+        int count = 0;
+        for (size_t i = 0; i < node.meshes.size(); ++i) {
+            int idx = node.meshes[i].GetIndex();
+            count += meshOffsets[idx + 1] - meshOffsets[idx];
+        }
+        ainode->mNumMeshes = count;
+
+        ainode->mMeshes = new unsigned int[count];
+
+        int k = 0;
+        for (size_t i = 0; i < node.meshes.size(); ++i) {
+            int idx = node.meshes[i].GetIndex();
+            for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) {
+                ainode->mMeshes[k] = j;
+            }
+        }
+    }
+
+    if (node.camera) {
+        pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
+    }
+
+    return ainode;
+}
+
+void glTF2Importer::ImportNodes(glTF2::Asset& r)
+{
+    if (!r.scene) return;
+
+    std::vector< Ref<Node> > rootNodes = r.scene->nodes;
+
+    // The root nodes
+    unsigned int numRootNodes = unsigned(rootNodes.size());
+    if (numRootNodes == 1) { // a single root node: use it
+        mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
+    }
+    else if (numRootNodes > 1) { // more than one root node: create a fake root
+        aiNode* root = new aiNode("ROOT");
+        root->mChildren = new aiNode*[numRootNodes];
+        for (unsigned int i = 0; i < numRootNodes; ++i) {
+            aiNode* node = ImportNode(mScene, r, meshOffsets, rootNodes[i]);
+            node->mParent = root;
+            root->mChildren[root->mNumChildren++] = node;
+        }
+        mScene->mRootNode = root;
+    }
+
+    //if (!mScene->mRootNode) {
+    //  mScene->mRootNode = new aiNode("EMPTY");
+    //}
+}
+
+void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
+{
+    embeddedTexIdxs.resize(r.images.Size(), -1);
+
+    int numEmbeddedTexs = 0;
+    for (size_t i = 0; i < r.images.Size(); ++i) {
+        if (r.images[i].HasData())
+            numEmbeddedTexs += 1;
+    }
+
+    if (numEmbeddedTexs == 0)
+        return;
+
+    mScene->mTextures = new aiTexture*[numEmbeddedTexs];
+
+    // Add the embedded textures
+    for (size_t i = 0; i < r.images.Size(); ++i) {
+        Image img = r.images[i];
+        if (!img.HasData()) continue;
+
+        int idx = mScene->mNumTextures++;
+        embeddedTexIdxs[i] = idx;
+
+        aiTexture* tex = mScene->mTextures[idx] = new aiTexture();
+
+        size_t length = img.GetDataLength();
+        void* data = img.StealData();
+
+        tex->mWidth = static_cast<unsigned int>(length);
+        tex->mHeight = 0;
+        tex->pcData = reinterpret_cast<aiTexel*>(data);
+
+        if (!img.mimeType.empty()) {
+            const char* ext = strchr(img.mimeType.c_str(), '/') + 1;
+            if (ext) {
+                if (strcmp(ext, "jpeg") == 0) ext = "jpg";
+
+                size_t len = strlen(ext);
+                if (len <= 3) {
+                    strcpy(tex->achFormatHint, ext);
+                }
+            }
+        }
+    }
+}
+
+void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
+
+    this->mScene = pScene;
+
+    // read the asset file
+    glTF2::Asset asset(pIOHandler);
+    asset.Load(pFile);
+
+    //
+    // Copy the data out
+    //
+
+    ImportEmbeddedTextures(asset);
+    ImportMaterials(asset);
+
+    ImportMeshes(asset);
+
+    ImportCameras(asset);
+
+    ImportNodes(asset);
+
+    // TODO: it does not split the loaded vertices, should it?
+    //pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+    MakeVerboseFormatProcess process;
+    process.Execute(pScene);
+
+
+    if (pScene->mNumMeshes == 0) {
+        pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+    }
+}
+
+#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
+

+ 91 - 0
code/glTF2Importer.h

@@ -0,0 +1,91 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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 AI_GLTF2IMPORTER_H_INC
+#define AI_GLTF2IMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include <assimp/DefaultIOSystem.h>
+
+struct aiNode;
+
+
+namespace glTF2
+{
+    class Asset;
+}
+
+namespace Assimp {
+
+/**
+ * Load the glTF2 format.
+ * https://github.com/KhronosGroup/glTF/tree/master/specification
+ */
+class glTF2Importer : public BaseImporter{
+public:
+    glTF2Importer();
+    virtual ~glTF2Importer();
+    virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const;
+
+protected:
+    virtual const aiImporterDesc* GetInfo() const;
+    virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler );
+
+private:
+
+    std::vector<unsigned int> meshOffsets;
+
+    std::vector<int> embeddedTexIdxs;
+
+    aiScene* mScene;
+
+    void ImportEmbeddedTextures(glTF2::Asset& a);
+    void ImportMaterials(glTF2::Asset& a);
+    void ImportMeshes(glTF2::Asset& a);
+    void ImportCameras(glTF2::Asset& a);
+    void ImportLights(glTF2::Asset& a);
+    void ImportNodes(glTF2::Asset& a);
+
+};
+
+} // Namespace assimp
+
+#endif // AI_GLTF2IMPORTER_H_INC
+

+ 5 - 3
code/glTFAsset.h

@@ -232,7 +232,9 @@ namespace glTF
             case ComponentType_UNSIGNED_BYTE:
                 return 1;
             default:
-                throw DeadlyImportError("GLTF: Unsupported Component Type "+t);
+                std::string err = "GLTF: Unsupported Component Type ";
+                err += t;
+                throw DeadlyImportError(err);
         }
     }
 
@@ -1058,13 +1060,13 @@ namespace glTF
             std::string version; //!< Specifies the target rendering API (default: "1.0.3")
         } profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {})
 
-        int version; //!< The glTF format version (should be 1)
+        std::string version; //!< The glTF format version (should be 1.0)
 
         void Read(Document& doc);
 
         AssetMetadata()
             : premultipliedAlpha(false)
-            , version(0)
+            , version("")
         {
         }
     };

+ 24 - 15
code/glTFAsset.inl

@@ -40,6 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #include "StringUtils.h"
+#include <iomanip>
 
 // Header files, Assimp
 #include <assimp/DefaultLogger.hpp>
@@ -128,6 +129,12 @@ namespace {
         return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0;
     }
 
+    inline Value* FindNumber(Value& val, const char* id)
+    {
+        Value::MemberIterator it = val.FindMember(id);
+        return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0;
+    }
+
     inline Value* FindArray(Value& val, const char* id)
     {
         Value::MemberIterator it = val.FindMember(id);
@@ -309,7 +316,9 @@ inline void Buffer::Read(Value& obj, Asset& r)
     }
     else { // Local file
         if (byteLength > 0) {
-            IOStream* file = r.OpenFile(uri, "rb");
+            std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : "";
+
+            IOStream* file = r.OpenFile(dir + uri, "rb");
             if (file) {
                 bool ok = LoadFromStream(*file, byteLength);
                 delete file;
@@ -332,7 +341,7 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO
         stream.Seek(baseOffset, aiOrigin_SET);
     }
 
-    mData.reset(new uint8_t[byteLength]);
+    mData.reset(new uint8_t[byteLength], std::default_delete<uint8_t[]>());
 
     if (stream.Read(mData.get(), byteLength, 1) != 1) {
         return false;
@@ -1228,13 +1237,21 @@ inline void Scene::Read(Value& obj, Asset& r)
 inline void AssetMetadata::Read(Document& doc)
 {
     // read the version, etc.
-    int statedVersion = 0;
     if (Value* obj = FindObject(doc, "asset")) {
         ReadMember(*obj, "copyright", copyright);
         ReadMember(*obj, "generator", generator);
 
         premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false);
-        statedVersion = MemberOrDefault(*obj, "version", 0);
+
+        if (Value* versionString = FindString(*obj, "version")) {
+            version = versionString->GetString();
+        } else if (Value* versionNumber = FindNumber (*obj, "version")) {
+            char buf[4];
+
+            ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble());
+
+            version = buf;
+        }
 
         if (Value* profile = FindObject(*obj, "profile")) {
             ReadMember(*profile, "api",     this->profile.api);
@@ -1242,16 +1259,8 @@ inline void AssetMetadata::Read(Document& doc)
         }
     }
 
-    version = std::max(statedVersion, version);
-    if (version == 0) {
-        // if missing version, we'll assume version 1...
-        version = 1;
-    }
-
-    if (version != 1) {
-        char msg[128];
-        ai_snprintf(msg, 128, "GLTF: Unsupported glTF version: %d", version);
-        throw DeadlyImportError(msg);
+    if (version.empty() || version[0] != '1') {
+        throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
     }
 }
 
@@ -1273,7 +1282,7 @@ inline void Asset::ReadBinaryHeader(IOStream& stream)
     }
 
     AI_SWAP4(header.version);
-    asset.version = header.version;
+    asset.version = std::to_string(header.version);
     if (header.version != 1) {
         throw DeadlyImportError("GLTF: Unsupported binary glTF version");
     }

+ 2 - 7
code/glTFAssetWriter.inl

@@ -606,13 +606,8 @@ namespace glTF {
     {
         Value asset;
         asset.SetObject();
-        {
-            char versionChar[10];
-            ai_snprintf(versionChar, sizeof(versionChar), "%d", mAsset.asset.version);
-            asset.AddMember("version", Value(versionChar, mAl).Move(), mAl);
-
-            asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
-        }
+        asset.AddMember("version", Value(mAsset.asset.version, mAl).Move(), mAl);
+        asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl);
         mDoc.AddMember("asset", asset, mAl);
     }
 

+ 11 - 11
code/glTFExporter.cpp

@@ -444,7 +444,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buf
         Ref<Node> nodeRef = mAsset.nodes.Get(aib->mName.C_Str());
         nodeRef->jointName = nodeRef->id;
 
-        unsigned int jointNamesIndex;
+        unsigned int jointNamesIndex = 0;
         bool addJointToJointNames = true;
         for ( unsigned int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) {
             if (skinRef->jointNames[idx_joint]->jointName.compare(nodeRef->jointName) == 0) {
@@ -469,11 +469,11 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buf
             float vertWeight      = aib->mWeights[idx_weights].mWeight;
 
             // A vertex can only have at most four joint weights. Ignore all others.
-            if (jointsPerVertex[vertexId] > 3) { 
-                continue; 
+            if (jointsPerVertex[vertexId] > 3) {
+                continue;
             }
 
-            vertexJointData[vertexId][jointsPerVertex[vertexId]] = jointNamesIndex;
+            vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex);
             vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
 
             jointsPerVertex[vertexId] += 1;
@@ -507,7 +507,7 @@ void glTFExporter::ExportMeshes()
 
     // Variables needed for compression. BEGIN.
     // Indices, not pointers - because pointer to buffer is changing while writing to it.
-    size_t idx_srcdata_begin;// Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer.
+    size_t idx_srcdata_begin = 0; // Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer.
     size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals.
     std::vector<size_t> idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer.
     size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer.
@@ -872,10 +872,10 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref<Animati
             size_t frameIndex = i * nodeChannel->mNumPositionKeys / numKeyframes;
             // mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
             // Check if we have to cast type here. e.g. uint16_t()
-            timeData[i] = nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond;
+            timeData[i] = static_cast<float>(nodeChannel->mPositionKeys[frameIndex].mTime / ticksPerSecond);
         }
 
-        Ref<Accessor> timeAccessor = ExportData(mAsset, animId, buffer, numKeyframes, &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT);
+        Ref<Accessor> timeAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), &timeData[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT);
         if (timeAccessor) animRef->Parameters.TIME = timeAccessor;
     }
 
@@ -888,7 +888,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref<Animati
             translationData[i] = nodeChannel->mPositionKeys[frameIndex].mValue;
         }
 
-        Ref<Accessor> tranAccessor = ExportData(mAsset, animId, buffer, numKeyframes, translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+        Ref<Accessor> tranAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), translationData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
         if ( tranAccessor ) {
             animRef->Parameters.translation = tranAccessor;
         }
@@ -904,7 +904,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref<Animati
             scaleData[i] = nodeChannel->mScalingKeys[frameIndex].mValue;
         }
 
-        Ref<Accessor> scaleAccessor = ExportData(mAsset, animId, buffer, numKeyframes, scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+        Ref<Accessor> scaleAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), scaleData, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
         if ( scaleAccessor ) {
             animRef->Parameters.scale = scaleAccessor;
         }
@@ -923,7 +923,7 @@ inline void ExtractAnimationData(Asset& mAsset, std::string& animId, Ref<Animati
             rotationData[i][3] = nodeChannel->mRotationKeys[frameIndex].mValue.w;
         }
 
-        Ref<Accessor> rotAccessor = ExportData(mAsset, animId, buffer, numKeyframes, rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
+        Ref<Accessor> rotAccessor = ExportData(mAsset, animId, buffer, static_cast<unsigned int>(numKeyframes), rotationData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
         if ( rotAccessor ) {
             animRef->Parameters.rotation = rotAccessor;
         }
@@ -953,7 +953,7 @@ void glTFExporter::ExportAnimations()
             Ref<Animation> animRef = mAsset->animations.Create(name);
 
             /******************* Parameters ********************/
-            ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, anim->mTicksPerSecond);
+            ExtractAnimationData(*mAsset, name, animRef, bufferRef, nodeChannel, static_cast<float>(anim->mTicksPerSecond));
 
             for (unsigned int j = 0; j < 3; ++j) {
                 std::string channelType;

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