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

Merge branch 'master' into master

Kim Kulling 6 жил өмнө
parent
commit
77281b75b2
50 өөрчлөгдсөн 998 нэмэгдсэн , 554 устгасан
  1. 1 0
      .gitignore
  2. 29 14
      Build.md
  3. 28 13
      CMakeLists.txt
  4. 2 0
      Readme.md
  5. 12 6
      appveyor.yml
  6. 1 0
      assimpTargets-release.cmake.in
  7. 1 1
      code/ASELoader.cpp
  8. 0 1
      code/ASEParser.cpp
  9. 5 5
      code/ASEParser.h
  10. 1 1
      code/AssbinLoader.cpp
  11. 23 0
      code/CMakeLists.txt
  12. 73 39
      code/ColladaExporter.cpp
  13. 11 0
      code/ColladaLoader.cpp
  14. 77 3
      code/ColladaParser.cpp
  15. 15 0
      code/ColladaParser.h
  16. 2 2
      code/Exporter.cpp
  17. 2 2
      code/FBXCommon.h
  18. 65 12
      code/FBXConverter.cpp
  19. 21 3
      code/FBXConverter.h
  20. 7 4
      code/FBXExportNode.cpp
  21. 6 6
      code/FBXExportNode.h
  22. 141 116
      code/FBXExportProperty.cpp
  23. 24 24
      code/FBXExportProperty.h
  24. 59 25
      code/FBXExporter.cpp
  25. 25 14
      code/FBXImportSettings.h
  26. 7 1
      code/FBXImporter.cpp
  27. 3 3
      code/FBXMeshGeometry.cpp
  28. 1 1
      code/FBXParser.cpp
  29. 60 0
      code/FBXUtil.cpp
  30. 9 0
      code/FBXUtil.h
  31. 28 10
      code/ObjFileParser.cpp
  32. 1 1
      code/ObjFileParser.h
  33. 13 20
      code/ValidateDataStructure.cpp
  34. 7 4
      code/glTF2Importer.cpp
  35. 0 105
      code/makefile.mingw
  36. 5 5
      code/res/assimp.rc
  37. 1 1
      include/assimp/Exporter.hpp
  38. 4 0
      include/assimp/color4.inl
  39. 20 5
      include/assimp/config.h.in
  40. 8 0
      include/assimp/scene.h
  41. 0 103
      packaging/windows-innosetup/script.iss
  42. 74 0
      packaging/windows-innosetup/script_x64.iss
  43. 75 0
      packaging/windows-innosetup/script_x86.iss
  44. 1 1
      port/AndroidJNI/README.md
  45. 1 1
      port/PyAssimp/pyassimp/core.py
  46. 11 0
      revision.h.in
  47. 1 1
      test/models/PLY/cube_test.ply
  48. 33 0
      test/unit/utObjImportExport.cpp
  49. 2 1
      tools/assimp_qt_viewer/CMakeLists.txt
  50. 2 0
      tools/assimp_view/CMakeLists.txt

+ 1 - 0
.gitignore

@@ -17,6 +17,7 @@ CMakeLists.txt.user
 
 
 # Generated
 # Generated
 assimp.pc
 assimp.pc
+assimp.aps
 revision.h
 revision.h
 contrib/zlib/zconf.h
 contrib/zlib/zconf.h
 contrib/zlib/zlib.pc
 contrib/zlib/zlib.pc

+ 29 - 14
Build.md

@@ -1,32 +1,32 @@
-# Install CMake
+# Build Instructions
+## Install CMake
 Asset-Importer-Lib can be build for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via
 Asset-Importer-Lib can be build for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via
-```
+```bash
 sudo apt-get install cmake
 sudo apt-get install cmake
 ```
 ```
 
 
-# Get the source
+## Get the source
 Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
 Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
-```
+```bash
 git clone https://github.com/assimp/assimp.git
 git clone https://github.com/assimp/assimp.git
 ```
 ```
 
 
-# Build instructions for Windows with Visual-Studio
+## Build instructions for Windows with Visual-Studio
 
 
 First you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/
 First you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/
 To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
 To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
-```
-> cmake CMakeLists.txt
+```bash
+cmake CMakeLists.txt
 ```
 ```
 This will generate the project files for the visual studio. All dependencies used to build Asset-IMporter-Lib shall be part of the repo. If you want to use you own zlib.installation this is possible as well. Check the options for it.
 This will generate the project files for the visual studio. All dependencies used to build Asset-IMporter-Lib shall be part of the repo. If you want to use you own zlib.installation this is possible as well. Check the options for it.
 
 
-# Build instructions for Windows with UWP
-See https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app
+## Build instructions for Windows with UWP
+See <https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app>
 
 
-
-# Build instrcutions for Linux / Unix
+## Build instructions for Linux / Unix
 Open a terminal and got to your repository. You can generate the makefiles and build the library via:
 Open a terminal and got to your repository. You can generate the makefiles and build the library via:
 
 
-```
+```bash
 cmake CMakeLists.txt
 cmake CMakeLists.txt
 make -j4
 make -j4
 ```
 ```
@@ -34,7 +34,23 @@ The option -j descripes the number of parallel processes for the build. In this
 
 
 If you want to use a IDE for linux you can try QTCreator for instance. 
 If you want to use a IDE for linux you can try QTCreator for instance. 
 
 
-# CMake build options
+## Build instructions for MinGW
+ Older versions of MinGW's compiler (e.g. 5.1.0) do not support the -mbig_obj flag 
+required to compile some of assimp's files, especially for debug builds.
+Version 7.3.0 of g++-mingw-w64 & gcc-mingw-w64 appears to work.
+
+Please see [CMake Cross Compiling](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling) for general information on CMake Toolchains.
+
+Some users have had success building assimp using MinGW on Linux using [polly](https://github.com/ruslo/polly/).
+
+The following toolchain, which is not maintained by assimp, seems to work on Linux: [linux-mingw-w64-gnuxx11.cmake](https://github.com/ruslo/polly/blob/master/linux-mingw-w64-gnuxx11.cmake)
+
+The following toolchain may or may not be helpful for building assimp using MinGW on Windows (untested):
+ [mingw-cxx17.cmake](https://github.com/ruslo/polly/blob/master/mingw-cxx17.cmake)
+
+Besides the toolchain, compilation should be the same as for Linux / Unix.
+
+## CMake build options
 The cmake-build-environment provides options to configure the build. The following options can be used:
 The cmake-build-environment provides options to configure the build. The following options can be used:
 - **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib.
 - **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib.
 - **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle
 - **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle
@@ -55,4 +71,3 @@ The cmake-build-environment provides options to configure the build. The followi
 - **INJECT_DEBUG_POSTFIX( default ON )**: Inject debug postfix in .a/.so lib names
 - **INJECT_DEBUG_POSTFIX( default ON )**: Inject debug postfix in .a/.so lib names
 - **IGNORE_GIT_HASH ( default OFF )**: Don't call git to get the hash.
 - **IGNORE_GIT_HASH ( default OFF )**: Don't call git to get the hash.
 - **ASSIMP_INSTALL_PDB ( default ON )**: Install MSVC debug files.
 - **ASSIMP_INSTALL_PDB ( default ON )**: Install MSVC debug files.
-

+ 28 - 13
CMakeLists.txt

@@ -106,7 +106,7 @@ OPTION ( BUILD_DOCS
   OFF
   OFF
 )
 )
 OPTION( INJECT_DEBUG_POSTFIX
 OPTION( INJECT_DEBUG_POSTFIX
-  "Inject debug postfix in .a/.so lib names"
+  "Inject debug postfix in .a/.so/.dll lib names"
   ON
   ON
 )
 )
 
 
@@ -127,12 +127,15 @@ if (WIN32)
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 endif()
 endif()
 
 
-
 IF(MSVC)
 IF(MSVC)
   OPTION( ASSIMP_INSTALL_PDB
   OPTION( ASSIMP_INSTALL_PDB
     "Install MSVC debug files."
     "Install MSVC debug files."
     ON
     ON
   )
   )
+  IF(NOT (MSVC_VERSION LESS 1900))
+    # Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
+    ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
+  ENDIF()
 ENDIF(MSVC)
 ENDIF(MSVC)
 
 
 IF (BUILD_FRAMEWORK)
 IF (BUILD_FRAMEWORK)
@@ -148,8 +151,8 @@ ELSE()
 ENDIF(NOT BUILD_SHARED_LIBS)
 ENDIF(NOT BUILD_SHARED_LIBS)
 
 
 # Define here the needed parameters
 # Define here the needed parameters
-SET (ASSIMP_VERSION_MAJOR 4)
-SET (ASSIMP_VERSION_MINOR 1)
+SET (ASSIMP_VERSION_MAJOR 5)
+SET (ASSIMP_VERSION_MINOR 0)
 SET (ASSIMP_VERSION_PATCH 0)
 SET (ASSIMP_VERSION_PATCH 0)
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 SET (ASSIMP_SOVERSION 4)
 SET (ASSIMP_SOVERSION 4)
@@ -234,6 +237,11 @@ ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
   SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fPIC -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fPIC -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS}")
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS}")
 ELSEIF( CMAKE_COMPILER_IS_MINGW )
 ELSEIF( CMAKE_COMPILER_IS_MINGW )
+  IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
+    message(FATAL_ERROR "MinGW is too old to be supported. Please update MinGW and try again.")
+  ELSEIF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.3)
+    message(WARNING "MinGW is old, if you experience errors, update MinGW.")
+  ENDIF()
   SET( CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 -Wa,-mbig-obj ${CMAKE_CXX_FLAGS}" )
   SET( CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 -Wa,-mbig-obj ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS} ")
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS} ")
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
@@ -247,6 +255,7 @@ IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
 ELSE()
 ELSE()
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
+  # Experimental for pdb generation
 ENDIF()
 ENDIF()
 
 
 ENDIF( IOS )
 ENDIF( IOS )
@@ -301,7 +310,9 @@ SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE STRING
 SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
 SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
   "Path the tool executables are installed to." )
   "Path the tool executables are installed to." )
 
 
-IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+
+IF (is_multi_config OR (CMAKE_BUILD_TYPE STREQUAL "Debug"))
   SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfix for lib, samples and tools")
   SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfix for lib, samples and tools")
 ELSE()
 ELSE()
   SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Debug Postfix for lib, samples and tools")
   SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Debug Postfix for lib, samples and tools")
@@ -543,18 +554,22 @@ if(WIN32)
   if (CMAKE_SIZEOF_VOID_P EQUAL 8)
   if (CMAKE_SIZEOF_VOID_P EQUAL 8)
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin64/")
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin64/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib64/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib64/")
-  elseif()
+  else()
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin32/")
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin32/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib32/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib32/")
   ENDIF()
   ENDIF()
 
 
-  IF(MSVC12)
-    SET(ASSIMP_MSVC_VERSION "vc120")
-  ELSEIF(MSVC14)
-    SET(ASSIMP_MSVC_VERSION "vc140")
-  ELSEIF(MSVC15)
-    SET(ASSIMP_MSVC_VERSION "vc141")
-  ENDIF(MSVC12)
+  IF(MSVC_TOOLSET_VERSION)
+    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
+  ELSE()
+    IF(MSVC12)
+      SET(ASSIMP_MSVC_VERSION "vc120")
+    ELSEIF(MSVC14)
+      SET(ASSIMP_MSVC_VERSION "vc140")
+    ELSEIF(MSVC15)
+      SET(ASSIMP_MSVC_VERSION "vc141")
+    ENDIF(MSVC12)
+  ENDIF()
 
 
   IF(MSVC12 OR MSVC14 OR MSVC15 )
   IF(MSVC12 OR MSVC14 OR MSVC15 )
     ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)
     ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)

+ 2 - 0
Readme.md

@@ -17,6 +17,8 @@ A library to import and export various 3d-model-formats including scene-post-pro
 
 
 APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
 APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
 
 
+[Check the latest doc](https://assimp-docs.readthedocs.io/en/latest/).
+
 Additionally, assimp features various __mesh post processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
 Additionally, assimp features various __mesh post processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
 
 
 This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
 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).

+ 12 - 6
appveyor.yml

@@ -15,8 +15,8 @@ matrix:
     
     
 image:
 image:
   - Visual Studio 2013
   - Visual Studio 2013
-  - Previous Visual Studio 2015
-  - Previous Visual Studio 2017
+  - Visual Studio 2015
+  - Visual Studio 2017
     
     
 platform:
 platform:
   - Win32
   - Win32
@@ -28,10 +28,10 @@ install:
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set CMAKE_DEFINES -DASSIMP_WERROR=ON
   - 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 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013
-  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Previous Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
-  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Previous Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
+  - 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
   - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
-  - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%"
+  - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" .
   - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
   - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
@@ -53,7 +53,13 @@ build:
   project: Assimp.sln
   project: Assimp.sln
   
   
 after_build:
 after_build:
-  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" iscc packaging\windows-innosetup\script.iss
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (
+        if "%platform%"=="x64" (
+            iscc packaging\windows-innosetup\script_x64.iss
+        ) else (
+            iscc packaging\windows-innosetup\script_x86.iss
+        )
+    )
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
 
 
 test_script:
 test_script:

+ 1 - 0
assimpTargets-release.cmake.in

@@ -53,6 +53,7 @@ endif()
 set(CMAKE_IMPORT_FILE_VERSION)
 set(CMAKE_IMPORT_FILE_VERSION)
 
 
 get_filename_component(ASSIMP_ROOT_DIR "@CMAKE_INSTALL_PREFIX@" REALPATH)
 get_filename_component(ASSIMP_ROOT_DIR "@CMAKE_INSTALL_PREFIX@" REALPATH)
+
 set( ASSIMP_CXX_FLAGS ) # dynamically linked library
 set( ASSIMP_CXX_FLAGS ) # dynamically linked library
 set( ASSIMP_LINK_FLAGS "" )
 set( ASSIMP_LINK_FLAGS "" )
 set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
 set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")

+ 1 - 1
code/ASELoader.cpp

@@ -1299,7 +1299,7 @@ void ASEImporter::BuildMaterialIndices()
         }
         }
     }
     }
 
 
-    // Dekete our temporary array
+    // Delete our temporary array
     delete[] pcIntMaterials;
     delete[] pcIntMaterials;
 }
 }
 
 

+ 0 - 1
code/ASEParser.cpp

@@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of the ASE parser class
  *  @brief Implementation of the ASE parser class
  */
  */
 
 
-
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 

+ 5 - 5
code/ASEParser.h

@@ -188,10 +188,11 @@ struct Animation {
     } mRotationType, mScalingType, mPositionType;
     } mRotationType, mScalingType, mPositionType;
 
 
     Animation() AI_NO_EXCEPT
     Animation() AI_NO_EXCEPT
-        :   mRotationType   (TRACK)
-        ,   mScalingType    (TRACK)
-        ,   mPositionType   (TRACK)
-    {}
+    :   mRotationType   (TRACK)
+    ,   mScalingType    (TRACK)
+    ,   mPositionType   (TRACK) {
+        // empty
+    }
 
 
     //! List of track rotation keyframes
     //! List of track rotation keyframes
     std::vector< aiQuatKey > akeyRotations;
     std::vector< aiQuatKey > akeyRotations;
@@ -243,7 +244,6 @@ struct BaseNode {
         mTargetPosition.x = qnan;
         mTargetPosition.x = qnan;
     }
     }
 
 
-
     //! Name of the mesh
     //! Name of the mesh
     std::string mName;
     std::string mName;
 
 

+ 1 - 1
code/AssbinLoader.cpp

@@ -68,7 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace Assimp;
 
 
 static const aiImporterDesc desc = {
 static const aiImporterDesc desc = {
-    ".assbin Importer",
+    "Assimp Binary Importer",
     "Gargaj / Conspiracy",
     "Gargaj / Conspiracy",
     "",
     "",
     "",
     "",

+ 23 - 0
code/CMakeLists.txt

@@ -145,6 +145,10 @@ SET( Core_SRCS
   Assimp.cpp
   Assimp.cpp
 )
 )
 
 
+IF(MSVC)
+  list(APPEND Core_SRCS "res/assimp.rc")
+ENDIF(MSVC)
+
 SET( Logging_SRCS
 SET( Logging_SRCS
   ${HEADER_PATH}/DefaultLogger.hpp
   ${HEADER_PATH}/DefaultLogger.hpp
   ${HEADER_PATH}/LogStream.hpp
   ${HEADER_PATH}/LogStream.hpp
@@ -800,6 +804,18 @@ ADD_ASSIMP_IMPORTER( MMD
   MMDVmdParser.h
   MMDVmdParser.h
 )
 )
 
 
+# Workaround for issue #2406 - force problematic large file to be optimized to prevent string table overflow error
+# Used -Os instead of -O2 as previous issues had mentioned, since -Os is roughly speaking -O2, excluding any
+# optimizations that take up extra space. Given that the issue is a string table overflowing, -Os seemed appropriate
+# Also, I'm not positive if both link & compile flags are needed, but this hopefully ensures that the issue should not
+# recur for edge cases such as static builds.
+if ((CMAKE_COMPILER_IS_MINGW) AND (CMAKE_BUILD_TYPE MATCHES Debug))
+  message("-- Applying MinGW StepFileGen1.cpp Debug Workaround")
+  SET_SOURCE_FILES_PROPERTIES(Importer/StepFile/StepFileGen1.cpp PROPERTIES COMPILE_FLAGS -Os )
+  SET_SOURCE_FILES_PROPERTIES(Importer/StepFile/StepFileGen1.cpp PROPERTIES LINK_FLAGS -Os )
+  SET_SOURCE_FILES_PROPERTIES(Importer/StepFile/StepFileGen1.cpp PROPERTIES STATIC_LIBRARY_FLAGS -Os )
+endif()
+
 ADD_ASSIMP_IMPORTER( STEP
 ADD_ASSIMP_IMPORTER( STEP
     STEPFile.h
     STEPFile.h
     Importer/StepFile/StepFileImporter.h
     Importer/StepFile/StepFileImporter.h
@@ -1033,6 +1049,10 @@ ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
 
 
 if( MSVC )
 if( MSVC )
   # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
   # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
+  # CMake 3.12 added a variable for this
+  if(MSVC_TOOLSET_VERSION)
+    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
+  else()
   if( MSVC70 OR MSVC71 )
   if( MSVC70 OR MSVC71 )
     set(MSVC_PREFIX "vc70")
     set(MSVC_PREFIX "vc70")
   elseif( MSVC80 )
   elseif( MSVC80 )
@@ -1047,9 +1067,12 @@ if( MSVC )
     set(MSVC_PREFIX "vc120")
     set(MSVC_PREFIX "vc120")
   elseif( MSVC14 )
   elseif( MSVC14 )
     set(MSVC_PREFIX "vc140")
     set(MSVC_PREFIX "vc140")
+  elseif( MSVC15 )
+    set(MSVC_PREFIX "vc141")
   else()
   else()
     set(MSVC_PREFIX "vc150")
     set(MSVC_PREFIX "vc150")
   endif()
   endif()
+  endif()
   set(LIBRARY_SUFFIX "${ASSIMP_LIBRARY_SUFFIX}-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library")
   set(LIBRARY_SUFFIX "${ASSIMP_LIBRARY_SUFFIX}-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library")
 endif()
 endif()
 
 

+ 73 - 39
code/ColladaExporter.cpp

@@ -64,13 +64,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
-namespace Assimp
-{
+namespace Assimp {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
-void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
-{
+void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
     std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
     std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
     std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
     std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
 
 
@@ -93,12 +91,12 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp
 
 
-
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor for a specific scene to export
 // Constructor for a specific scene to export
-ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file)
-{
+ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) 
+: mIOSystem(pIOSystem)
+, mPath(path)
+, mFile(file) {
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     mOutput.imbue( std::locale("C") );
     mOutput.imbue( std::locale("C") );
     mOutput.precision(16);
     mOutput.precision(16);
@@ -115,17 +113,15 @@ ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, co
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Destructor
 // Destructor
-ColladaExporter::~ColladaExporter()
-{
-    if(mSceneOwned) {
+ColladaExporter::~ColladaExporter() {
+    if ( mSceneOwned ) {
         delete mScene;
         delete mScene;
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Starts writing the contents
 // Starts writing the contents
-void ColladaExporter::WriteFile()
-{
+void ColladaExporter::WriteFile() {
     // write the DTD
     // write the DTD
     mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
     mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
     // COLLADA element start
     // COLLADA element start
@@ -158,8 +154,7 @@ void ColladaExporter::WriteFile()
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes the asset header
 // Writes the asset header
-void ColladaExporter::WriteHeader()
-{
+void ColladaExporter::WriteHeader() {
     static const ai_real epsilon = ai_real( 0.00001 );
     static const ai_real epsilon = ai_real( 0.00001 );
     static const aiQuaternion x_rot(aiMatrix3x3(
     static const aiQuaternion x_rot(aiMatrix3x3(
         0, -1,  0,
         0, -1,  0,
@@ -238,25 +233,64 @@ void ColladaExporter::WriteHeader()
     mOutput << startstr << "<contributor>" << endstr;
     mOutput << startstr << "<contributor>" << endstr;
     PushTag();
     PushTag();
 
 
-    aiMetadata* meta = mScene->mRootNode->mMetaData;
+    // If no Scene metadata, use root node metadata
+    aiMetadata* meta = mScene->mMetaData;
+    if (nullptr == meta) {
+        meta = mScene->mRootNode->mMetaData;
+    }
+
     aiString value;
     aiString value;
-    if (!meta || !meta->Get("Author", value))
+    if (!meta || !meta->Get("Author", value)) {
         mOutput << startstr << "<author>" << "Assimp" << "</author>" << endstr;
         mOutput << startstr << "<author>" << "Assimp" << "</author>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
         mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
+    }
 
 
-    if (!meta || !meta->Get("AuthoringTool", value))
+    if (nullptr == meta || !meta->Get("AuthoringTool", value)) {
         mOutput << startstr << "<authoring_tool>" << "Assimp Exporter" << "</authoring_tool>" << endstr;
         mOutput << startstr << "<authoring_tool>" << "Assimp Exporter" << "</authoring_tool>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
         mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
+    }
 
 
-    //mOutput << startstr << "<author>" << mScene->author.C_Str() << "</author>" << endstr;
-    //mOutput << startstr << "<authoring_tool>" << mScene->authoringTool.C_Str() << "</authoring_tool>" << endstr;
+    if (meta) {
+        if (meta->Get("Comments", value)) {
+            mOutput << startstr << "<comments>" << XMLEscape(value.C_Str()) << "</comments>" << endstr;
+        }
+        if (meta->Get("Copyright", value)) {
+            mOutput << startstr << "<copyright>" << XMLEscape(value.C_Str()) << "</copyright>" << endstr;
+        }
+        if (meta->Get("SourceData", value)) {
+            mOutput << startstr << "<source_data>" << XMLEscape(value.C_Str()) << "</source_data>" << endstr;
+        }
+    }
 
 
     PopTag();
     PopTag();
     mOutput << startstr << "</contributor>" << endstr;
     mOutput << startstr << "</contributor>" << endstr;
-    mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+
+    if (nullptr == meta || !meta->Get("Created", value)) {
+        mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+    } else {
+        mOutput << startstr << "<created>" << XMLEscape(value.C_Str()) << "</created>" << endstr;
+    }
+
+    // Modified date is always the date saved
     mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
     mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
+
+    if (meta) {
+        if (meta->Get("Keywords", value)) {
+            mOutput << startstr << "<keywords>" << XMLEscape(value.C_Str()) << "</keywords>" << endstr;
+        }
+        if (meta->Get("Revision", value)) {
+            mOutput << startstr << "<revision>" << XMLEscape(value.C_Str()) << "</revision>" << endstr;
+        }
+        if (meta->Get("Subject", value)) {
+            mOutput << startstr << "<subject>" << XMLEscape(value.C_Str()) << "</subject>" << endstr;
+        }
+        if (meta->Get("Title", value)) {
+            mOutput << startstr << "<title>" << XMLEscape(value.C_Str()) << "</title>" << endstr;
+        }
+    }
+
     mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
     mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
     mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
     mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
     PopTag();
     PopTag();
@@ -269,12 +303,15 @@ void ColladaExporter::WriteTextures() {
     static const unsigned int buffer_size = 1024;
     static const unsigned int buffer_size = 1024;
     char str[buffer_size];
     char str[buffer_size];
 
 
-    if(mScene->HasTextures()) {
+    if (mScene->HasTextures()) {
         for(unsigned int i = 0; i < mScene->mNumTextures; i++) {
         for(unsigned int i = 0; i < mScene->mNumTextures; i++) {
             // It would be great to be able to create a directory in portable standard C++, but it's not the case,
             // It would be great to be able to create a directory in portable standard C++, but it's not the case,
             // so we just write the textures in the current directory.
             // so we just write the textures in the current directory.
 
 
             aiTexture* texture = mScene->mTextures[i];
             aiTexture* texture = mScene->mTextures[i];
+            if ( nullptr == texture ) {
+                continue;
+            }
 
 
             ASSIMP_itoa10(str, buffer_size, i + 1);
             ASSIMP_itoa10(str, buffer_size, i + 1);
 
 
@@ -428,6 +465,7 @@ void ColladaExporter::WritePointLight(const aiLight *const light){
     mOutput << startstr << "</point>" << endstr;
     mOutput << startstr << "</point>" << endstr;
 
 
 }
 }
+
 void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
 void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     const aiColor3D &color=  light->mColorDiffuse;
     const aiColor3D &color=  light->mColorDiffuse;
     mOutput << startstr << "<directional>" << endstr;
     mOutput << startstr << "<directional>" << endstr;
@@ -440,6 +478,7 @@ void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     mOutput << startstr << "</directional>" << endstr;
     mOutput << startstr << "</directional>" << endstr;
 
 
 }
 }
+
 void ColladaExporter::WriteSpotLight(const aiLight *const light){
 void ColladaExporter::WriteSpotLight(const aiLight *const light){
 
 
     const aiColor3D &color=  light->mColorDiffuse;
     const aiColor3D &color=  light->mColorDiffuse;
@@ -496,18 +535,16 @@ void ColladaExporter::WriteAmbienttLight(const aiLight *const light){
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reads a single surface entry from the given material keys
 // Reads a single surface entry from the given material keys
-void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex)
-{
-  if( pSrcMat->GetTextureCount( pTexture) > 0 )
-  {
+void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, 
+                                          aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex) {
+  if( pSrcMat->GetTextureCount( pTexture) > 0 ) {
     aiString texfile;
     aiString texfile;
     unsigned int uvChannel = 0;
     unsigned int uvChannel = 0;
     pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel);
     pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel);
 
 
     std::string index_str(texfile.C_Str());
     std::string index_str(texfile.C_Str());
 
 
-    if(index_str.size() != 0 && index_str[0] == '*')
-    {
+    if(index_str.size() != 0 && index_str[0] == '*') {
         unsigned int index;
         unsigned int index;
 
 
         index_str = index_str.substr(1, std::string::npos);
         index_str = index_str.substr(1, std::string::npos);
@@ -525,15 +562,13 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
         } else {
         } else {
             throw DeadlyExportError("could not find embedded texture at index " + index_str);
             throw DeadlyExportError("could not find embedded texture at index " + index_str);
         }
         }
-    } else
-    {
+    } else {
         poSurface.texture = texfile.C_Str();
         poSurface.texture = texfile.C_Str();
     }
     }
 
 
     poSurface.channel = uvChannel;
     poSurface.channel = uvChannel;
     poSurface.exist = true;
     poSurface.exist = true;
-  } else
-  {
+  } else {
     if( pKey )
     if( pKey )
       poSurface.exist = pSrcMat->Get( pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
       poSurface.exist = pSrcMat->Get( pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
   }
   }
@@ -541,15 +576,13 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
 // Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
-static bool isalnum_C(char c)
-{
+static bool isalnum_C(char c) {
   return ( nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",c) );
   return ( nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",c) );
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes an image entry for the given surface
 // Writes an image entry for the given surface
-void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd)
-{
+void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) {
   if( !pSurface.texture.empty() )
   if( !pSurface.texture.empty() )
   {
   {
     mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
     mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
@@ -803,8 +836,9 @@ void ColladaExporter::WriteControllerLibrary()
     mOutput << startstr << "<library_controllers>" << endstr;
     mOutput << startstr << "<library_controllers>" << endstr;
     PushTag();
     PushTag();
     
     
-    for( size_t a = 0; a < mScene->mNumMeshes; ++a)
+    for( size_t a = 0; a < mScene->mNumMeshes; ++a) {
         WriteController( a);
         WriteController( a);
+    }
 
 
     PopTag();
     PopTag();
     mOutput << startstr << "</library_controllers>" << endstr;
     mOutput << startstr << "</library_controllers>" << endstr;

+ 11 - 0
code/ColladaLoader.cpp

@@ -207,6 +207,17 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
                 0,  0,  0,  1);
                 0,  0,  0,  1);
     }
     }
 
 
+    // Store scene metadata
+    if (!parser.mAssetMetaData.empty()) {
+        const size_t numMeta(parser.mAssetMetaData.size());
+        pScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
+        size_t i = 0;
+        for (auto it = parser.mAssetMetaData.cbegin(); it != parser.mAssetMetaData.cend(); ++it, ++i)
+        {
+            pScene->mMetaData->Set(static_cast<unsigned int>(i), (*it).first, (*it).second);
+        }
+    }
+
     // store all meshes
     // store all meshes
     StoreSceneMeshes( pScene);
     StoreSceneMeshes( pScene);
 
 

+ 77 - 3
code/ColladaParser.cpp

@@ -264,14 +264,19 @@ void ColladaParser::ReadAssetInfo()
 
 
                 // check element end
                 // check element end
                 TestClosing( "up_axis");
                 TestClosing( "up_axis");
-            } else
+            }
+            else if(IsElement("contributor"))
             {
             {
-                SkipElement();
+                ReadContributorInfo();
+            }
+            else
+            {
+                ReadMetaDataItem(mAssetMetaData);
             }
             }
         }
         }
         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
         {
         {
-            if( strcmp( mReader->getNodeName(), "asset") != 0)
+            if (strcmp( mReader->getNodeName(), "asset") != 0)
                 ThrowException( "Expected end of <asset> element.");
                 ThrowException( "Expected end of <asset> element.");
 
 
             break;
             break;
@@ -279,6 +284,75 @@ void ColladaParser::ReadAssetInfo()
     }
     }
 }
 }
 
 
+// ------------------------------------------------------------------------------------------------
+// Reads the contributor info
+void ColladaParser::ReadContributorInfo()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            ReadMetaDataItem(mAssetMetaData);
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "contributor") != 0)
+                ThrowException("Expected end of <contributor> element.");
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single string metadata item
+void ColladaParser::ReadMetaDataItem(StringMetaData &metadata)
+{
+    // Metadata such as created, keywords, subject etc
+    const char* key_char = mReader->getNodeName();
+    if (key_char != nullptr)
+    {
+        const std::string key_str(key_char);
+        const char* value_char = TestTextContent();
+        if (value_char != nullptr)
+        {
+            std::string camel_key_str = key_str;
+            ToCamelCase(camel_key_str);
+            aiString aistr;
+            aistr.Set(value_char);
+            metadata.emplace(camel_key_str, aistr);
+            TestClosing(key_str.c_str());
+        }
+        else
+            SkipElement();
+    }
+    else
+        SkipElement();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert underscore_seperated to CamelCase: "authoring_tool" becomes "AuthoringTool"
+void ColladaParser::ToCamelCase(std::string &text)
+{
+    if (text.empty())
+        return;
+    // Capitalise first character
+    text[0] = ToUpper(text[0]);
+    for (auto it = text.begin(); it != text.end(); /*iterated below*/)
+    {
+        if ((*it) == '_')
+        {
+            it = text.erase(it);
+            if (it != text.end())
+                (*it) = ToUpper(*it);
+        }
+        else
+            ++it;
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reads the animation clips
 // Reads the animation clips
 void ColladaParser::ReadAnimationClipLibrary()
 void ColladaParser::ReadAnimationClipLibrary()

+ 15 - 0
code/ColladaParser.h

@@ -66,6 +66,9 @@ namespace Assimp
         friend class ColladaLoader;
         friend class ColladaLoader;
 
 
     protected:
     protected:
+        /** Map for generic metadata as aiString */
+        typedef std::map<std::string, aiString> StringMetaData;
+
         /** Constructor from XML file */
         /** Constructor from XML file */
         ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
         ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
 
 
@@ -81,6 +84,15 @@ namespace Assimp
         /** Reads asset information such as coordinate system information and legal blah */
         /** Reads asset information such as coordinate system information and legal blah */
         void ReadAssetInfo();
         void ReadAssetInfo();
 
 
+        /** Reads contributor information such as author and legal blah */
+        void ReadContributorInfo();
+
+        /** Reads generic metadata into provided map */
+        void ReadMetaDataItem(StringMetaData &metadata);
+
+        /** Convert underscore_seperated to CamelCase "authoring_tool" becomes "AuthoringTool" */
+        static void ToCamelCase(std::string &text);
+
         /** Reads the animation library */
         /** Reads the animation library */
         void ReadAnimationLibrary();
         void ReadAnimationLibrary();
 
 
@@ -343,6 +355,9 @@ namespace Assimp
         /** Which is the up vector */
         /** Which is the up vector */
         enum { UP_X, UP_Y, UP_Z } mUpDirection;
         enum { UP_X, UP_Y, UP_Z } mUpDirection;
 
 
+        /** Asset metadata (global for scene) */
+        StringMetaData mAssetMetaData;
+
         /** Collada file format version */
         /** Collada file format version */
         Collada::FormatVersion mFormat;
         Collada::FormatVersion mFormat;
     };
     };

+ 2 - 2
code/Exporter.cpp

@@ -288,7 +288,7 @@ void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
 const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
-                                                unsigned int, const ExportProperties* /*pProperties*/ ) {
+                                                unsigned int pPreprocessing, const ExportProperties* pProperties) {
     if (pimpl->blob) {
     if (pimpl->blob) {
         delete pimpl->blob;
         delete pimpl->blob;
         pimpl->blob = nullptr;
         pimpl->blob = nullptr;
@@ -298,7 +298,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
     BlobIOSystem* blobio = new BlobIOSystem();
     BlobIOSystem* blobio = new BlobIOSystem();
     pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
     pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
 
 
-    if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) {
+    if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
         pimpl->mIOSystem = old;
         pimpl->mIOSystem = old;
         return nullptr;
         return nullptr;
     }
     }

+ 2 - 2
code/FBXCommon.h

@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
-
+namespace Assimp {
 namespace FBX
 namespace FBX
 {
 {
     const std::string NULL_RECORD = { // 13 null bytes
     const std::string NULL_RECORD = { // 13 null bytes
@@ -80,7 +80,7 @@ namespace FBX
         TransformInheritance_MAX // end-of-enum sentinel
         TransformInheritance_MAX // end-of-enum sentinel
     };
     };
 }
 }
-
+}
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
 #endif // AI_FBXCOMMON_H_INC
 #endif // AI_FBXCOMMON_H_INC

+ 65 - 12
code/FBXConverter.cpp

@@ -67,6 +67,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sstream>
 #include <sstream>
 #include <iomanip>
 #include <iomanip>
 
 
+
 namespace Assimp {
 namespace Assimp {
     namespace FBX {
     namespace FBX {
 
 
@@ -76,7 +77,7 @@ namespace Assimp {
 
 
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
 
 
-        FBXConverter::FBXConverter(aiScene* out, const Document& doc)
+        FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit )
         : defaultMaterialIndex()
         : defaultMaterialIndex()
         , lights()
         , lights()
         , cameras()
         , cameras()
@@ -89,7 +90,9 @@ namespace Assimp {
         , mNodeNames()
         , mNodeNames()
         , anim_fps()
         , anim_fps()
         , out(out)
         , out(out)
-        , doc(doc) {
+        , doc(doc)
+        , mRemoveEmptyBones( removeEmptyBones )
+        , mCurrentUnit(FbxUnit::cm) {
             // animations need to be converted first since this will
             // animations need to be converted first since this will
             // populate the node_anim_chain_bits map, which is needed
             // populate the node_anim_chain_bits map, which is needed
             // to determine which nodes need to be generated.
             // to determine which nodes need to be generated.
@@ -117,6 +120,7 @@ namespace Assimp {
 
 
             ConvertGlobalSettings();
             ConvertGlobalSettings();
             TransferDataToScene();
             TransferDataToScene();
+            ConvertToUnitScale(unit);
 
 
             // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
             // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
             // to make sure the scene passes assimp's validation. FBX files
             // to make sure the scene passes assimp's validation. FBX files
@@ -387,6 +391,7 @@ namespace Assimp {
                 break;
                 break;
             default:
             default:
                 ai_assert(false);
                 ai_assert(false);
+                break;
             }
             }
         }
         }
 
 
@@ -977,7 +982,9 @@ namespace Assimp {
             unsigned int epcount = 0;
             unsigned int epcount = 0;
             for (unsigned i = 0; i < indices.size(); i++)
             for (unsigned i = 0; i < indices.size(); i++)
             {
             {
-                if (indices[i] < 0) epcount++;
+                if (indices[i] < 0) {
+                    epcount++;
+                }
             }
             }
             unsigned int pcount = static_cast<unsigned int>( indices.size() );
             unsigned int pcount = static_cast<unsigned int>( indices.size() );
             unsigned int scount = out_mesh->mNumFaces = pcount - epcount;
             unsigned int scount = out_mesh->mNumFaces = pcount - epcount;
@@ -1407,7 +1414,7 @@ namespace Assimp {
 
 
                     const WeightIndexArray& indices = cluster->GetIndices();
                     const WeightIndexArray& indices = cluster->GetIndices();
 
 
-                    if (indices.empty()) {
+                    if (indices.empty() && mRemoveEmptyBones ) {
                         continue;
                         continue;
                     }
                     }
 
 
@@ -1439,13 +1446,11 @@ namespace Assimp {
 
 
                                 if (index_out_indices.back() == no_index_sentinel) {
                                 if (index_out_indices.back() == no_index_sentinel) {
                                     index_out_indices.back() = out_indices.size();
                                     index_out_indices.back() = out_indices.size();
-
                                 }
                                 }
 
 
                                 if (no_mat_check) {
                                 if (no_mat_check) {
                                     out_indices.push_back(out_idx[i]);
                                     out_indices.push_back(out_idx[i]);
-                                }
-                                else {
+                                } else {
                                     // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
                                     // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
                                     const std::vector<unsigned int>::iterator it = std::lower_bound(
                                     const std::vector<unsigned int>::iterator it = std::lower_bound(
                                         outputVertStartIndices->begin(),
                                         outputVertStartIndices->begin(),
@@ -1461,11 +1466,11 @@ namespace Assimp {
                             }
                             }
                         }
                         }
                     }
                     }
-
+                    
                     // if we found at least one, generate the output bones
                     // if we found at least one, generate the output bones
                     // XXX this could be heavily simplified by collecting the bone
                     // XXX this could be heavily simplified by collecting the bone
                     // data in a single step.
                     // data in a single step.
-                    if (ok) {
+                    if (ok && mRemoveEmptyBones) {
                         ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
                         ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
                             count_out_indices, node_global_transform);
                             count_out_indices, node_global_transform);
                     }
                     }
@@ -1596,6 +1601,13 @@ namespace Assimp {
                 out_mat->AddProperty(&str, AI_MATKEY_NAME);
                 out_mat->AddProperty(&str, AI_MATKEY_NAME);
             }
             }
 
 
+            // Set the shading mode as best we can: The FBX specification only mentions Lambert and Phong, and only Phong is mentioned in Assimp's aiShadingMode enum.
+            if (material.GetShadingModel() == "phong")
+            {
+                aiShadingMode shadingMode = aiShadingMode_Phong;
+                out_mat->AddProperty<aiShadingMode>(&shadingMode, 1, AI_MATKEY_SHADING_MODEL);               
+            }
+
             // shading stuff and colors
             // shading stuff and colors
             SetShadingPropertiesCommon(out_mat, props);
             SetShadingPropertiesCommon(out_mat, props);
             SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh );
             SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh );
@@ -3407,8 +3419,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
 
 
             na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
             na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
             na->mScalingKeys = new aiVectorKey[keys.size()];
             na->mScalingKeys = new aiVectorKey[keys.size()];
-            if (keys.size() > 0)
+            if (keys.size() > 0) {
                 InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime);
                 InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime);
+            }
         }
         }
 
 
         void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
         void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
@@ -3472,6 +3485,46 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
             out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
             out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
         }
         }
 
 
+        void FBXConverter::ConvertToUnitScale( FbxUnit unit ) {
+            if (mCurrentUnit == unit) {
+                return;
+            }
+
+            ai_real scale = 1.0;
+            if (mCurrentUnit == FbxUnit::cm) {
+                if (unit == FbxUnit::m) {
+                    scale = (ai_real)0.01;
+                } else if (unit == FbxUnit::km) {
+                    scale = (ai_real)0.00001;
+                }
+            } else if (mCurrentUnit == FbxUnit::m) {
+                if (unit == FbxUnit::cm) {
+                    scale = (ai_real)100.0;
+                } else if (unit == FbxUnit::km) {
+                    scale = (ai_real)0.001;
+                }
+            } else if (mCurrentUnit == FbxUnit::km) {
+                if (unit == FbxUnit::cm) {
+                    scale = (ai_real)100000.0;
+                } else if (unit == FbxUnit::m) {
+                    scale = (ai_real)1000.0;
+                }
+            }
+            
+            for (auto mesh : meshes) {
+                if (nullptr == mesh) {
+                    continue;
+                }
+
+                if (mesh->HasPositions()) {
+                    for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+                        aiVector3D &pos = mesh->mVertices[i];
+                        pos *= scale;
+                    }
+                }
+            }
+        }
+
         void FBXConverter::TransferDataToScene()
         void FBXConverter::TransferDataToScene()
         {
         {
             ai_assert(!out->mMeshes);
             ai_assert(!out->mMeshes);
@@ -3525,9 +3578,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
         }
         }
 
 
         // ------------------------------------------------------------------------------------------------
         // ------------------------------------------------------------------------------------------------
-        void ConvertToAssimpScene(aiScene* out, const Document& doc)
+        void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit)
         {
         {
-            FBXConverter converter(out, doc);
+            FBXConverter converter(out, doc, removeEmptyBones, unit);
         }
         }
 
 
     } // !FBX
     } // !FBX

+ 21 - 3
code/FBXConverter.h

@@ -76,12 +76,22 @@ namespace FBX {
 
 
 class Document;
 class Document;
 
 
+enum class FbxUnit {
+    cm = 0,
+    m,
+    km,
+    NumUnits,
+
+    Undefined
+};
+
 /** 
 /** 
  *  Convert a FBX #Document to #aiScene
  *  Convert a FBX #Document to #aiScene
  *  @param out Empty scene to be populated
  *  @param out Empty scene to be populated
- *  @param doc Parsed FBX document 
+ *  @param doc Parsed FBX document
+ *  @param removeEmptyBones Will remove bones, which do not have any references to vertices.
  */
  */
-void ConvertToAssimpScene(aiScene* out, const Document& doc);
+void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
 
 
 /** Dummy class to encapsulate the conversion process */
 /** Dummy class to encapsulate the conversion process */
 class FBXConverter {
 class FBXConverter {
@@ -112,7 +122,7 @@ public:
     };
     };
 
 
 public:
 public:
-    FBXConverter(aiScene* out, const Document& doc);
+    FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
     ~FBXConverter();
     ~FBXConverter();
 
 
 private:
 private:
@@ -414,6 +424,10 @@ private:
 
 
     void ConvertGlobalSettings();
     void ConvertGlobalSettings();
 
 
+    // ------------------------------------------------------------------------------------------------
+    //  Will perform the conversion from a given unit to the requested unit.
+    void ConvertToUnitScale(FbxUnit unit);
+
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     void TransferDataToScene();
     void TransferDataToScene();
@@ -453,6 +467,10 @@ private:
 
 
     aiScene* const out;
     aiScene* const out;
     const FBX::Document& doc;
     const FBX::Document& doc;
+
+    bool mRemoveEmptyBones;
+
+    FbxUnit mCurrentUnit;
 };
 };
 
 
 }
 }

+ 7 - 4
code/FBXExportNode.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sstream> // ostringstream
 #include <sstream> // ostringstream
 #include <memory> // shared_ptr
 #include <memory> // shared_ptr
 
 
+namespace Assimp {
 // AddP70<type> helpers... there's no usable pattern here,
 // AddP70<type> helpers... there's no usable pattern here,
 // so all are defined as separate functions.
 // so all are defined as separate functions.
 // Even "animatable" properties are often completely different
 // Even "animatable" properties are often completely different
@@ -252,7 +253,8 @@ void FBX::Node::DumpChildren(
     } else {
     } else {
         std::ostringstream ss;
         std::ostringstream ss;
         DumpChildrenAscii(ss, indent);
         DumpChildrenAscii(ss, indent);
-        s.PutString(ss.str());
+        if (ss.tellp() > 0)
+            s.PutString(ss.str());
     }
     }
 }
 }
 
 
@@ -266,7 +268,8 @@ void FBX::Node::End(
     } else {
     } else {
         std::ostringstream ss;
         std::ostringstream ss;
         EndAscii(ss, indent, has_children);
         EndAscii(ss, indent, has_children);
-        s.PutString(ss.str());
+        if (ss.tellp() > 0)
+            s.PutString(ss.str());
     }
     }
 }
 }
 
 
@@ -367,7 +370,7 @@ void FBX::Node::EndBinary(
     bool has_children
     bool has_children
 ) {
 ) {
     // if there were children, add a null record
     // if there were children, add a null record
-    if (has_children) { s.PutString(FBX::NULL_RECORD); }
+    if (has_children) { s.PutString(Assimp::FBX::NULL_RECORD); }
 
 
     // now go back and write initial pos
     // now go back and write initial pos
     this->end_pos = s.Tell();
     this->end_pos = s.Tell();
@@ -563,6 +566,6 @@ void FBX::Node::WritePropertyNode(
         FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
         FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
     }
     }
 }
 }
-
+}
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 6 - 6
code/FBXExportNode.h

@@ -54,16 +54,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
+namespace Assimp {
 namespace FBX {
 namespace FBX {
     class Node;
     class Node;
 }
 }
 
 
-class FBX::Node
-{
-public: // public data members
+class FBX::Node {
+public: 
     // TODO: accessors
     // TODO: accessors
     std::string name; // node name
     std::string name; // node name
-    std::vector<FBX::Property> properties; // node properties
+    std::vector<FBX::FBXExportProperty> properties; // node properties
     std::vector<FBX::Node> children; // child nodes
     std::vector<FBX::Node> children; // child nodes
 
 
     // some nodes always pretend they have children...
     // some nodes always pretend they have children...
@@ -214,7 +214,7 @@ public: // static member functions
         Assimp::StreamWriterLE& s,
         Assimp::StreamWriterLE& s,
         bool binary, int indent
         bool binary, int indent
     ) {
     ) {
-        FBX::Property p(value);
+        FBX::FBXExportProperty p(value);
         FBX::Node node(name, p);
         FBX::Node node(name, p);
         node.Dump(s, binary, indent);
         node.Dump(s, binary, indent);
     }
     }
@@ -264,7 +264,7 @@ private: // static helper functions
     );
     );
 
 
 };
 };
-
+}
 
 
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 

+ 141 - 116
code/FBXExportProperty.cpp

@@ -52,187 +52,210 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <locale>
 #include <locale>
 #include <sstream> // ostringstream
 #include <sstream> // ostringstream
 
 
+namespace Assimp {
+namespace FBX {
 
 
 // constructors for single element properties
 // constructors for single element properties
 
 
-FBX::Property::Property(bool v)
-    : type('C'), data(1)
-{
-    data = {uint8_t(v)};
+FBXExportProperty::FBXExportProperty(bool v)
+: type('C')
+, data(1) {
+    data = {
+        uint8_t(v)
+    };
 }
 }
 
 
-FBX::Property::Property(int16_t v) : type('Y'), data(2)
-{
+FBXExportProperty::FBXExportProperty(int16_t v)
+: type('Y')
+, data(2) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<int16_t*>(d))[0] = v;
     (reinterpret_cast<int16_t*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(int32_t v) : type('I'), data(4)
-{
+FBXExportProperty::FBXExportProperty(int32_t v)
+: type('I')
+, data(4) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<int32_t*>(d))[0] = v;
     (reinterpret_cast<int32_t*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(float v) : type('F'), data(4)
-{
+FBXExportProperty::FBXExportProperty(float v)
+: type('F')
+, data(4) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<float*>(d))[0] = v;
     (reinterpret_cast<float*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(double v) : type('D'), data(8)
-{
+FBXExportProperty::FBXExportProperty(double v)
+: type('D')
+, data(8) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<double*>(d))[0] = v;
     (reinterpret_cast<double*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(int64_t v) : type('L'), data(8)
-{
+FBXExportProperty::FBXExportProperty(int64_t v)
+: type('L')
+, data(8) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<int64_t*>(d))[0] = v;
     (reinterpret_cast<int64_t*>(d))[0] = v;
 }
 }
 
 
-
 // constructors for array-type properties
 // constructors for array-type properties
 
 
-FBX::Property::Property(const char* c, bool raw)
-    : Property(std::string(c), raw)
-{}
+FBXExportProperty::FBXExportProperty(const char* c, bool raw)
+: FBXExportProperty(std::string(c), raw) {
+    // empty
+}
 
 
 // strings can either be saved as "raw" (R) data, or "string" (S) data
 // strings can either be saved as "raw" (R) data, or "string" (S) data
-FBX::Property::Property(const std::string& s, bool raw)
-    : type(raw ? 'R' : 'S'), data(s.size())
-{
+FBXExportProperty::FBXExportProperty(const std::string& s, bool raw)
+: type(raw ? 'R' : 'S')
+, data(s.size()) {
     for (size_t i = 0; i < s.size(); ++i) {
     for (size_t i = 0; i < s.size(); ++i) {
         data[i] = uint8_t(s[i]);
         data[i] = uint8_t(s[i]);
     }
     }
 }
 }
 
 
-FBX::Property::Property(const std::vector<uint8_t>& r)
-    : type('R'), data(r)
-{}
+FBXExportProperty::FBXExportProperty(const std::vector<uint8_t>& r)
+: type('R')
+, data(r) {
+    // empty
+}
 
 
-FBX::Property::Property(const std::vector<int32_t>& va)
-    : type('i'), data(4*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<int32_t>& va)
+: type('i')
+, data(4 * va.size() ) {
     int32_t* d = reinterpret_cast<int32_t*>(data.data());
     int32_t* d = reinterpret_cast<int32_t*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const std::vector<int64_t>& va)
-    : type('l'), data(8*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<int64_t>& va)
+: type('l')
+, data(8 * va.size()) {
     int64_t* d = reinterpret_cast<int64_t*>(data.data());
     int64_t* d = reinterpret_cast<int64_t*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const std::vector<float>& va)
-    : type('f'), data(4*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<float>& va)
+: type('f')
+, data(4 * va.size()) {
     float* d = reinterpret_cast<float*>(data.data());
     float* d = reinterpret_cast<float*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const std::vector<double>& va)
-    : type('d'), data(8*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<double>& va)
+: type('d')
+, data(8 * va.size()) {
     double* d = reinterpret_cast<double*>(data.data());
     double* d = reinterpret_cast<double*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const aiMatrix4x4& vm)
-    : type('d'), data(8*16)
-{
+FBXExportProperty::FBXExportProperty(const aiMatrix4x4& vm)
+: type('d')
+, data(8 * 16) {
     double* d = reinterpret_cast<double*>(data.data());
     double* d = reinterpret_cast<double*>(data.data());
     for (unsigned int c = 0; c < 4; ++c) {
     for (unsigned int c = 0; c < 4; ++c) {
         for (unsigned int r = 0; r < 4; ++r) {
         for (unsigned int r = 0; r < 4; ++r) {
-            d[4*c+r] = vm[r][c];
+            d[4 * c + r] = vm[r][c];
         }
         }
     }
     }
 }
 }
 
 
 // public member functions
 // public member functions
 
 
-size_t FBX::Property::size()
-{
+size_t FBXExportProperty::size() {
     switch (type) {
     switch (type) {
-    case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L':
-        return data.size() + 1;
-    case 'S': case 'R':
-        return data.size() + 5;
-    case 'i': case 'd':
-        return data.size() + 13;
-    default:
-        throw DeadlyExportError("Requested size on property of unknown type");
+        case 'C':
+        case 'Y':
+        case 'I':
+        case 'F':
+        case 'D':
+        case 'L':
+            return data.size() + 1;
+        case 'S':
+        case 'R':
+            return data.size() + 5;
+        case 'i':
+        case 'd':
+            return data.size() + 13;
+        default:
+            throw DeadlyExportError("Requested size on property of unknown type");
     }
     }
 }
 }
 
 
-void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s)
-{
+void FBXExportProperty::DumpBinary(Assimp::StreamWriterLE& s) {
     s.PutU1(type);
     s.PutU1(type);
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     size_t N;
     size_t N;
     switch (type) {
     switch (type) {
-    case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
-    case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
-    case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
-    case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
-    case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
-    case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
-    case 'S':
-    case 'R':
-        s.PutU4(uint32_t(data.size()));
-        for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
-        return;
-    case 'i':
-        N = data.size() / 4;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
-        }
-        return;
-    case 'l':
-        N = data.size() / 8;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
-        }
-        return;
-    case 'f':
-        N = data.size() / 4;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutF4((reinterpret_cast<float*>(d))[i]);
-        }
-        return;
-    case 'd':
-        N = data.size() / 8;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutF8((reinterpret_cast<double*>(d))[i]);
-        }
-        return;
-    default:
-        std::ostringstream err;
-        err << "Tried to dump property with invalid type '";
-        err << type << "'!";
-        throw DeadlyExportError(err.str());
+        case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
+        case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
+        case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
+        case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
+        case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
+        case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
+        case 'S':
+        case 'R':
+            s.PutU4(uint32_t(data.size()));
+            for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
+            return;
+        case 'i':
+            N = data.size() / 4;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
+            }
+            return;
+        case 'l':
+            N = data.size() / 8;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
+            }
+            return;
+        case 'f':
+            N = data.size() / 4;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutF4((reinterpret_cast<float*>(d))[i]);
+            }
+            return;
+        case 'd':
+            N = data.size() / 8;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutF8((reinterpret_cast<double*>(d))[i]);
+            }
+            return;
+        default:
+            std::ostringstream err;
+            err << "Tried to dump property with invalid type '";
+            err << type << "'!";
+            throw DeadlyExportError(err.str());
     }
     }
 }
 }
 
 
-void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
-{
+void FBXExportProperty::DumpAscii(Assimp::StreamWriterLE& outstream, int indent) {
     std::ostringstream ss;
     std::ostringstream ss;
     ss.imbue(std::locale::classic());
     ss.imbue(std::locale::classic());
     ss.precision(15); // this seems to match official FBX SDK exports
     ss.precision(15); // this seems to match official FBX SDK exports
@@ -240,8 +263,7 @@ void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
     outstream.PutString(ss.str());
     outstream.PutString(ss.str());
 }
 }
 
 
-void FBX::Property::DumpAscii(std::ostream& s, int indent)
-{
+void FBXExportProperty::DumpAscii(std::ostream& s, int indent) {
     // no writing type... or anything. just shove it into the stream.
     // no writing type... or anything. just shove it into the stream.
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     size_t N;
     size_t N;
@@ -360,5 +382,8 @@ void FBX::Property::DumpAscii(std::ostream& s, int indent)
     }
     }
 }
 }
 
 
+} // Namespace FBX
+} // Namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 24 - 24
code/FBXExportProperty.h

@@ -47,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
-
 #include <assimp/types.h> // aiMatrix4x4
 #include <assimp/types.h> // aiMatrix4x4
 #include <assimp/StreamWriter.h> // StreamWriterLE
 #include <assimp/StreamWriter.h> // StreamWriterLE
 
 
@@ -56,11 +55,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <ostream>
 #include <ostream>
 #include <type_traits> // is_void
 #include <type_traits> // is_void
 
 
+namespace Assimp {
 namespace FBX {
 namespace FBX {
-    class Property;
-}
 
 
-/** FBX::Property
+/** @brief FBX::Property
  *
  *
  *  Holds a value of any of FBX's recognized types,
  *  Holds a value of any of FBX's recognized types,
  *  each represented by a particular one-character code.
  *  each represented by a particular one-character code.
@@ -78,35 +76,34 @@ namespace FBX {
  *  S : string (array of 1-byte char)
  *  S : string (array of 1-byte char)
  *  R : raw data (array of bytes)
  *  R : raw data (array of bytes)
  */
  */
-class FBX::Property
-{
+class FBXExportProperty {
 public:
 public:
     // constructors for basic types.
     // constructors for basic types.
     // all explicit to avoid accidental typecasting
     // all explicit to avoid accidental typecasting
-    explicit Property(bool v);
+    explicit FBXExportProperty(bool v);
     // TODO: determine if there is actually a byte type,
     // TODO: determine if there is actually a byte type,
     // or if this always means <bool>. 'C' seems to imply <char>,
     // or if this always means <bool>. 'C' seems to imply <char>,
     // so possibly the above was intended to represent both.
     // so possibly the above was intended to represent both.
-    explicit Property(int16_t v);
-    explicit Property(int32_t v);
-    explicit Property(float v);
-    explicit Property(double v);
-    explicit Property(int64_t v);
+    explicit FBXExportProperty(int16_t v);
+    explicit FBXExportProperty(int32_t v);
+    explicit FBXExportProperty(float v);
+    explicit FBXExportProperty(double v);
+    explicit FBXExportProperty(int64_t v);
     // strings can either be stored as 'R' (raw) or 'S' (string) type
     // strings can either be stored as 'R' (raw) or 'S' (string) type
-    explicit Property(const char* c, bool raw=false);
-    explicit Property(const std::string& s, bool raw=false);
-    explicit Property(const std::vector<uint8_t>& r);
-    explicit Property(const std::vector<int32_t>& va);
-    explicit Property(const std::vector<int64_t>& va);
-    explicit Property(const std::vector<double>& va);
-    explicit Property(const std::vector<float>& va);
-    explicit Property(const aiMatrix4x4& vm);
+    explicit FBXExportProperty(const char* c, bool raw = false);
+    explicit FBXExportProperty(const std::string& s, bool raw = false);
+    explicit FBXExportProperty(const std::vector<uint8_t>& r);
+    explicit FBXExportProperty(const std::vector<int32_t>& va);
+    explicit FBXExportProperty(const std::vector<int64_t>& va);
+    explicit FBXExportProperty(const std::vector<double>& va);
+    explicit FBXExportProperty(const std::vector<float>& va);
+    explicit FBXExportProperty(const aiMatrix4x4& vm);
 
 
     // this will catch any type not defined above,
     // this will catch any type not defined above,
     // so that we don't accidentally convert something we don't want.
     // so that we don't accidentally convert something we don't want.
     // for example (const char*) --> (bool)... seriously wtf C++
     // for example (const char*) --> (bool)... seriously wtf C++
     template <class T>
     template <class T>
-    explicit Property(T v) : type('X') {
+    explicit FBXExportProperty(T v) : type('X') {
         static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION");
         static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION");
     } // note: no line wrap so it appears verbatim on the compiler error
     } // note: no line wrap so it appears verbatim on the compiler error
 
 
@@ -114,9 +111,9 @@ public:
     size_t size();
     size_t size();
 
 
     // write this property node as binary data to the given stream
     // write this property node as binary data to the given stream
-    void DumpBinary(Assimp::StreamWriterLE &s);
-    void DumpAscii(Assimp::StreamWriterLE &s, int indent=0);
-    void DumpAscii(std::ostream &s, int indent=0);
+    void DumpBinary(Assimp::StreamWriterLE& s);
+    void DumpAscii(Assimp::StreamWriterLE& s, int indent = 0);
+    void DumpAscii(std::ostream& s, int indent = 0);
     // note: make sure the ostream is in classic "C" locale
     // note: make sure the ostream is in classic "C" locale
 
 
 private:
 private:
@@ -124,6 +121,9 @@ private:
     std::vector<uint8_t> data;
     std::vector<uint8_t> data;
 };
 };
 
 
+} // Namespace FBX
+} // Namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
 #endif // AI_FBXEXPORTPROPERTY_H_INC
 #endif // AI_FBXEXPORTPROPERTY_H_INC

+ 59 - 25
code/FBXExporter.cpp

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXExportNode.h"
 #include "FBXExportNode.h"
 #include "FBXExportProperty.h"
 #include "FBXExportProperty.h"
 #include "FBXCommon.h"
 #include "FBXCommon.h"
+#include "FBXUtil.h"
 
 
 #include <assimp/version.h> // aiGetVersion
 #include <assimp/version.h> // aiGetVersion
 #include <assimp/IOSystem.hpp>
 #include <assimp/IOSystem.hpp>
@@ -73,7 +74,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian
 const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian
 
 
+using namespace Assimp;
+using namespace Assimp::FBX;
+
 // some constants that we'll use for writing metadata
 // some constants that we'll use for writing metadata
+namespace Assimp {
 namespace FBX {
 namespace FBX {
     const std::string EXPORT_VERSION_STR = "7.4.0";
     const std::string EXPORT_VERSION_STR = "7.4.0";
     const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015
     const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015
@@ -92,11 +97,6 @@ namespace FBX {
         ";------------------------------------------------------------------";
         ";------------------------------------------------------------------";
 }
 }
 
 
-using namespace Assimp;
-using namespace FBX;
-
-namespace Assimp {
-
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Worker function for exporting a scene to binary FBX.
     // Worker function for exporting a scene to binary FBX.
     // Prototyped and registered in Exporter.cpp
     // Prototyped and registered in Exporter.cpp
@@ -121,6 +121,7 @@ namespace Assimp {
         IOSystem* pIOSystem,
         IOSystem* pIOSystem,
         const aiScene* pScene,
         const aiScene* pScene,
         const ExportProperties* pProperties
         const ExportProperties* pProperties
+
     ){
     ){
         // initialize the exporter
         // initialize the exporter
         FBXExporter exporter(pScene, pProperties);
         FBXExporter exporter(pScene, pProperties);
@@ -1393,10 +1394,6 @@ void FBXExporter::WriteObjects ()
 
 
     // FbxVideo - stores images used by textures.
     // FbxVideo - stores images used by textures.
     for (const auto &it : uid_by_image) {
     for (const auto &it : uid_by_image) {
-        if (it.first.compare(0, 1, "*") == 0) {
-            // TODO: embedded textures
-            continue;
-        }
         FBX::Node n("Video");
         FBX::Node n("Video");
         const int64_t& uid = it.second;
         const int64_t& uid = it.second;
         const std::string name = ""; // TODO: ... name???
         const std::string name = ""; // TODO: ... name???
@@ -1406,7 +1403,33 @@ void FBXExporter::WriteObjects ()
         // TODO: get full path... relative path... etc... ugh...
         // TODO: get full path... relative path... etc... ugh...
         // for now just use the same path for everything,
         // for now just use the same path for everything,
         // and hopefully one of them will work out.
         // and hopefully one of them will work out.
-        const std::string& path = it.first;
+        std::string path = it.first;
+        // try get embedded texture
+        const aiTexture* embedded_texture = mScene->GetEmbeddedTexture(it.first.c_str());
+        if (embedded_texture != nullptr) {
+            // change the path (use original filename, if available. If name is empty, concatenate texture index with file extension)
+            std::stringstream newPath;
+            if (embedded_texture->mFilename.length > 0) {
+                newPath << embedded_texture->mFilename.C_Str();
+            } else if (embedded_texture->achFormatHint[0]) {
+                int texture_index = std::stoi(path.substr(1, path.size() - 1));
+                newPath << texture_index << "." << embedded_texture->achFormatHint;
+            }
+            path = newPath.str();
+            // embed the texture
+            size_t texture_size = static_cast<size_t>(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u));
+            if (binary) {
+                // embed texture as binary data
+                std::vector<uint8_t> tex_data;
+                tex_data.resize(texture_size);
+                memcpy(&tex_data[0], (char*)embedded_texture->pcData, texture_size);
+                n.AddChild("Content", tex_data);
+            } else {
+                // embed texture in base64 encoding
+                std::string encoded_texture = FBX::Util::EncodeBase64((char*)embedded_texture->pcData, texture_size);
+                n.AddChild("Content", encoded_texture);
+            }
+        }
         p.AddP70("Path", "KString", "XRefUrl", "", path);
         p.AddP70("Path", "KString", "XRefUrl", "", path);
         n.AddChild(p);
         n.AddChild(p);
         n.AddChild("UseMipMap", int32_t(0));
         n.AddChild("UseMipMap", int32_t(0));
@@ -1419,17 +1442,17 @@ void FBXExporter::WriteObjects ()
     // referenced by material_index/texture_type pairs.
     // referenced by material_index/texture_type pairs.
     std::map<std::pair<size_t,size_t>,int64_t> texture_uids;
     std::map<std::pair<size_t,size_t>,int64_t> texture_uids;
     const std::map<aiTextureType,std::string> prop_name_by_tt = {
     const std::map<aiTextureType,std::string> prop_name_by_tt = {
-        {aiTextureType_DIFFUSE, "DiffuseColor"},
-        {aiTextureType_SPECULAR, "SpecularColor"},
-        {aiTextureType_AMBIENT, "AmbientColor"},
-        {aiTextureType_EMISSIVE, "EmissiveColor"},
-        {aiTextureType_HEIGHT, "Bump"},
-        {aiTextureType_NORMALS, "NormalMap"},
-        {aiTextureType_SHININESS, "ShininessExponent"},
-        {aiTextureType_OPACITY, "TransparentColor"},
+        {aiTextureType_DIFFUSE,      "DiffuseColor"},
+        {aiTextureType_SPECULAR,     "SpecularColor"},
+        {aiTextureType_AMBIENT,      "AmbientColor"},
+        {aiTextureType_EMISSIVE,     "EmissiveColor"},
+        {aiTextureType_HEIGHT,       "Bump"},
+        {aiTextureType_NORMALS,      "NormalMap"},
+        {aiTextureType_SHININESS,    "ShininessExponent"},
+        {aiTextureType_OPACITY,      "TransparentColor"},
         {aiTextureType_DISPLACEMENT, "DisplacementColor"},
         {aiTextureType_DISPLACEMENT, "DisplacementColor"},
         //{aiTextureType_LIGHTMAP, "???"},
         //{aiTextureType_LIGHTMAP, "???"},
-        {aiTextureType_REFLECTION, "ReflectionColor"}
+        {aiTextureType_REFLECTION,   "ReflectionColor"}
         //{aiTextureType_UNKNOWN, ""}
         //{aiTextureType_UNKNOWN, ""}
     };
     };
     for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
     for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
@@ -1575,11 +1598,22 @@ void FBXExporter::WriteObjects ()
     // one sticky point is that the number of vertices may not match,
     // one sticky point is that the number of vertices may not match,
     // because assimp splits vertices by normal, uv, etc.
     // because assimp splits vertices by normal, uv, etc.
 
 
+    // functor for aiNode sorting
+    struct SortNodeByName
+    {
+        bool operator()(const aiNode *lhs, const aiNode *rhs) const
+        {
+            return strcmp(lhs->mName.C_Str(), rhs->mName.C_Str()) < 0;
+        }
+    };
+
     // first we should mark the skeleton for each mesh.
     // first we should mark the skeleton for each mesh.
     // the skeleton must include not only the aiBones,
     // the skeleton must include not only the aiBones,
     // but also all their parent nodes.
     // but also all their parent nodes.
     // anything that affects the position of any bone node must be included.
     // anything that affects the position of any bone node must be included.
-    std::vector<std::set<const aiNode*>> skeleton_by_mesh(mScene->mNumMeshes);
+    // Use SorNodeByName to make sure the exported result will be the same across all systems
+    // Otherwise the aiNodes of the skeleton would be sorted based on the pointer address, which isn't consistent
+    std::vector<std::set<const aiNode*, SortNodeByName>> skeleton_by_mesh(mScene->mNumMeshes);
     // at the same time we can build a list of all the skeleton nodes,
     // at the same time we can build a list of all the skeleton nodes,
     // which will be used later to mark them as type "limbNode".
     // which will be used later to mark them as type "limbNode".
     std::unordered_set<const aiNode*> limbnodes;
     std::unordered_set<const aiNode*> limbnodes;
@@ -1587,7 +1621,7 @@ void FBXExporter::WriteObjects ()
     std::map<std::string,aiNode*> node_by_bone;
     std::map<std::string,aiNode*> node_by_bone;
     for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
     for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
         const aiMesh* m = mScene->mMeshes[mi];
         const aiMesh* m = mScene->mMeshes[mi];
-        std::set<const aiNode*> skeleton;
+        std::set<const aiNode*, SortNodeByName> skeleton;
         for (size_t bi =0; bi < m->mNumBones; ++bi) {
         for (size_t bi =0; bi < m->mNumBones; ++bi) {
             const aiBone* b = m->mBones[bi];
             const aiBone* b = m->mBones[bi];
             const std::string name(b->mName.C_Str());
             const std::string name(b->mName.C_Str());
@@ -1728,7 +1762,7 @@ void FBXExporter::WriteObjects ()
         aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
         aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
 
 
         // now make a subdeformer for each bone in the skeleton
         // now make a subdeformer for each bone in the skeleton
-        const std::set<const aiNode*> &skeleton = skeleton_by_mesh[mi];
+        const std::set<const aiNode*, SortNodeByName> skeleton= skeleton_by_mesh[mi];
         for (const aiNode* bone_node : skeleton) {
         for (const aiNode* bone_node : skeleton) {
             // if there's a bone for this node, find it
             // if there's a bone for this node, find it
             const aiBone* b = nullptr;
             const aiBone* b = nullptr;
@@ -2237,8 +2271,8 @@ void FBXExporter::WriteModelNode(
 
 
     // not sure what these are for,
     // not sure what these are for,
     // but they seem to be omnipresent
     // but they seem to be omnipresent
-    m.AddChild("Shading", Property(true));
-    m.AddChild("Culling", Property("CullingOff"));
+    m.AddChild("Shading", FBXExportProperty(true));
+    m.AddChild("Culling", FBXExportProperty("CullingOff"));
 
 
     m.Dump(outstream, binary, 1);
     m.Dump(outstream, binary, 1);
 }
 }
@@ -2351,7 +2385,7 @@ void FBXExporter::WriteModelNodes(
         na.AddProperties(
         na.AddProperties(
             node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
             node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
         );
         );
-        na.AddChild("TypeFlags", Property("Skeleton"));
+        na.AddChild("TypeFlags", FBXExportProperty("Skeleton"));
         na.Dump(outstream, binary, 1);
         na.Dump(outstream, binary, 1);
         // and connect them
         // and connect them
         connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
         connections.emplace_back("C", "OO", node_attribute_uid, node_uid);

+ 25 - 14
code/FBXImportSettings.h

@@ -53,19 +53,22 @@ namespace FBX {
 struct ImportSettings
 struct ImportSettings
 {
 {
     ImportSettings()
     ImportSettings()
-        : strictMode(true)
-        , readAllLayers(true)
-        , readAllMaterials(false)
-        , readMaterials(true)
-        , readTextures(true)
-        , readCameras(true)
-        , readLights(true)
-        , readAnimations(true)
-        , readWeights(true)
-        , preservePivots(true)
-        , optimizeEmptyAnimationCurves(true)
-        , useLegacyEmbeddedTextureNaming(false)
-    {}
+    : strictMode(true)
+    , readAllLayers(true)
+    , readAllMaterials(false)
+    , readMaterials(true)
+    , readTextures(true)
+    , readCameras(true)
+    , readLights(true)
+    , readAnimations(true)
+    , readWeights(true)
+    , preservePivots(true)
+    , optimizeEmptyAnimationCurves(true)
+    , useLegacyEmbeddedTextureNaming(false)
+    , removeEmptyBones( true )
+    , convertToMeters( false ) {
+        // empty
+    }
 
 
 
 
     /** enable strict mode:
     /** enable strict mode:
@@ -141,8 +144,16 @@ struct ImportSettings
     bool optimizeEmptyAnimationCurves;
     bool optimizeEmptyAnimationCurves;
 
 
     /** use legacy naming for embedded textures eg: (*0, *1, *2)
     /** use legacy naming for embedded textures eg: (*0, *1, *2)
-    **/
+    */
     bool useLegacyEmbeddedTextureNaming;
     bool useLegacyEmbeddedTextureNaming;
+
+    /** Empty bones shall be removed
+    */
+    bool removeEmptyBones;
+
+    /** Set to true to perform a conversion from cm to meter after the import
+    */
+    bool convertToMeters;
 };
 };
 
 
 
 

+ 7 - 1
code/FBXImporter.cpp

@@ -140,6 +140,8 @@ void FBXImporter::SetupProperties(const Importer* pImp)
     settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
     settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
     settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
     settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
     settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
     settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
+    settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
+    settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -183,8 +185,12 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
         // take the raw parse-tree and convert it to a FBX DOM
         // take the raw parse-tree and convert it to a FBX DOM
         Document doc(parser,settings);
         Document doc(parser,settings);
 
 
+        FbxUnit unit(FbxUnit::cm);
+        if (settings.convertToMeters) {
+            unit = FbxUnit::m;
+        }
         // convert the FBX DOM to aiScene
         // convert the FBX DOM to aiScene
-        ConvertToAssimpScene(pScene,doc);
+        ConvertToAssimpScene(pScene,doc, settings.removeEmptyBones, unit);
 
 
         std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
         std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
     }
     }

+ 3 - 3
code/FBXMeshGeometry.cpp

@@ -568,15 +568,15 @@ void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, cons
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-static const std::string TangentIndexToken = "TangentIndex";
-static const std::string TangentsIndexToken = "TangentsIndex";
+static const char *TangentIndexToken = "TangentIndex";
+static const char *TangentsIndexToken = "TangentsIndex";
 
 
 void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
 void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
     const std::string& MappingInformationType,
     const std::string& MappingInformationType,
     const std::string& ReferenceInformationType)
     const std::string& ReferenceInformationType)
 {
 {
     const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
     const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
-    const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken.c_str() : TangentIndexToken.c_str();
+    const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken : TangentIndexToken;
     ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
     ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
         str,
         str,
         strIdx,
         strIdx,

+ 1 - 1
code/FBXParser.cpp

@@ -117,7 +117,7 @@ namespace FBX {
 Element::Element(const Token& key_token, Parser& parser)
 Element::Element(const Token& key_token, Parser& parser)
 : key_token(key_token)
 : key_token(key_token)
 {
 {
-    TokenPtr n = NULL;
+    TokenPtr n = nullptr;
     do {
     do {
         n = parser.AdvanceToNextToken();
         n = parser.AdvanceToNextToken();
         if(!n) {
         if(!n) {

+ 60 - 0
code/FBXUtil.cpp

@@ -157,6 +157,66 @@ size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
     return outLength;
     return outLength;
 }
 }
 
 
+static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+char EncodeBase64(char byte)
+{
+    return to_base64_string[(size_t)byte];
+}
+
+/** Encodes a block of 4 bytes to base64 encoding
+*
+*  @param bytes Bytes to encode.
+*  @param out_string String to write encoded values to.
+*  @param string_pos Position in out_string.*/
+void EncodeByteBlock(const char* bytes, std::string& out_string, size_t string_pos)
+{
+    char b0 = (bytes[0] & 0xFC) >> 2;
+    char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4);
+    char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6);
+    char b3 = (bytes[2] & 0x3F);
+
+    out_string[string_pos + 0] = EncodeBase64(b0);
+    out_string[string_pos + 1] = EncodeBase64(b1);
+    out_string[string_pos + 2] = EncodeBase64(b2);
+    out_string[string_pos + 3] = EncodeBase64(b3);
+}
+
+std::string EncodeBase64(const char* data, size_t length)
+{
+    // calculate extra bytes needed to get a multiple of 3
+    size_t extraBytes = 3 - length % 3;
+
+    // number of base64 bytes
+    size_t encodedBytes = 4 * (length + extraBytes) / 3;
+
+    std::string encoded_string(encodedBytes, '=');
+
+    // read blocks of 3 bytes
+    for (size_t ib3 = 0; ib3 < length / 3; ib3++)
+    {
+        const size_t iByte = ib3 * 3;
+        const size_t iEncodedByte = ib3 * 4;
+        const char* currData = &data[iByte];
+
+        EncodeByteBlock(currData, encoded_string, iEncodedByte);
+    }
+
+    // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed)
+    if (extraBytes > 0)
+    {
+        char finalBytes[4] = { 0,0,0,0 };
+        memcpy(&finalBytes[0], &data[length - length % 3], length % 3);
+
+        const size_t iEncodedByte = encodedBytes - 4;
+        EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte);
+
+        // add '=' at the end
+        for (size_t i = 0; i < 4 * extraBytes / 3; i++)
+            encoded_string[encodedBytes - i - 1] = '=';
+    }
+    return encoded_string;
+}
+
 } // !Util
 } // !Util
 } // !FBX
 } // !FBX
 } // !Assimp
 } // !Assimp

+ 9 - 0
code/FBXUtil.h

@@ -113,6 +113,15 @@ uint8_t DecodeBase64(char ch);
 *  @return size of the decoded data (number of bytes)*/
 *  @return size of the decoded data (number of bytes)*/
 size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
 size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
 
 
+char EncodeBase64(char byte);
+
+/** Encode bytes in base64-encoding
+*
+*  @param data Binary data to encode.
+*  @param inLength Number of bytes to encode.
+*  @return base64-encoded string*/
+std::string EncodeBase64(const char* data, size_t length);
+
 }
 }
 }
 }
 }
 }

+ 28 - 10
code/ObjFileParser.cpp

@@ -151,7 +151,7 @@ void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
                 } else if (*m_DataIt == 't') {
                 } else if (*m_DataIt == 't') {
                     // read in texture coordinate ( 2D or 3D )
                     // read in texture coordinate ( 2D or 3D )
                     ++m_DataIt;
                     ++m_DataIt;
-                    size_t dim = getVector(m_pModel->m_TextureCoord);
+                    size_t dim = getTexCoordVector(m_pModel->m_TextureCoord);
                     m_pModel->m_TextureCoordDim = std::max(m_pModel->m_TextureCoordDim, (unsigned int)dim);
                     m_pModel->m_TextureCoordDim = std::max(m_pModel->m_TextureCoordDim, (unsigned int)dim);
                 } else if (*m_DataIt == 'n') {
                 } else if (*m_DataIt == 'n') {
                     // Read in normal vector definition
                     // Read in normal vector definition
@@ -272,6 +272,17 @@ static bool isDataDefinitionEnd( const char *tmp ) {
     return false;
     return false;
 }
 }
 
 
+static bool isNanOrInf(const char * in) {
+    // Look for "nan" or "inf", case insensitive
+    if ((in[0] == 'N' || in[0] == 'n') && ASSIMP_strincmp(in, "nan", 3) == 0) {
+        return true;
+    }
+    else if ((in[0] == 'I' || in[0] == 'i') && ASSIMP_strincmp(in, "inf", 3) == 0) {
+        return true;
+    }
+    return false;
+}
+
 size_t ObjFileParser::getNumComponentsInDataDefinition() {
 size_t ObjFileParser::getNumComponentsInDataDefinition() {
     size_t numComponents( 0 );
     size_t numComponents( 0 );
     const char* tmp( &m_DataIt[0] );
     const char* tmp( &m_DataIt[0] );
@@ -285,7 +296,7 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
         if ( !SkipSpaces( &tmp ) ) {
         if ( !SkipSpaces( &tmp ) ) {
             break;
             break;
         }
         }
-        const bool isNum( IsNumeric( *tmp ) );
+        const bool isNum( IsNumeric( *tmp ) || isNanOrInf(tmp));
         SkipToken( tmp );
         SkipToken( tmp );
         if ( isNum ) {
         if ( isNum ) {
             ++numComponents;
             ++numComponents;
@@ -297,7 +308,7 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
     return numComponents;
     return numComponents;
 }
 }
 
 
-size_t ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
+size_t ObjFileParser::getTexCoordVector( std::vector<aiVector3D> &point3d_array ) {
     size_t numComponents = getNumComponentsInDataDefinition();
     size_t numComponents = getNumComponentsInDataDefinition();
     ai_real x, y, z;
     ai_real x, y, z;
     if( 2 == numComponents ) {
     if( 2 == numComponents ) {
@@ -319,6 +330,17 @@ size_t ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     } else {
     } else {
         throw DeadlyImportError( "OBJ: Invalid number of components" );
         throw DeadlyImportError( "OBJ: Invalid number of components" );
     }
     }
+
+    // Coerce nan and inf to 0 as is the OBJ default value
+    if (!std::isfinite(x))
+        x = 0;
+
+    if (!std::isfinite(y))
+        y = 0;
+
+    if (!std::isfinite(z))
+        z = 0;
+
     point3d_array.push_back( aiVector3D( x, y, z ) );
     point3d_array.push_back( aiVector3D( x, y, z ) );
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
     return numComponents;
     return numComponents;
@@ -429,13 +451,6 @@ void ObjFileParser::getFace( aiPrimitiveType type ) {
             if (type == aiPrimitiveType_POINT) {
             if (type == aiPrimitiveType_POINT) {
                 ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement");
                 ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement");
             }
             }
-            if (iPos == 0) {
-                //if there are no texture coordinates in the file, but normals
-                if (!vt && vn) {
-                    iPos = 1;
-                    iStep++;
-                }
-            }
             iPos++;
             iPos++;
         } else if( IsSpaceOrNewLine( *m_DataIt ) ) {
         } else if( IsSpaceOrNewLine( *m_DataIt ) ) {
             iPos = 0;
             iPos = 0;
@@ -452,6 +467,9 @@ void ObjFileParser::getFace( aiPrimitiveType type ) {
                 ++iStep;
                 ++iStep;
             }
             }
 
 
+            if (iPos == 1 && !vt && vn)
+                iPos = 2; // skip texture coords for normals if there are no tex coords
+
             if ( iVal > 0 ) {
             if ( iVal > 0 ) {
                 // Store parsed index
                 // Store parsed index
                 if ( 0 == iPos ) {
                 if ( 0 == iPos ) {

+ 1 - 1
code/ObjFileParser.h

@@ -96,7 +96,7 @@ protected:
     /// Get the number of components in a line.
     /// Get the number of components in a line.
     size_t getNumComponentsInDataDefinition();
     size_t getNumComponentsInDataDefinition();
     /// Stores the vector
     /// Stores the vector
-    size_t getVector( std::vector<aiVector3D> &point3d_array );
+    size_t getTexCoordVector( std::vector<aiVector3D> &point3d_array );
     /// Stores the following 3d vector.
     /// Stores the following 3d vector.
     void getVector3( std::vector<aiVector3D> &point3d_array );
     void getVector3( std::vector<aiVector3D> &point3d_array );
     /// Stores the following homogeneous vector as a 3D vector
     /// Stores the following homogeneous vector as a 3D vector

+ 13 - 20
code/ValidateDataStructure.cpp

@@ -46,8 +46,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *    the data structure returned by Assimp.
  *    the data structure returned by Assimp.
  */
  */
 
 
-
-
 // internal headers
 // internal headers
 #include "ValidateDataStructure.h"
 #include "ValidateDataStructure.h"
 #include <assimp/BaseImporter.h>
 #include <assimp/BaseImporter.h>
@@ -110,8 +108,8 @@ void ValidateDSProcess::ReportWarning(const char* msg,...)
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-inline int HasNameMatch(const aiString& in, aiNode* node)
-{
+inline
+int HasNameMatch(const aiString& in, aiNode* node) {
     int result = (node->mName == in ? 1 : 0 );
     int result = (node->mName == in ? 1 : 0 );
     for (unsigned int i = 0; i < node->mNumChildren;++i)    {
     for (unsigned int i = 0; i < node->mNumChildren;++i)    {
         result += HasNameMatch(in,node->mChildren[i]);
         result += HasNameMatch(in,node->mChildren[i]);
@@ -121,9 +119,8 @@ inline int HasNameMatch(const aiString& in, aiNode* node)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 template <typename T>
-inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size,
-    const char* firstName, const char* secondName)
-{
+inline
+void ValidateDSProcess::DoValidation(T** parray, unsigned int size, const char* firstName, const char* secondName) {
     // validate all entries
     // validate all entries
     if (size)
     if (size)
     {
     {
@@ -181,7 +178,8 @@ inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 template <typename T>
 inline
 inline
-void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName, const char* secondName) {
+void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName,
+        const char* secondName) {
     // validate all entries
     // validate all entries
     DoValidationEx(array,size,firstName,secondName);
     DoValidationEx(array,size,firstName,secondName);
 
 
@@ -201,9 +199,8 @@ void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size,
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 // Executes the post processing step on the given imported data.
-void ValidateDSProcess::Execute( aiScene* pScene)
-{
-    this->mScene = pScene;
+void ValidateDSProcess::Execute( aiScene* pScene) {
+    mScene = pScene;
     ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin");
     ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin");
 
 
     // validate the node graph of the scene
     // validate the node graph of the scene
@@ -516,13 +513,11 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void ValidateDSProcess::Validate( const aiMesh* pMesh,
-    const aiBone* pBone,float* afSum)
-{
+void ValidateDSProcess::Validate( const aiMesh* pMesh, const aiBone* pBone,float* afSum) {
     this->Validate(&pBone->mName);
     this->Validate(&pBone->mName);
 
 
     if (!pBone->mNumWeights)    {
     if (!pBone->mNumWeights)    {
-        ReportError("aiBone::mNumWeights is zero");
+        //ReportError("aiBone::mNumWeights is zero");
     }
     }
 
 
     // check whether all vertices affected by this bone are valid
     // check whether all vertices affected by this bone are valid
@@ -563,9 +558,6 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
     else {
     else {
     	ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
     	ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
     }
     }
-
-    // Animation duration is allowed to be zero in cases where the anim contains only a single key frame.
-    // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -746,8 +738,9 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
                     "AI_MATKEY_SHININESS_STRENGTH key is 0.0");
                     "AI_MATKEY_SHININESS_STRENGTH key is 0.0");
             }
             }
             break;
             break;
-        default: ;
-        };
+        default:
+            break;
+        }
     }
     }
 
 
     if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) {
     if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) {

+ 7 - 4
code/glTF2Importer.cpp

@@ -789,13 +789,16 @@ static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std
     delete[] indices16;
     delete[] indices16;
 }
 }
 
 
+static std::string GetNodeName(const Node& node)
+{
+    return node.name.empty() ? node.id : node.name;
+}
+
 aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
 aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
 {
 {
     Node& node = *ptr;
     Node& node = *ptr;
 
 
-    std::string nameOrId = node.name.empty() ? node.id : node.name;
-
-    aiNode* ainode = new aiNode(nameOrId);
+    aiNode* ainode = new aiNode(GetNodeName(node));
 
 
     if (!node.children.empty()) {
     if (!node.children.empty()) {
         ainode->mNumChildren = unsigned(node.children.size());
         ainode->mNumChildren = unsigned(node.children.size());
@@ -921,7 +924,7 @@ struct AnimationSamplers {
 aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& samplers)
 aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& samplers)
 {
 {
     aiNodeAnim* anim = new aiNodeAnim();
     aiNodeAnim* anim = new aiNodeAnim();
-    anim->mNodeName = node.name;
+    anim->mNodeName = GetNodeName(node);
 
 
     static const float kMillisecondsFromSeconds = 1000.f;
     static const float kMillisecondsFromSeconds = 1000.f;
 
 

+ 0 - 105
code/makefile.mingw

@@ -1,105 +0,0 @@
-### USE OF THIS MAKEFILE IS NOT RECOMMENDED.
-### It is no longer maintained. Use CMAKE instead.
-
-# ---------------------------------------------------------------------------
-# Makefile for Open Asset Import Library (MinGW32-make)
-# [email protected]
-#   - just a quick'n'dirty one, could be buggy ...
-#
-# Usage: mingw32-make -f makefile.mingw <target> <macros>
-
-# TARGETS:
-#   all                  Build a shared so from the whole library
-#   clean                Cleanup object files, prepare for rebuild
-#   static               Build a static library (*.a)
-
-# MACROS: (make clean before you change one)
-#   NOBOOST=1            Build against boost workaround
-#   SINGLETHREADED=1     Build single-threaded library
-#   DEBUG=1              Build debug build of library
-# 
-# ---------------------------------------------------------------------------
-
-# C++ object files
-OBJECTS   := $(patsubst %.cpp,%.o,  $(wildcard *.cpp)) 
-OBJECTS   += $(patsubst %.cpp,%.o,  $(wildcard extra/*.cpp)) 
-OBJECTS   += $(patsubst %.cpp,%.o,  $(wildcard ./../contrib/irrXML/*.cpp)) 
-
-# C object files 
-OBJECTSC  := $(patsubst %.c,%.oc,   $(wildcard ./../contrib/zlib/*.c))
-OBJECTSC  += $(patsubst %.c,%.oc,   $(wildcard ./../contrib/ConvertUTF/*.c))
-OBJECTSC  += $(patsubst %.c,%.oc,   $(wildcard ./../contrib/unzip/*.c))
-
-# Include flags for gcc
-INCLUDEFLAGS =
-
-# Preprocessor defines for gcc
-DEFINEFLAGS = 
-
-# Suffix for the output binary, represents build type
-NAMESUFFIX = 
-
-# Output path for binaries
-BINPATH = ../bin/mingw/
-
-# GCC compiler flags 
-CPPFLAGS=-Wall 
-
-# Setup environment for noboost build
-ifeq ($(NOBOOST),1)
-	SINGLETHREADED = 1
-	INCLUDEFLAGS  += -I./BoostWorkaround/
-	DEFINEFLAGS   += -DASSIMP_BUILD_BOOST_WORKAROUND 
-#	NAMESUFFIX    += -noboost
-else
-	# adjust this manually if your boost is stored elsewhere
-	INCLUDEFLAGS  += -I"C:/Program Files/boost/boost_1_38"
-	#INCLUDEFLAGS  += -I"$(BOOST_DIR)"
-
-endif
-
-# Setup environment for st build
-ifeq ($(SINGLETHREADED),1)
-	DEFINEFLAGS   += -DASSIMP_BUILD_SINGLETHREADED
-#	NAMESUFFIX    += -st
-endif
-
-# Setup environment for debug build
-ifeq ($(DEBUG),1)
-	DEFINEFLAGS   += -D_DEBUG -DDEBUG
-	CPPFLAGS      += -g
-#	NAMESUFFIX    += -debug
-else
-	CPPFLAGS      += -O2 -s
-	DEFINEFLAGS   += -DNDEBUG -D_NDEBUG
-endif
-
-# Output name of shared library
-SHARED_TARGET = $(BINPATH)/libassimp$(NAMESUFFIX).so
-
-# Output name of static library
-STATIC = $(BINPATH)/libassimp$(NAMESUFFIX).a
-
-# target: all
-# usage : build a shared library (*.so)
-all:	$(SHARED_TARGET)
-
-$(SHARED_TARGET):  $(OBJECTS)  $(OBJECTSC)
-	gcc -o $@ $(OBJECTS) $(OBJECTSC) -shared -lstdc++ 
-%.o:%.cpp
-	$(CXX) -c  $(CPPFLAGS) $? -o $@ $(INCLUDEFLAGS) $(DEFINEFLAGS)
-%.oc:%.c
-	$(CXX) -x c -c -ansi $(CPPFLAGS) $? -o $@ 
-
-# target: clean
-# usage : cleanup all object files, prepare for a rebuild
-.PHONY: clean
-clean:
-	-del *.o .\..\contrib\irrXML\*.o .\..\contrib\zlib\*.oc .\..\contrib\unzip\*.oc .\..\contrib\ConvertUTF\*.oc
-
-# target: static
-# usage : build a static library (*.a)
-static:    $(STATIC) 
-$(STATIC):    $(OBJECTS) $(OBJECTSC)
-	ar rcs $@ $(OBJECTS) $(OBJECTSC)
-

+ 5 - 5
code/res/assimp.rc

@@ -1,7 +1,7 @@
 // Microsoft Visual C++ generated resource script.
 // Microsoft Visual C++ generated resource script.
 //
 //
 #include "resource.h"
 #include "resource.h"
-#include "..\..\revision.h"
+#include "revision.h"
 
 
 #define APSTUDIO_READONLY_SYMBOLS
 #define APSTUDIO_READONLY_SYMBOLS
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
@@ -31,8 +31,8 @@ LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
 //
 //
 
 
 VS_VERSION_INFO VERSIONINFO
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,1,SVNRevision, 0
- PRODUCTVERSION 1,1,SVNRevision,0
+ FILEVERSION VER_FILEVERSION
+ PRODUCTVERSION VER_FILEVERSION
  FILEFLAGSMASK 0x17L
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
 #ifdef _DEBUG
  FILEFLAGS 0x1L
  FILEFLAGS 0x1L
@@ -50,12 +50,12 @@ BEGIN
             VALUE "Comments", "Licensed under a 3-clause BSD license"
             VALUE "Comments", "Licensed under a 3-clause BSD license"
             VALUE "CompanyName", "assimp team"
             VALUE "CompanyName", "assimp team"
             VALUE "FileDescription", "Open Asset Import Library"
             VALUE "FileDescription", "Open Asset Import Library"
-            VALUE "FileVersion", 1,1,SVNRevision,0
+            VALUE "FileVersion", VER_FILEVERSION
             VALUE "InternalName", "assimp "
             VALUE "InternalName", "assimp "
             VALUE "LegalCopyright", "Copyright (C) 2006-2010"
             VALUE "LegalCopyright", "Copyright (C) 2006-2010"
             VALUE "OriginalFilename", "assimpNN.dll"
             VALUE "OriginalFilename", "assimpNN.dll"
             VALUE "ProductName", "Open Asset Import Library"
             VALUE "ProductName", "Open Asset Import Library"
-            VALUE "ProductVersion", 1,1,SVNRevision,0
+            VALUE "ProductVersion", VER_FILEVERSION_STR
 		,0
 		,0
         END
         END
     END
     END

+ 1 - 1
include/assimp/Exporter.hpp

@@ -190,7 +190,7 @@ public:
     * @note Use aiCopyScene() to get a modifiable copy of a previously
     * @note Use aiCopyScene() to get a modifiable copy of a previously
     *   imported scene. */
     *   imported scene. */
     const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId,
     const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId,
-        unsigned int pPreprocessing = 0u, const ExportProperties* = nullptr);
+        unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
     const aiExportDataBlob* ExportToBlob(  const aiScene* pScene, const std::string& pFormatId,
     const aiExportDataBlob* ExportToBlob(  const aiScene* pScene, const std::string& pFormatId,
         unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
         unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
 
 

+ 4 - 0
include/assimp/color4.inl

@@ -85,6 +85,8 @@ AI_FORCE_INLINE TReal aiColor4t<TReal>::operator[](unsigned int i) const {
             return g;
             return g;
         case 2:
         case 2:
             return b;
             return b;
+        case 3:
+            return a;
         default:
         default:
             break;
             break;
     }
     }
@@ -100,6 +102,8 @@ AI_FORCE_INLINE TReal& aiColor4t<TReal>::operator[](unsigned int i) {
             return g;
             return g;
         case 2:
         case 2:
             return b;
             return b;
+        case 3:
+            return a;
         default:
         default:
             break;
             break;
     }
     }

+ 20 - 5
include/assimp/config.h.in

@@ -651,13 +651,28 @@ enum aiComponent
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief Set whether the fbx importer will use the legacy embedded texture naming.
 /** @brief Set whether the fbx importer will use the legacy embedded texture naming.
-*
-* The default value is false (0)
-* Property type: bool
-*/
+ *
+ * The default value is false (0)
+ * Property type: bool
+ */
 #define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \
 #define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \
 	"AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
 	"AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
-	
+
+// ---------------------------------------------------------------------------
+/** @brief  Set wether the importer shall not remove empty bones.
+ *  
+ *  Empty bone are often used to define connections for other models.
+ */
+#define AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES \
+    "AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES"
+
+
+// ---------------------------------------------------------------------------
+/** @brief  Set wether the FBX importer shall convert the unit from cm to m.
+ */
+#define AI_CONFIG_FBX_CONVERT_TO_M \
+    "AI_CONFIG_FBX_CONVERT_TO_M"
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief  Set the vertex animation keyframe to be imported
 /** @brief  Set the vertex animation keyframe to be imported
  *
  *

+ 8 - 0
include/assimp/scene.h

@@ -389,6 +389,14 @@ struct aiScene
 
 
     //! Returns an embedded texture
     //! Returns an embedded texture
     const aiTexture* GetEmbeddedTexture(const char* filename) const {
     const aiTexture* GetEmbeddedTexture(const char* filename) const {
+        // lookup using texture ID (if referenced like: "*1", "*2", etc.)
+        if ('*' == *filename) {
+            int index = std::atoi(filename + 1);
+            if (0 > index || mNumTextures <= static_cast<unsigned>(index))
+                return nullptr;
+            return mTextures[index];
+        }
+        // lookup using filename
         const char* shortFilename = GetShortFilename(filename);
         const char* shortFilename = GetShortFilename(filename);
         for (unsigned int i = 0; i < mNumTextures; i++) {
         for (unsigned int i = 0; i < mNumTextures; i++) {
             const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str());
             const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str());

+ 0 - 103
packaging/windows-innosetup/script.iss

@@ -1,103 +0,0 @@
-; Setup script for use with Inno Setup.
-
-[Setup]
-AppName=Open Asset Import Library - SDK
-AppVerName=Open Asset Import Library - SDK (v4.1.0)
-DefaultDirName={pf}\Assimp
-DefaultGroupName=Assimp
-UninstallDisplayIcon={app}\bin\x86\assimp.exe
-OutputDir=out
-AppCopyright=Assimp Development Team
-SetupIconFile=..\..\tools\shared\assimp_tools_icon.ico
-WizardImageFile=compiler:WizModernImage-IS.BMP
-WizardSmallImageFile=compiler:WizModernSmallImage-IS.BMP
-LicenseFile=License.rtf
-OutputBaseFileName=assimp-sdk-4.1.0-setup
-VersionInfoVersion=4.1.0.0
-VersionInfoTextVersion=4.1.0
-VersionInfoCompany=Assimp Development Team
-ArchitecturesInstallIn64BitMode=x64
-
-[Types]
-Name: "full";    Description: "Full installation"
-Name: "compact"; Description: "Compact installation, no test models or language bindings"
-Name: "custom";  Description: "Custom installation"; Flags: iscustom
-
-[Components]
-Name: "main";        Description: "Main Files (32 and 64 Bit)"; Types: full compact custom; Flags: fixed
-Name: "tools";       Description: "Asset Viewer & Command Line Tools (32 and 64 Bit)"; Types: full compact
-Name: "help";        Description: "Help Files"; Types: full compact
-Name: "samples";     Description: "Samples"; Types: full
-Name: "test";        Description: "Test Models (BSD-licensed)"; Types: full
-Name: "test_nonbsd"; Description: "Test Models (other (free) licenses)"; Types: full
-;Name: "pyassimp";    Description: "Python Bindings"; Types: full
-;Name: "dassimp";     Description: "D Bindings"; Types: full
-;Name: "assimp_net";  Description: "C#/.NET Bindings"; Types: full
-
-[Run]
-;Filename: "{app}\stub\vc_redist.x86.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2017 redistributable package (32 Bit)"; Check: not IsWin64
-Filename: "{app}\stub\vc_redist.x64.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2017 redistributable package (64 Bit)"; Check: IsWin64
-
-[Files]
-Source: "readme_installer.txt"; DestDir: "{app}"; Flags: isreadme
-
-; Installer stub
-;Source: "vc_redist.x86.exe"; DestDir: "{app}\stub\"; Check: not IsWin64
-Source: "vc_redist.x64.exe"; DestDir: "{app}\stub\"; Check: IsWin64
-
-; Common stuff
-Source: "..\..\CREDITS"; DestDir: "{app}"
-Source: "..\..\LICENSE"; DestDir: "{app}"
-Source: "..\..\README"; DestDir: "{app}"
-Source: "WEB"; DestDir: "{app}"
-
-Source: "..\..\scripts\*"; DestDir: "{app}\scripts"; Flags: recursesubdirs
-
-; x86 binaries
-;Source: "..\..\bin\release\x86\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x86"
-;Source: "..\..\bin\release\x86\assimp_viewer.exe";    DestDir: "{app}\bin\x86"; Components: tools
-;Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll";     DestDir: "{app}\bin\x86"; Components: tools
-;Source: "C:\Windows\SysWOW64\D3DX9_42.dll";           DestDir: "{app}\bin\x86"; Components: tools
-;Source: "..\..\bin\release\x86\assimp.exe";           DestDir: "{app}\bin\x86"; Components: tools
-
-; x64 binaries
-Source: "..\..\bin\release\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x64"
-Source: "..\..\bin\release\assimp_viewer.exe";    DestDir: "{app}\bin\x64"; Components: tools
-Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll"; DestDir: "{app}\bin\x64"; DestName: "D3DCompiler_42.dll"; Components: tools
-Source: "C:\Windows\SysWOW64\D3DX9_42.dll";       DestDir: "{app}\bin\x64"; DestName: "D3DX9_42.dll"; Components: tools
-Source: "..\..\bin\release\assimp.exe";           DestDir: "{app}\bin\x64"; Components: tools
-
-; Documentation
-;Source: "..\..\doc\AssimpDoc_Html\AssimpDoc.chm"; DestDir: "{app}\doc"; Components: help
-;Source: "..\..\doc\AssimpCmdDoc_Html\AssimpCmdDoc.chm"; DestDir: "{app}\doc"; Components: help
-;Source: "..\..\doc\datastructure.xml"; DestDir: "{app}\doc"; Components: help
-
-; Import libraries
-;Source: "..\..\lib\release\x86\assimp.lib"; DestDir: "{app}\lib\x86"
-Source: "..\..\lib\release\assimp-vc140-mt.lib"; DestDir: "{app}\lib\x64"
-
-; Samples
-Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Components: samples
-
-; Include files
-Source: "..\..\include\*"; DestDir: "{app}\include"; Flags: recursesubdirs
-
-; dAssimp
-;Source: "..\..\port\dAssimp\*"; DestDir: "{app}\port\D"; Flags: recursesubdirs; Components: dassimp
-
-; Assimp.NET
-;Source: "..\..\port\Assimp.NET\*"; DestDir: "{app}\port\C#"; Flags: recursesubdirs; Components: assimp_net
-
-; PyAssimp
-;Source: "..\..\port\PyAssimp\*"; DestDir: "{app}\port\Python"; Excludes: "*.pyc,*.dll"; Flags: recursesubdirs; Components: pyassimp
-
-; Test repository
-;Source: "..\..\test\models\*"; DestDir: "{app}\test\models"; Flags: recursesubdirs; Components: test
-;Source: "..\..\test\regression\*"; DestDir: "{app}\test\regression"; Flags: recursesubdirs; Components: test
-;Source: "..\..\test\models-nonbsd\*"; DestDir: "{app}\test\models-nonbsd"; Flags: recursesubdirs; Components: test_nonbsd
-
-[Icons]
-Name: "{group}\Assimp Manual"; Filename: "{app}\doc\AssimpDoc.chm" ; Components: help
-Name: "{group}\Assimp Command Line Manual"; Filename: "{app}\doc\AssimpCmdDoc.chm"; Components: help
-Name: "{group}\AssimpView"; Filename: "{app}\bin\x64\assimp_view.exe"; Components: tools; Check: IsWin64
-Name: "{group}\AssimpView"; Filename: "{app}\bin\x86\assimp_view.exe"; Components: tools; Check: not IsWin64

+ 74 - 0
packaging/windows-innosetup/script_x64.iss

@@ -0,0 +1,74 @@
+; Setup script for use with Inno Setup.
+
+[Setup]
+AppName=Open Asset Import Library - SDK
+AppVerName=Open Asset Import Library - SDK (v5.0.0)
+DefaultDirName={pf}\Assimp
+DefaultGroupName=Assimp
+UninstallDisplayIcon={app}\bin\x64\assimp.exe
+OutputDir=out
+AppCopyright=Assimp Development Team
+SetupIconFile=..\..\tools\shared\assimp_tools_icon.ico
+WizardImageFile=compiler:WizModernImage-IS.BMP
+WizardSmallImageFile=compiler:WizModernSmallImage-IS.BMP
+LicenseFile=License.rtf
+OutputBaseFileName=assimp-sdk-5.0.0-setup
+VersionInfoVersion=5.0.0.0
+VersionInfoTextVersion=5.0.0
+VersionInfoCompany=Assimp Development Team
+ArchitecturesInstallIn64BitMode=x64
+
+[Types]
+Name: "full";    Description: "Full installation"
+Name: "compact"; Description: "Compact installation, no test models or language bindings"
+Name: "custom";  Description: "Custom installation"; Flags: iscustom
+
+[Components]
+Name: "main";        Description: "Main Files ( 64 Bit )"; Types: full compact custom; Flags: fixed
+Name: "tools";       Description: "Asset Viewer & Command Line Tools (32 and 64 Bit)"; Types: full compact
+Name: "help";        Description: "Help Files"; Types: full compact
+Name: "samples";     Description: "Samples"; Types: full
+Name: "test";        Description: "Test Models (BSD-licensed)"; Types: full
+Name: "test_nonbsd"; Description: "Test Models (other (free) licenses)"; Types: full
+
+[Run]
+Filename: "{app}\stub\vc_redist.x64.exe"; Parameters: "/qb /passive /quiet"; StatusMsg: "Installing VS2017 redistributable package (64 Bit)"; Check: IsWin64
+
+[Files]
+Source: "readme_installer.txt"; DestDir: "{app}"; Flags: isreadme
+
+; Installer stub
+Source: "vc_redist.x64.exe"; DestDir: "{app}\stub\"; Check: IsWin64
+
+; Common stuff
+Source: "..\..\CREDITS"; DestDir: "{app}"
+Source: "..\..\LICENSE"; DestDir: "{app}"
+Source: "..\..\README"; DestDir: "{app}"
+Source: "WEB"; DestDir: "{app}"
+
+Source: "..\..\scripts\*"; DestDir: "{app}\scripts"; Flags: recursesubdirs
+
+; x64 binaries
+Source: "..\..\bin\release\assimp-vc141-mt.dll";  DestDir: "{app}\bin\x64"
+Source: "..\..\bin\release\assimp_viewer.exe";    DestDir: "{app}\bin\x64"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll"; DestDir: "{app}\bin\x64"; DestName: "D3DCompiler_42.dll"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DX9_42.dll";       DestDir: "{app}\bin\x64"; DestName: "D3DX9_42.dll"; Components: tools
+Source: "..\..\bin\release\assimp.exe";           DestDir: "{app}\bin\x64"; Components: tools
+
+; Import libraries
+Source: "..\..\lib\release\assimp-vc141-mt.lib"; DestDir: "{app}\lib\x64"
+
+; Samples
+Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Components: samples
+
+; Include files
+Source: "..\..\include\*"; DestDir: "{app}\include"; Flags: recursesubdirs
+
+; CMake files
+Source: "..\..\cmake-modules\*"; DestDir: "{app}\cmake-modules"; Flags: recursesubdirs
+
+[Icons]
+; Name: "{group}\Assimp Manual"; Filename: "{app}\doc\AssimpDoc.chm" ; Components: help
+; Name: "{group}\Assimp Command Line Manual"; Filename: "{app}\doc\AssimpCmdDoc.chm"; Components: help
+; Name: "{group}\AssimpView"; Filename: "{app}\bin\x64\assimp_view.exe"; Components: tools; Check: IsWin64
+; Name: "{group}\AssimpView"; Filename: "{app}\bin\x86\assimp_view.exe"; Components: tools; Check: not IsWin64

+ 75 - 0
packaging/windows-innosetup/script_x86.iss

@@ -0,0 +1,75 @@
+; Setup script for use with Inno Setup.
+
+[Setup]
+AppName=Open Asset Import Library - SDK
+AppVerName=Open Asset Import Library - SDK (v5.0.0)
+DefaultDirName={pf}\Assimp
+DefaultGroupName=Assimp
+UninstallDisplayIcon={app}\bin\x86\assimp.exe
+OutputDir=out
+AppCopyright=Assimp Development Team
+SetupIconFile=..\..\tools\shared\assimp_tools_icon.ico
+WizardImageFile=compiler:WizModernImage-IS.BMP
+WizardSmallImageFile=compiler:WizModernSmallImage-IS.BMP
+LicenseFile=License.rtf
+OutputBaseFileName=assimp-sdk-5.0.0-setup
+VersionInfoVersion=4.1.0.0
+VersionInfoTextVersion=4.1.0
+VersionInfoCompany=Assimp Development Team
+;ArchitecturesInstallIn64BitMode=x64
+
+[Types]
+Name: "full";    Description: "Full installation"
+Name: "compact"; Description: "Compact installation, no test models or language bindings"
+Name: "custom";  Description: "Custom installation"; Flags: iscustom
+
+[Components]
+Name: "main";        Description: "Main Files (32 and 64 Bit)"; Types: full compact custom; Flags: fixed
+Name: "tools";       Description: "Asset Viewer & Command Line Tools (32 and 64 Bit)"; Types: full compact
+Name: "help";        Description: "Help Files"; Types: full compact
+Name: "samples";     Description: "Samples"; Types: full
+Name: "test";        Description: "Test Models (BSD-licensed)"; Types: full
+Name: "test_nonbsd"; Description: "Test Models (other (free) licenses)"; Types: full
+
+[Run]
+Filename: "{app}\stub\vc_redist.x86.exe"; Parameters: "/qb /passive /quiet"; StatusMsg: "Installing VS2017 redistributable package (32 Bit)"; Check: not IsWin64
+
+[Files]
+Source: "readme_installer.txt"; DestDir: "{app}"; Flags: isreadme
+
+; Installer stub
+Source: "vc_redist.x86.exe"; DestDir: "{app}\stub\"; Check: not IsWin64
+
+; Common stuff
+Source: "..\..\CREDITS"; DestDir: "{app}"
+Source: "..\..\LICENSE"; DestDir: "{app}"
+Source: "..\..\README"; DestDir: "{app}"
+Source: "WEB"; DestDir: "{app}"
+
+Source: "..\..\scripts\*"; DestDir: "{app}\scripts"; Flags: recursesubdirs
+
+; x86 binaries
+Source: "..\..\bin\release\assimp-vc141-mt.dll";  DestDir: "{app}\bin\x86"
+Source: "..\..\bin\release\assimp_viewer.exe";    DestDir: "{app}\bin\x86"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll";     DestDir: "{app}\bin\x86"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DX9_42.dll";           DestDir: "{app}\bin\x86"; Components: tools
+Source: "..\..\bin\release\assimp.exe";           DestDir: "{app}\bin\x86"; Components: tools
+
+
+; Import libraries
+Source: "..\..\lib\release\assimp-vc141-mt.lib"; DestDir: "{app}\lib\x86"
+
+; Samples
+Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Components: samples
+
+; Include files
+Source: "..\..\include\*"; DestDir: "{app}\include"; Flags: recursesubdirs
+
+; CMake files
+Source: "..\..\cmake-modules\*"; DestDir: "{app}\cmake-modules"; Flags: recursesubdirs
+
+[Icons]
+; Name: "{group}\Assimp Manual"; Filename: "{app}\doc\AssimpDoc.chm" ; Components: help
+; Name: "{group}\Assimp Command Line Manual"; Filename: "{app}\doc\AssimpCmdDoc.chm"; Components: help
+; Name: "{group}\AssimpView"; Filename: "{app}\bin\x64\assimp_view.exe"; Components: tools; Check: IsWin64
+; Name: "{group}\AssimpView"; Filename: "{app}\bin\x86\assimp_view.exe"; Components: tools; Check: not IsWin64

+ 1 - 1
port/AndroidJNI/README.md

@@ -1,6 +1,6 @@
 Build Asset Importer Lib for Android
 Build Asset Importer Lib for Android
 ====================================
 ====================================
-This module provides a fascade for the io-stream-access to files behind the android-asset-management within 
+This module provides a facade for the io-stream-access to files behind the android-asset-management within 
 an Android-native application.
 an Android-native application.
 - It is built as a static library
 - It is built as a static library
 - It requires Android NDK with android API > 9 support.
 - It requires Android NDK with android API > 9 support.

+ 1 - 1
port/PyAssimp/pyassimp/core.py

@@ -93,7 +93,7 @@ def _is_init_type(obj):
         return False
         return False
     tname = obj.__class__.__name__
     tname = obj.__class__.__name__
     return not (tname[:2] == 'c_' or tname == 'Structure' \
     return not (tname[:2] == 'c_' or tname == 'Structure' \
-            or tname == 'POINTER') and not isinstance(obj,int)
+            or tname == 'POINTER') and not isinstance(obj, (int, str, bytes))
 
 
 def _init(self, target = None, parent = None):
 def _init(self, target = None, parent = None):
     """
     """

+ 11 - 0
revision.h.in

@@ -4,4 +4,15 @@
 #define GitVersion 0x@GIT_COMMIT_HASH@
 #define GitVersion 0x@GIT_COMMIT_HASH@
 #define GitBranch "@GIT_BRANCH@"
 #define GitBranch "@GIT_BRANCH@"
 
 
+#define VER_MAJOR @ASSIMP_VERSION_MAJOR@
+#define VER_MINOR @ASSIMP_VERSION_MINOR@
+#define VER_PATCH @ASSIMP_VERSION_PATCH@
+#define VER_BUILD @ASSIMP_PACKAGE_VERSION@
+
+#define STR_HELP(x) #x
+#define STR(x) STR_HELP(x)
+
+#define VER_FILEVERSION             VER_MAJOR,VER_MINOR,VER_PATCH,VER_BUILD
+#define VER_FILEVERSION_STR         STR(VER_MAJOR) "." STR(VER_MINOR) "." STR(VER_PATCH) "." STR(VER_BUILD)
+
 #endif // ASSIMP_REVISION_H_INC
 #endif // ASSIMP_REVISION_H_INC

+ 1 - 1
test/models/PLY/cube_test.ply

@@ -1,6 +1,6 @@
 ply
 ply
 format ascii 1.0
 format ascii 1.0
-comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.412856994)
+comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.3297435427)
 element vertex 8
 element vertex 8
 property float x
 property float x
 property float y
 property float y

+ 33 - 0
test/unit/utObjImportExport.cpp

@@ -391,6 +391,39 @@ TEST_F(utObjImportExport, invalid_normals_uvs) {
     EXPECT_NE(nullptr, scene);
     EXPECT_NE(nullptr, scene);
 }
 }
 
 
+TEST_F(utObjImportExport, no_vt_just_vns) {
+    static const char *ObjModel =
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 0 0 0\n"
+		"v 10 0 0\n"
+		"v 0 10 0\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"vn 0 0 1\n"
+		"f 10/10 11/11 12/12\n";
+
+    Assimp::Importer myImporter;
+    const aiScene *scene = myImporter.ReadFileFromMemory(ObjModel, strlen(ObjModel), 0);
+    EXPECT_NE(nullptr, scene);
+}
+
 TEST_F( utObjImportExport, mtllib_after_g ) {
 TEST_F( utObjImportExport, mtllib_after_g ) {
     ::Assimp::Importer importer;
     ::Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_mtllib_after_g.obj", aiProcess_ValidateDataStructure );
     const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_mtllib_after_g.obj", aiProcess_ValidateDataStructure );

+ 2 - 1
tools/assimp_qt_viewer/CMakeLists.txt

@@ -1,7 +1,8 @@
 set(PROJECT_VERSION "")
 set(PROJECT_VERSION "")
 project(assimp_qt_viewer)
 project(assimp_qt_viewer)
 
 
-cmake_minimum_required(VERSION 3.0)
+# Qt5 requires cmake 3.1 or newer
+cmake_minimum_required(VERSION 3.1)
 
 
 FIND_PACKAGE(OpenGL QUIET)
 FIND_PACKAGE(OpenGL QUIET)
 
 

+ 2 - 0
tools/assimp_view/CMakeLists.txt

@@ -88,6 +88,8 @@ SET_PROPERTY(TARGET assimp_viewer PROPERTY DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
 IF ( MSVC )
 IF ( MSVC )
   ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
+  # assimp_viewer is ANSI (MBCS) throughout
+  REMOVE_DEFINITIONS( -DUNICODE -D_UNICODE )
 ENDIF ( MSVC )
 ENDIF ( MSVC )