Bläddra i källkod

Merge branch 'master' of https://github.com/assimp/assimp

Marco Di Benedetto 7 år sedan
förälder
incheckning
d33cc8ac5a
100 ändrade filer med 2127 tillägg och 931 borttagningar
  1. 3 0
      .gitignore
  2. 29 6
      .travis.sh
  3. 17 11
      .travis.yml
  4. 27 5
      CMakeLists.txt
  5. 8 0
      CREDITS
  6. 2 2
      Readme.md
  7. 43 22
      appveyor.yml
  8. 1 1
      assimp.pc.in
  9. 2 2
      cmake-modules/CoverallsGenerateGcov.cmake
  10. 7 2
      code/3DSExporter.cpp
  11. 7 10
      code/3DSExporter.h
  12. 9 7
      code/3DSHelper.h
  13. 89 0
      code/3MFXmlTags.h
  14. 2 2
      code/AMFImporter.hpp
  15. 1 1
      code/AMFImporter_Node.hpp
  16. 6 3
      code/AMFImporter_Postprocess.cpp
  17. 1 0
      code/ASELoader.cpp
  18. 2 1
      code/AssbinExporter.cpp
  19. 11 0
      code/AssbinLoader.cpp
  20. 1 1
      code/AssxmlExporter.cpp
  21. 41 21
      code/B3DImporter.cpp
  22. 5 4
      code/B3DImporter.h
  23. 3 4
      code/BaseImporter.cpp
  24. 0 36
      code/BaseImporter.h
  25. 2 2
      code/Bitmap.cpp
  26. 6 0
      code/BlenderDNA.h
  27. 5 2
      code/BlenderDNA.inl
  28. 1 1
      code/BlenderIntermediate.h
  29. 7 7
      code/BlenderLoader.cpp
  30. 3 1
      code/BlenderModifier.cpp
  31. 31 12
      code/BlenderScene.cpp
  32. 8 0
      code/BlenderScene.h
  33. 4 4
      code/C4DImporter.cpp
  34. 42 5
      code/CMakeLists.txt
  35. 260 8
      code/ColladaExporter.cpp
  36. 8 1
      code/ColladaExporter.h
  37. 1 1
      code/ColladaHelper.h
  38. 6 1
      code/ColladaLoader.cpp
  39. 2 3
      code/ColladaParser.cpp
  40. 1 1
      code/ColladaParser.h
  41. 328 0
      code/D3MFExporter.cpp
  42. 103 0
      code/D3MFExporter.h
  43. 29 66
      code/D3MFImporter.cpp
  44. 5 7
      code/D3MFImporter.h
  45. 78 141
      code/D3MFOpcPackage.cpp
  46. 12 7
      code/D3MFOpcPackage.h
  47. 1 1
      code/DeboneProcess.h
  48. 2 1
      code/DefaultIOStream.cpp
  49. 75 31
      code/DefaultIOSystem.cpp
  50. 147 0
      code/EmbedTexturesProcess.cpp
  51. 84 0
      code/EmbedTexturesProcess.h
  52. 24 13
      code/Exporter.cpp
  53. 23 22
      code/FBXBinaryTokenizer.cpp
  54. 38 38
      code/FBXConverter.cpp
  55. 2 2
      code/FBXDocument.cpp
  56. 4 4
      code/FBXImportSettings.h
  57. 1 1
      code/FBXImporter.cpp
  58. 28 28
      code/FBXMaterial.cpp
  59. 8 2
      code/FBXMeshGeometry.cpp
  60. 9 0
      code/FBXParser.cpp
  61. 2 0
      code/FBXParser.h
  62. 26 17
      code/FIReader.cpp
  63. 19 4
      code/FIReader.hpp
  64. 76 36
      code/FindDegenerates.cpp
  65. 40 19
      code/FindDegenerates.h
  66. 15 10
      code/FindInvalidDataProcess.cpp
  67. 1 4
      code/FixNormalsStep.h
  68. 1 1
      code/IFCBoolean.cpp
  69. 51 100
      code/IFCCurve.cpp
  70. 5 5
      code/IFCGeometry.cpp
  71. 1 40
      code/IFCOpenings.cpp
  72. 4 3
      code/IFCReaderGen1.cpp
  73. 1 1
      code/IRRLoader.cpp
  74. 2 4
      code/Importer.cpp
  75. 1 0
      code/ImproveCacheLocality.cpp
  76. 1 3
      code/LWOAnimation.cpp
  77. 1 1
      code/LWOMaterial.cpp
  78. 1 1
      code/LWSLoader.cpp
  79. 8 12
      code/LineSplitter.h
  80. 4 4
      code/LogAux.h
  81. 3 5
      code/MD2Loader.cpp
  82. 3 3
      code/MD3Loader.cpp
  83. 1 1
      code/MD5Loader.h
  84. 6 11
      code/MDCLoader.cpp
  85. 7 0
      code/MDLLoader.cpp
  86. 13 13
      code/MDLMaterialLoader.cpp
  87. 8 8
      code/MMDImporter.cpp
  88. 1 1
      code/MMDPmxParser.cpp
  89. 1 0
      code/MMDPmxParser.h
  90. 1 1
      code/MMDVmdParser.h
  91. 11 8
      code/NFFLoader.cpp
  92. 45 15
      code/ObjExporter.cpp
  93. 2 2
      code/ObjExporter.h
  94. 13 10
      code/ObjFileImporter.cpp
  95. 12 16
      code/ObjFileParser.cpp
  96. 2 1
      code/ObjFileParser.h
  97. 1 1
      code/OgreMaterial.cpp
  98. 1 1
      code/OpenGEXExporter.cpp
  99. 25 11
      code/OpenGEXImporter.cpp
  100. 6 4
      code/OptimizeGraph.cpp

+ 3 - 0
.gitignore

@@ -60,6 +60,7 @@ test/gtest/src/gtest-stamp/Debug/gtest-build
 *.lib
 test/gtest/src/gtest-stamp/Debug/
 tools/assimp_view/assimp_viewer.vcxproj.user
+*.pyc
 
 # Unix editor backups
 *~
@@ -81,3 +82,5 @@ lib64/assimp-vc120-mtd.ilk
 lib64/assimp-vc120-mtd.exp
 lib64/assimp-vc120-mt.exp
 xcuserdata
+
+cmake-build-debug

+ 29 - 6
.travis.sh

@@ -1,5 +1,11 @@
-function generate()
-{
+#---------------------------------------------------------------------------
+#Open Asset Import Library (assimp)
+#---------------------------------------------------------------------------
+# Copyright (c) 2006-2017, assimp team
+#
+# License see LICENSE file
+#
+function generate() {
     OPTIONS="-DASSIMP_WERROR=ON"
 
     if [ "$DISABLE_EXPORTERS" = "YES" ] ; then
@@ -26,18 +32,35 @@ function generate()
         OPTIONS="$OPTIONS -DASSIMP_ASAN=OFF"
     fi
 
+    if [ "$UBSAN" = "ON" ] ; then
+        OPTIONS="$OPTIONS -DASSIMP_UBSAN=ON"
+    fi
+
     cmake -G "Unix Makefiles" $OPTIONS
 }
-
+# build and run unittests, if not android
 if [ $ANDROID ]; then
     ant -v -Dmy.dir=${TRAVIS_BUILD_DIR} -f ${TRAVIS_BUILD_DIR}/port/jassimp/build.xml ndk-jni
 fi
 if [ "$TRAVIS_OS_NAME" = "linux" ]; then
+  if [ $ANALYZE = "ON" ] ; then
+    if [ "$CC" = "clang" ]; then
+        scan-build cmake -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF -DASSIMP_BUILD_TESTS=OFF
+        scan-build --status-bugs make -j2
+    else
+        cppcheck --version
+        generate \
+        && cppcheck --error-exitcode=1 -j2 -Iinclude -Icode code 2> cppcheck.txt
+        if [ -s cppcheck.txt ]; then
+            cat cppcheck.txt
+            exit 1
+        fi
+    fi
+  else
     generate \
     && make -j4 \
     && sudo make install \
     && sudo ldconfig \
-    && (cd test/unit; ../../bin/unit) \
-    #&& (cd test/regression; chmod 755 run.py; ./run.py ../../bin/assimp; \
-	#   chmod 755 result_checker.py; ./result_checker.py)
+    && (cd test/unit; ../../bin/unit)
+  fi
 fi

+ 17 - 11
.travis.yml

@@ -4,7 +4,7 @@ language: cpp
 cache: ccache
 
 before_install:
-  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq && sudo apt-get install cmake && sudo apt-get install cmake python3 && sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- ; fi
+  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq && sudo apt-get install cmake cppcheck && sudo apt-get install cmake python3 && sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- ; fi
   - 'if [ "$TRAVIS_OS_NAME" = "osx" ];  then
        if brew ls --versions cmake > /dev/null; then
          echo cmake already installed.;
@@ -18,10 +18,6 @@ before_install:
   # install latest LCOV (1.9 was failing)
   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz && tar xf lcov_1.11.orig.tar.gz && sudo make -C lcov-1.11/ install && gem install coveralls-lcov && lcov --version && g++ --version ; fi
 
-branches:
-  only:
-    - master
-
 os:
   - linux
 
@@ -37,17 +33,27 @@ env:
 
 matrix:
   include:
-    - os: linux
-      compiler: gcc
-      env: DISABLE_EXPORTERS=YES ENABLE_COVERALLS=ON
-    - os: linux
-      compiler: gcc
-      env: SHARED_BUILD=ON
+    # disabled until clang 5.0 analyzer issues are fixed
+    # - os: linux
+    #   compiler: clang
+    #   env: ANALYZE=ON
     - os: linux
       compiler: clang
       env: ASAN=ON
     - os: linux
       compiler: clang
+      env: UBSAN=ON
+    - os: linux
+      compiler: clang
+      env: SHARED_BUILD=ON
+    - os: linux
+      compiler: gcc
+      env: ANALYZE=ON
+    - os: linux
+      compiler: gcc
+      env: DISABLE_EXPORTERS=YES ENABLE_COVERALLS=ON
+    - os: linux
+      compiler: gcc
       env: SHARED_BUILD=ON
 
 install:

+ 27 - 5
CMakeLists.txt

@@ -42,6 +42,10 @@ OPTION( BUILD_SHARED_LIBS
   "Build package with shared libraries."
   ON
 )
+OPTION( BUILD_FRAMEWORK
+  "Build package as Mac OS X Framework bundle."
+  OFF
+)
 OPTION( ASSIMP_DOUBLE_PRECISION
   "Set to ON to enable double precision processing"
   OFF
@@ -86,6 +90,10 @@ OPTION ( ASSIMP_ASAN
   "Enable AddressSanitizer."
   OFF
 )
+OPTION ( ASSIMP_UBSAN
+  "Enable Undefined Behavior sanitizer."
+  OFF
+)
 OPTION ( SYSTEM_IRRXML
   "Use system installed Irrlicht/IrrXML library."
   OFF
@@ -95,6 +103,7 @@ OPTION ( BUILD_DOCS
   OFF
 )
 
+# Use subset of Windows.h
 if (WIN32)
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 endif()
@@ -107,6 +116,11 @@ IF(MSVC)
   )
 ENDIF(MSVC)
 
+IF (BUILD_FRAMEWORK)
+  SET (BUILD_SHARED_LIBS ON)
+  MESSAGE(STATUS "Framework bundle building enabled")
+ENDIF(BUILD_FRAMEWORK)
+
 IF(NOT BUILD_SHARED_LIBS)
   MESSAGE(STATUS "Shared libraries disabled")
   SET(LINK_SEARCH_START_STATIC TRUE)
@@ -116,8 +130,8 @@ ENDIF(NOT BUILD_SHARED_LIBS)
 
 # Define here the needed parameters
 SET (ASSIMP_VERSION_MAJOR 4)
-SET (ASSIMP_VERSION_MINOR 0)
-SET (ASSIMP_VERSION_PATCH 1)
+SET (ASSIMP_VERSION_MINOR 1)
+SET (ASSIMP_VERSION_PATCH 0)
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 SET (ASSIMP_SOVERSION 4)
 SET (PROJECT_VERSION "${ASSIMP_VERSION}")
@@ -179,15 +193,17 @@ SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names")
 
 IF( UNIX )
   # Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux
-  IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux
-    ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 )
+  IF( ${OPERATING_SYSTEM} MATCHES "Android")
+  ELSE()
+    IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux
+      #ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 )
+    ENDIF()
   ENDIF()
 
   # Use GNUInstallDirs for Unix predefined directories
   INCLUDE(GNUInstallDirs)
 ENDIF( UNIX )
 
-
 # Grouped compiler settings
 IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
   # hide all not-exported symbols
@@ -239,6 +255,12 @@ if (ASSIMP_ASAN)
     SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
 endif()
 
+if (ASSIMP_UBSAN)
+    MESSAGE(STATUS "Undefined Behavior sanitizer enabled")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
+endif()
+
 INCLUDE (FindPkgMacros)
 INCLUDE (PrecompiledHeader)
 

+ 8 - 0
CREDITS

@@ -158,3 +158,11 @@ Contributed X File exporter
 Contributed Step (stp) exporter
 
 For a more detailed list just check: https://github.com/assimp/assimp/network/members
+
+Patreons:
+- migenius
+- Marcus
+- Cort
+- elect
+- Steffen
+

+ 2 - 2
Readme.md

@@ -119,8 +119,8 @@ Take a look into the `INSTALL` file. Our build system is CMake, if you used CMak
 * [.NET](port/AssimpNET/Readme.md)
 * [Pascal](port/AssimpPascal/Readme.md)
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
-* [Unity 3d Plugin] (https://www.assetstore.unity3d.com/en/#!/content/91777)
-* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, ~collada)
+* [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777)
+* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, collada, md2)
 
 ### Other tools ###
 [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.

+ 43 - 22
appveyor.yml

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

+ 1 - 1
assimp.pc.in

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

+ 2 - 2
cmake-modules/CoverallsGenerateGcov.cmake

@@ -310,7 +310,7 @@ foreach (GCOV_FILE ${GCOV_FILES})
 	message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}")
 
 	# Loads the gcov file as a list of lines.
-	# (We first open the file and replace all occurences of [] with _
+	# (We first open the file and replace all occurrences of [] with _
 	#  because CMake will fail to parse a line containing unmatched brackets...
 	#  also the \ to escaped \n in macros screws up things.)
 	# https://public.kitware.com/Bug/view.php?id=15369
@@ -329,7 +329,7 @@ foreach (GCOV_FILE ${GCOV_FILES})
 	# Instead of trying to parse the source from the
 	# gcov file, simply read the file contents from the source file.
 	# (Parsing it from the gcov is hard because C-code uses ; in many places
-	#  which also happens to be the same as the CMake list delimeter).
+	#  which also happens to be the same as the CMake list delimiter).
 	file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
 
 	string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")

+ 7 - 2
code/3DSExporter.cpp

@@ -39,7 +39,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
 
@@ -151,7 +150,7 @@ namespace {
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
-void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
+void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
 {
     std::shared_ptr<IOStream> outfile (pIOSystem->Open(pFile, "wb"));
     if(!outfile) {
@@ -210,6 +209,12 @@ Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr<IOStream> outfile, con
     }
 }
 
+// ------------------------------------------------------------------------------------------------
+Discreet3DSExporter::~Discreet3DSExporter() {
+    // empty
+}
+
+
 // ------------------------------------------------------------------------------------------------
 int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling_level)
 {

+ 7 - 10
code/3DSExporter.h

@@ -60,23 +60,21 @@ namespace Assimp
 {
 
 // ------------------------------------------------------------------------------------------------
-/** Helper class to export a given scene to a 3DS file. */
+/**
+ *  @brief  Helper class to export a given scene to a 3DS file.
+ */
 // ------------------------------------------------------------------------------------------------
-class Discreet3DSExporter
-{
+class Discreet3DSExporter {
 public:
     Discreet3DSExporter(std::shared_ptr<IOStream> outfile, const aiScene* pScene);
+    ~Discreet3DSExporter();
 
 private:
-
     void WriteMeshes();
     void WriteMaterials();
     void WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags);
-
     void WriteFaceMaterialChunk(const aiMesh& mesh);
-
     int WriteHierarchy(const aiNode& node, int level, int sibling_level);
-
     void WriteString(const std::string& s);
     void WriteString(const aiString& s);
     void WriteColor(const aiColor3D& color);
@@ -84,7 +82,6 @@ private:
     void WritePercentChunk(double f);
 
 private:
-
     const aiScene* const scene;
     StreamWriterLE writer;
 
@@ -95,6 +92,6 @@ private:
 
 };
 
-}
+} // Namespace Assimp
 
-#endif
+#endif // AI_3DSEXPORTER_H_INC

+ 9 - 7
code/3DSHelper.h

@@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_3DSFILEHELPER_H_INC
 #define AI_3DSFILEHELPER_H_INC
 
-
 #include "SpatialSort.h"
 #include "SmoothingGroups.h"
 #include "StringUtils.h"
@@ -64,16 +63,19 @@ namespace D3DS  {
 /** Discreet3DS class: Helper class for loading 3ds files. Defines chunks
 *  and data structures.
 */
-class Discreet3DS
-{
+class Discreet3DS {
 private:
-    inline Discreet3DS() {}
+    Discreet3DS() {
+        // empty
+    }
 
-public:
+    ~Discreet3DS() {
+        // empty
+    }
 
+public:
     //! data structure for a single chunk in a .3ds file
-    struct Chunk
-    {
+    struct Chunk {
         uint16_t    Flag;
         uint32_t    Size;
     } PACK_STRUCT;

+ 89 - 0
code/3MFXmlTags.h

@@ -0,0 +1,89 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+namespace Assimp {
+namespace D3MF {
+
+namespace XmlTag {
+    static const std::string model = "model";
+    static const std::string model_unit = "unit";
+    static const std::string metadata = "metadata";
+    static const std::string resources = "resources";
+    static const std::string object = "object";
+    static const std::string mesh = "mesh";
+    static const std::string vertices = "vertices";
+    static const std::string vertex = "vertex";
+    static const std::string triangles = "triangles";
+    static const std::string triangle = "triangle";
+    static const std::string x = "x";
+    static const std::string y = "y";
+    static const std::string z = "z";
+    static const std::string v1 = "v1";
+    static const std::string v2 = "v2";
+    static const std::string v3 = "v3";
+    static const std::string id = "id";
+    static const std::string name = "name";
+    static const std::string type = "type";
+    static const std::string build = "build";
+    static const std::string item = "item";
+    static const std::string objectid = "objectid";
+    static const std::string transform = "transform";
+
+    static const std::string CONTENT_TYPES_ARCHIVE = "[Content_Types].xml";
+    static const std::string ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels";
+    static const std::string SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types";
+    static const std::string SCHEMA_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships";
+    static const std::string RELS_RELATIONSHIP_CONTAINER = "Relationships";
+    static const std::string RELS_RELATIONSHIP_NODE = "Relationship";
+    static const std::string RELS_ATTRIB_TARGET = "Target";
+    static const std::string RELS_ATTRIB_TYPE = "Type";
+    static const std::string RELS_ATTRIB_ID = "Id";
+    static const std::string PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel";
+    static const std::string PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket";
+    static const std::string PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture";
+    static const std::string PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
+    static const std::string PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail";
+
+}
+
+} // Namespace D3MF
+} // Namespace Assimp

+ 2 - 2
code/AMFImporter.hpp

@@ -249,7 +249,7 @@ private:
 
 	/// \fn size_t PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A)
 	/// Return converted texture ID which related to specified source textures ID's. If converted texture does not exist then it will be created and ID on new
-	/// converted texture will be returned. Convertion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it
+	/// converted texture will be returned. Conversion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it
 	/// to converted textures list.
 	/// Any of source ID's can be absent(empty string) or even one ID only specified. But at least one ID must be specified.
 	/// \param [in] pID_R - ID of source "red" texture.
@@ -378,7 +378,7 @@ private:
 	void XML_CheckNode_MustHaveChildren();
 
 	/// \fn bool XML_CheckNode_NameEqual(const std::string& pNodeName)
-	/// Chek if current node name is equal to pNodeName.
+	/// Check if current node name is equal to pNodeName.
 	/// \param [in] pNodeName - name for checking.
 	/// return true if current node name is equal to pNodeName, else - false.
 	bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; }

+ 1 - 1
code/AMFImporter_Node.hpp

@@ -137,7 +137,7 @@ struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement
 {
 	/****************** Variables ******************/
 
-	std::string ObjectID;///< ID of object for instanciation.
+	std::string ObjectID;///< ID of object for instantiation.
 	/// \var Delta - The distance of translation in the x, y, or z direction, respectively, in the referenced object's coordinate system, to
 	/// create an instance of the object in the current constellation.
 	aiVector3D Delta;

+ 6 - 3
code/AMFImporter_Postprocess.cpp

@@ -60,7 +60,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp
 {
 
-aiColor4D AMFImporter::SPP_Material::GetColor(const float pX, const float pY, const float pZ) const
+aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const
 {
     aiColor4D tcol;
 
@@ -281,8 +281,11 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string&
 	{
 		if(!pID.empty())
 		{
-			for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++)
-				converted_texture.Data[idx_target] = src_texture[pSrcTexNum]->Data.at(idx_src);
+			for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) {
+				CAMFImporter_NodeElement_Texture* tex = src_texture[pSrcTexNum];
+				ai_assert(tex);
+				converted_texture.Data[idx_target] = tex->Data.at(idx_src);
+			}
 		}
 	};// auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
 

+ 1 - 0
code/ASELoader.cpp

@@ -1021,6 +1021,7 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMesh
 
                             // convert bones, if existing
                             if (!mesh.mBones.empty()) {
+                                ai_assert(avOutputBones);
                                 // check whether there is a vertex weight for this vertex index
                                 if (iIndex2 < mesh.mBoneVertices.size())    {
 

+ 2 - 1
code/AssbinExporter.cpp

@@ -171,6 +171,7 @@ inline size_t Write<aiQuaternion>(IOStream * stream, const aiQuaternion& v)
     t += Write<float>(stream,v.x);
     t += Write<float>(stream,v.y);
     t += Write<float>(stream,v.z);
+    ai_assert(t == 16);
     return 16;
 }
 
@@ -810,7 +811,7 @@ inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size)
         }
     };
 
-void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
+void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
 {
     AssbinExport exporter;
     exporter.WriteBinaryDump( pFile, pIOSystem, pScene );

+ 11 - 0
code/AssbinLoader.cpp

@@ -200,6 +200,7 @@ template <typename T> void ReadBounds( IOStream * stream, T* /*p*/, unsigned int
 
 void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* parent ) {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AINODE);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -274,6 +275,7 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node, aiNode* p
 void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b )
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AIBONE);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -298,6 +300,7 @@ void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b )
 void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AIMESH);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -423,6 +426,7 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
 void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop)
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -440,6 +444,7 @@ void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialPro
 void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat)
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIAL);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -462,6 +467,7 @@ void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat)
 void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd)
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AINODEANIM);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -511,6 +517,7 @@ void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd)
 void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim )
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AIANIMATION);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -532,6 +539,7 @@ void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim )
 void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex)
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AITEXTURE);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -556,6 +564,7 @@ void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex)
 void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l )
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AILIGHT);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -583,6 +592,7 @@ void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l )
 void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam )
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AICAMERA);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 
@@ -599,6 +609,7 @@ void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam )
 void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
 {
     uint32_t chunkID = Read<uint32_t>(stream);
+    (void)(chunkID);
     ai_assert(chunkID == ASSBIN_CHUNK_AISCENE);
     /*uint32_t size =*/ Read<uint32_t>(stream);
 

+ 1 - 1
code/AssxmlExporter.cpp

@@ -631,7 +631,7 @@ void WriteDump(const aiScene* scene, IOStream* io, bool shortened) {
 
 } // end of namespace AssxmlExport
 
-void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
+void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
 {
     IOStream * out = pIOSystem->Open( pFile, "wt" );
     if (!out) return;

+ 41 - 21
code/B3DImporter.cpp

@@ -93,7 +93,6 @@ void DeleteAllBarePointers(std::vector<T>& x)
 
 B3DImporter::~B3DImporter()
 {
-    DeleteAllBarePointers(_animations);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -171,7 +170,8 @@ int B3DImporter::ReadByte(){
 // ------------------------------------------------------------------------------------------------
 int B3DImporter::ReadInt(){
     if( _pos+4<=_buf.size() ){
-        int n=*(int*)&_buf[_pos];
+        int n;
+        memcpy(&n, &_buf[_pos], 4);
         _pos+=4;
         return n;
     }
@@ -182,7 +182,8 @@ int B3DImporter::ReadInt(){
 // ------------------------------------------------------------------------------------------------
 float B3DImporter::ReadFloat(){
     if( _pos+4<=_buf.size() ){
-        float n=*(float*)&_buf[_pos];
+        float n;
+        memcpy(&n, &_buf[_pos], 4);
         _pos+=4;
         return n;
     }
@@ -265,6 +266,21 @@ T *B3DImporter::to_array( const vector<T> &v ){
     return p;
 }
 
+
+// ------------------------------------------------------------------------------------------------
+template<class T>
+T **unique_to_array( vector<std::unique_ptr<T> > &v ){
+    if( v.empty() ) {
+        return 0;
+    }
+    T **p = new T*[ v.size() ];
+    for( size_t i = 0; i < v.size(); ++i ){
+        p[i] = v[i].release();
+    }
+    return p;
+}
+
+
 // ------------------------------------------------------------------------------------------------
 void B3DImporter::ReadTEXS(){
     while( ChunkSize() ){
@@ -293,8 +309,7 @@ void B3DImporter::ReadBRUS(){
         /*int blend=**/ReadInt();
         int fx=ReadInt();
 
-        aiMaterial *mat=new aiMaterial;
-        _materials.push_back( mat );
+        std::unique_ptr<aiMaterial> mat(new aiMaterial);
 
         // Name
         aiString ainame( name );
@@ -331,6 +346,7 @@ void B3DImporter::ReadBRUS(){
                 mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) );
             }
         }
+        _materials.emplace_back( std::move(mat) );
     }
 }
 
@@ -384,8 +400,7 @@ void B3DImporter::ReadTRIS( int v0 ){
         Fail( "Bad material id" );
     }
 
-    aiMesh *mesh=new aiMesh;
-    _meshes.push_back( mesh );
+    std::unique_ptr<aiMesh> mesh(new aiMesh);
 
     mesh->mMaterialIndex=matid;
     mesh->mNumFaces=0;
@@ -413,6 +428,8 @@ void B3DImporter::ReadTRIS( int v0 ){
         ++mesh->mNumFaces;
         ++face;
     }
+
+    _meshes.emplace_back( std::move(mesh) );
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -498,11 +515,11 @@ void B3DImporter::ReadANIM(){
     int frames=ReadInt();
     float fps=ReadFloat();
 
-    aiAnimation *anim=new aiAnimation;
-    _animations.push_back( anim );
+    std::unique_ptr<aiAnimation> anim(new aiAnimation);
 
     anim->mDuration=frames;
     anim->mTicksPerSecond=fps;
+    _animations.emplace_back( std::move(anim) );
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -529,7 +546,7 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){
     node->mParent=parent;
     node->mTransformation=tform;
 
-    aiNodeAnim *nodeAnim=0;
+    std::unique_ptr<aiNodeAnim> nodeAnim;
     vector<unsigned> meshes;
     vector<aiNode*> children;
 
@@ -547,11 +564,10 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){
             ReadANIM();
         }else if( t=="KEYS" ){
             if( !nodeAnim ){
-                nodeAnim=new aiNodeAnim;
-                _nodeAnims.push_back( nodeAnim );
+                nodeAnim.reset(new aiNodeAnim);
                 nodeAnim->mNodeName=node->mName;
             }
-            ReadKEYS( nodeAnim );
+            ReadKEYS( nodeAnim.get() );
         }else if( t=="NODE" ){
             aiNode *child=ReadNODE( node );
             children.push_back( child );
@@ -559,6 +575,10 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){
         ExitChunk();
     }
 
+    if (nodeAnim) {
+        _nodeAnims.emplace_back( std::move(nodeAnim) );
+    }
+
     node->mNumMeshes= static_cast<unsigned int>(meshes.size());
     node->mMeshes=to_array( meshes );
 
@@ -584,7 +604,6 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
 
     _nodeAnims.clear();
 
-    DeleteAllBarePointers(_animations);
     _animations.clear();
 
     string t=ReadChunk();
@@ -620,7 +639,7 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
         aiNode *node=_nodes[i];
 
         for( size_t j=0;j<node->mNumMeshes;++j ){
-            aiMesh *mesh=_meshes[node->mMeshes[j]];
+            aiMesh *mesh = _meshes[node->mMeshes[j]].get();
 
             int n_tris=mesh->mNumFaces;
             int n_verts=mesh->mNumVertices=n_tris * 3;
@@ -683,27 +702,28 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
 
     //nodes
     scene->mRootNode=_nodes[0];
+    _nodes.clear();  // node ownership now belongs to scene
 
     //material
     if( !_materials.size() ){
-        _materials.push_back( new aiMaterial );
+        _materials.emplace_back( std::unique_ptr<aiMaterial>(new aiMaterial) );
     }
     scene->mNumMaterials= static_cast<unsigned int>(_materials.size());
-    scene->mMaterials=to_array( _materials );
+    scene->mMaterials = unique_to_array( _materials );
 
     //meshes
     scene->mNumMeshes= static_cast<unsigned int>(_meshes.size());
-    scene->mMeshes=to_array( _meshes );
+    scene->mMeshes = unique_to_array( _meshes );
 
     //animations
     if( _animations.size()==1 && _nodeAnims.size() ){
 
-        aiAnimation *anim=_animations.back();
+        aiAnimation *anim = _animations.back().get();
         anim->mNumChannels=static_cast<unsigned int>(_nodeAnims.size());
-        anim->mChannels=to_array( _nodeAnims );
+        anim->mChannels = unique_to_array( _nodeAnims );
 
         scene->mNumAnimations=static_cast<unsigned int>(_animations.size());
-        scene->mAnimations=to_array( _animations );
+        scene->mAnimations=unique_to_array( _animations );
     }
 
     // convert to RH

+ 5 - 4
code/B3DImporter.h

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/material.h>
 #include "BaseImporter.h"
 
+#include <memory>
 #include <vector>
 
 struct aiNodeAnim;
@@ -116,15 +117,15 @@ private:
     std::vector<unsigned> _stack;
 
     std::vector<std::string> _textures;
-    std::vector<aiMaterial*> _materials;
+    std::vector<std::unique_ptr<aiMaterial> > _materials;
 
     int _vflags,_tcsets,_tcsize;
     std::vector<Vertex> _vertices;
 
     std::vector<aiNode*> _nodes;
-    std::vector<aiMesh*> _meshes;
-    std::vector<aiNodeAnim*> _nodeAnims;
-    std::vector<aiAnimation*> _animations;
+    std::vector<std::unique_ptr<aiMesh> > _meshes;
+    std::vector<std::unique_ptr<aiNodeAnim> > _nodeAnims;
+    std::vector<std::unique_ptr<aiAnimation> > _animations;
 };
 
 }

+ 3 - 4
code/BaseImporter.cpp

@@ -89,12 +89,12 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
     FileSystemFilter filter(pFile,pIOHandler);
 
     // create a scene object to hold the data
-    ScopeGuard<aiScene> sc(new aiScene());
+    std::unique_ptr<aiScene> sc(new aiScene());
 
     // dispatch importing
     try
     {
-        InternReadFile( pFile, sc, &filter);
+        InternReadFile( pFile, sc.get(), &filter);
 
     } catch( const std::exception& err )    {
         // extract error description
@@ -104,8 +104,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
     }
 
     // return what we gathered from the import.
-    sc.dismiss();
-    return sc;
+    return sc.release();
 }
 
 // ------------------------------------------------------------------------------------------------

+ 0 - 36
code/BaseImporter.h

@@ -65,42 +65,6 @@ class IOStream;
 #define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \
     (string[1] << 16) + (string[2] << 8) + string[3]))
 
-// ---------------------------------------------------------------------------
-template <typename T>
-struct ScopeGuard
-{
-    explicit ScopeGuard(T* obj) : obj(obj), mdismiss() {}
-    ~ScopeGuard () throw() {
-        if (!mdismiss) {
-            delete obj;
-        }
-        obj = NULL;
-    }
-
-    T* dismiss() {
-        mdismiss=true;
-        return obj;
-    }
-
-    operator T*() {
-        return obj;
-    }
-
-    T* operator -> () {
-        return obj;
-    }
-
-private:
-    // no copying allowed.
-    ScopeGuard();
-    ScopeGuard( const ScopeGuard & );
-    ScopeGuard &operator = ( const ScopeGuard & );
-
-    T* obj;
-    bool mdismiss;
-};
-
-
 
 // ---------------------------------------------------------------------------
 /** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface

+ 2 - 2
code/Bitmap.cpp

@@ -102,7 +102,7 @@ namespace Assimp {
         offset += Copy(&data[offset], header.size);
         offset += Copy(&data[offset], header.reserved1);
         offset += Copy(&data[offset], header.reserved2);
-        offset += Copy(&data[offset], header.offset);
+                  Copy(&data[offset], header.offset);
 
         file->Write(data, Header::header_size, 1);
     }
@@ -122,7 +122,7 @@ namespace Assimp {
         offset += Copy(&data[offset], dib.x_resolution);
         offset += Copy(&data[offset], dib.y_resolution);
         offset += Copy(&data[offset], dib.nb_colors);
-        offset += Copy(&data[offset], dib.nb_important_colors);
+                  Copy(&data[offset], dib.nb_important_colors);
 
         file->Write(data, DIB::dib_size, 1);
     }

+ 6 - 0
code/BlenderDNA.h

@@ -92,6 +92,12 @@ struct Error : DeadlyImportError {
  *  descendents. It serves as base class for all data structure fields. */
 // -------------------------------------------------------------------------------
 struct ElemBase {
+    ElemBase()
+    : dna_type(nullptr)
+    {
+        // empty
+    }
+
     virtual ~ElemBase() {
         // empty
     }

+ 5 - 2
code/BlenderDNA.inl

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

+ 1 - 1
code/BlenderIntermediate.h

@@ -110,7 +110,7 @@ namespace Blender {
         void operator= (const TempArray&)  {
         }
 
-        TempArray(const TempArray& arr) {
+        TempArray(const TempArray& /*arr*/) {
         }
 
     private:

+ 7 - 7
code/BlenderLoader.cpp

@@ -1148,7 +1148,7 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
 // ------------------------------------------------------------------------------------------------
 aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj, const Camera* cam, ConversionData& /*conv_data*/)
 {
-    ScopeGuard<aiCamera> out(new aiCamera());
+    std::unique_ptr<aiCamera> out(new aiCamera());
     out->mName = obj->id.name+2;
     out->mPosition = aiVector3D(0.f, 0.f, 0.f);
     out->mUp = aiVector3D(0.f, 1.f, 0.f);
@@ -1159,13 +1159,13 @@ aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj,
     out->mClipPlaneNear = cam->clipsta;
     out->mClipPlaneFar = cam->clipend;
 
-    return out.dismiss();
+    return out.release();
 }
 
 // ------------------------------------------------------------------------------------------------
 aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, const Lamp* lamp, ConversionData& /*conv_data*/)
 {
-    ScopeGuard<aiLight> out(new aiLight());
+    std::unique_ptr<aiLight> out(new aiLight());
     out->mName = obj->id.name+2;
 
     switch (lamp->type)
@@ -1203,7 +1203,7 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
     out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
-    return out.dismiss();
+    return out.release();
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1221,7 +1221,7 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers
         ++it;
     }
 
-    ScopeGuard<aiNode> node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB'
+    std::unique_ptr<aiNode> node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB'
     if (obj->data) {
         switch (obj->type)
         {
@@ -1305,14 +1305,14 @@ aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, Convers
         aiNode** nd = node->mChildren = new aiNode*[node->mNumChildren]();
         for (const Object* nobj :children) {
             *nd = ConvertNode(in,nobj,conv_data,node->mTransformation * parentTransform);
-            (*nd++)->mParent = node;
+            (*nd++)->mParent = node.get();
         }
     }
 
     // apply modifiers
     modifier_cache->ApplyModifiers(*node,conv_data,in,*obj);
 
-    return node.dismiss();
+    return node.release();
 }
 
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 3 - 1
code/BlenderModifier.cpp

@@ -310,7 +310,9 @@ void  BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data
 
     std::unique_ptr<Subdivider> subd(Subdivider::Create(algo));
     ai_assert(subd);
-
+    if ( conv_data.meshes->empty() ) {
+        return;
+    }
     aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
     std::unique_ptr<aiMesh*[]> tempmeshes(new aiMesh*[out.mNumMeshes]());
 

+ 31 - 12
code/BlenderScene.cpp

@@ -59,7 +59,9 @@ template <> void Structure :: Convert<Object> (
 {
 
     ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
-    ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp,"type",db);
+    dest.type = static_cast<Assimp::Blender::Object::Type>(temp);
     ReadFieldArray2<ErrorPolicy_Warn>(dest.obmat,"obmat",db);
     ReadFieldArray2<ErrorPolicy_Warn>(dest.parentinv,"parentinv",db);
     ReadFieldArray<ErrorPolicy_Warn>(dest.parsubstr,"parsubstr",db);
@@ -100,14 +102,21 @@ template <> void Structure :: Convert<MTex> (
     ) const
 {
 
-    ReadField<ErrorPolicy_Igno>((short&)dest.mapto,"mapto",db);
-    ReadField<ErrorPolicy_Igno>((int&)dest.blendtype,"blendtype",db);
+    int temp_short = 0;
+    ReadField<ErrorPolicy_Igno>(temp_short,"mapto",db);
+    dest.mapto = static_cast<Assimp::Blender::MTex::MapType>(temp_short);
+    int temp = 0;
+    ReadField<ErrorPolicy_Igno>(temp,"blendtype",db);
+    dest.blendtype = static_cast<Assimp::Blender::MTex::BlendType>(temp);
     ReadFieldPtr<ErrorPolicy_Igno>(dest.object,"*object",db);
     ReadFieldPtr<ErrorPolicy_Igno>(dest.tex,"*tex",db);
     ReadFieldArray<ErrorPolicy_Igno>(dest.uvname,"uvname",db);
-    ReadField<ErrorPolicy_Igno>((int&)dest.projx,"projx",db);
-    ReadField<ErrorPolicy_Igno>((int&)dest.projy,"projy",db);
-    ReadField<ErrorPolicy_Igno>((int&)dest.projz,"projz",db);
+    ReadField<ErrorPolicy_Igno>(temp,"projx",db);
+    dest.projx = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(temp,"projy",db);
+    dest.projy = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(temp,"projz",db);
+    dest.projx = static_cast<Assimp::Blender::MTex::Projection>(temp);
     ReadField<ErrorPolicy_Igno>(dest.mapping,"mapping",db);
     ReadFieldArray<ErrorPolicy_Igno>(dest.ofs,"ofs",db);
     ReadFieldArray<ErrorPolicy_Igno>(dest.size,"size",db);
@@ -190,7 +199,9 @@ template <> void Structure :: Convert<Lamp> (
 {
 
     ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
-    ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp,"type",db);
+    dest.type = static_cast<Assimp::Blender::Lamp::Type>(temp);
     ReadField<ErrorPolicy_Igno>(dest.flags,"flags",db);
     ReadField<ErrorPolicy_Igno>(dest.colormodel,"colormodel",db);
     ReadField<ErrorPolicy_Igno>(dest.totex,"totex",db);
@@ -204,7 +215,8 @@ template <> void Structure :: Convert<Lamp> (
     ReadField<ErrorPolicy_Igno>(dest.spotblend,"spotblend",db);
     ReadField<ErrorPolicy_Igno>(dest.att1,"att1",db);
     ReadField<ErrorPolicy_Igno>(dest.att2,"att2",db);
-    ReadField<ErrorPolicy_Igno>((int&)dest.falloff_type,"falloff_type",db);
+    ReadField<ErrorPolicy_Igno>(temp,"falloff_type",db);
+    dest.falloff_type = static_cast<Assimp::Blender::Lamp::FalloffType>(temp);
     ReadField<ErrorPolicy_Igno>(dest.sun_brightness,"sun_brightness",db);
     ReadField<ErrorPolicy_Igno>(dest.area_size,"area_size",db);
     ReadField<ErrorPolicy_Igno>(dest.area_sizey,"area_sizey",db);
@@ -693,8 +705,12 @@ template <> void Structure :: Convert<Tex> (
     const FileDatabase& db
     ) const
 {
-    ReadField<ErrorPolicy_Igno>((short&)dest.imaflag,"imaflag",db);
-    ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+    short temp_short = 0;
+    ReadField<ErrorPolicy_Igno>(temp_short,"imaflag",db);
+    dest.imaflag = static_cast<Assimp::Blender::Tex::ImageFlags>(temp_short);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp,"type",db);
+    dest.type = static_cast<Assimp::Blender::Tex::Type>(temp);
     ReadFieldPtr<ErrorPolicy_Warn>(dest.ima,"*ima",db);
 
     db.reader->IncPtr(size);
@@ -708,8 +724,11 @@ template <> void Structure :: Convert<Camera> (
 {
 
     ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
-    ReadField<ErrorPolicy_Warn>((int&)dest.type,"type",db);
-    ReadField<ErrorPolicy_Warn>((int&)dest.flag,"flag",db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Warn>(temp,"type",db);
+    dest.type = static_cast<Assimp::Blender::Camera::Type>(temp);
+    ReadField<ErrorPolicy_Warn>(temp,"flag",db);
+    dest.flag = static_cast<Assimp::Blender::Camera::Type>(temp);
     ReadField<ErrorPolicy_Warn>(dest.lens,"lens",db);
     ReadField<ErrorPolicy_Warn>(dest.sensor_x,"sensor_x",db);
     ReadField<ErrorPolicy_Igno>(dest.clipsta,"clipsta",db);

+ 8 - 0
code/BlenderScene.h

@@ -225,6 +225,14 @@ struct TFace : ElemBase {
 
 // -------------------------------------------------------------------------------
 struct MTFace : ElemBase {
+	MTFace()
+	: flag(0)
+	, mode(0)
+	, tile(0)
+	, unwrap(0)
+	{
+	}
+
     float uv[4][2] FAIL;
     char flag;
     short mode;

+ 4 - 4
code/C4DImporter.cpp

@@ -185,11 +185,11 @@ void C4DImporter::InternReadFile( const std::string& pFile,
         if(mesh->mMaterialIndex >= mat_count) {
             ++mat_count;
 
-            ScopeGuard<aiMaterial> def_material(new aiMaterial());
+            std::unique_ptr<aiMaterial> def_material(new aiMaterial());
             const aiString name(AI_DEFAULT_MATERIAL_NAME);
             def_material->AddProperty(&name, AI_MATKEY_NAME);
 
-            materials.push_back(def_material.dismiss());
+            materials.push_back(def_material.release());
             break;
         }
     }
@@ -412,7 +412,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     const CPolygon* polys = polyObject->GetPolygonR();
     ai_assert(polys != NULL);
 
-    ScopeGuard<aiMesh> mesh(new aiMesh());
+    std::unique_ptr<aiMesh> mesh(new aiMesh());
     mesh->mNumFaces = static_cast<unsigned int>(polyCount);
     aiFace* face = mesh->mFaces = new aiFace[mesh->mNumFaces]();
 
@@ -616,7 +616,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     }
 
     mesh->mMaterialIndex = ResolveMaterial(polyObject);
-    return mesh.dismiss();
+    return mesh.release();
 }
 
 

+ 42 - 5
code/CMakeLists.txt

@@ -156,6 +156,8 @@ SET( Common_SRCS
   SkeletonMeshBuilder.h
   SplitByBoneCountProcess.cpp
   SplitByBoneCountProcess.h
+  ScaleProcess.cpp
+  ScaleProcess.h
   SmoothingGroups.h
   StandardShapes.cpp
   StandardShapes.h
@@ -523,6 +525,8 @@ SET( PostProcessing_SRCS
   ComputeUVMappingProcess.h
   ConvertToLHProcess.cpp
   ConvertToLHProcess.h
+  EmbedTexturesProcess.cpp
+  EmbedTexturesProcess.h
   FindDegenerates.cpp
   FindDegenerates.h
   FindInstancesProcess.cpp
@@ -675,8 +679,11 @@ ADD_ASSIMP_IMPORTER( GLTF
 ADD_ASSIMP_IMPORTER( 3MF
     D3MFImporter.h
     D3MFImporter.cpp
+    D3MFExporter.h
+    D3MFExporter.cpp
     D3MFOpcPackage.h
     D3MFOpcPackage.cpp
+    3MFXmlTags.h
 )
 
 ADD_ASSIMP_IMPORTER( MMD
@@ -738,6 +745,14 @@ SET( unzip_SRCS
 )
 SOURCE_GROUP( unzip FILES ${unzip_SRCS})
 
+SET( ziplib_SRCS
+  ../contrib/zip/src/miniz.h
+  ../contrib/zip/src/zip.c
+  ../contrib/zip/src/zip.h
+)
+
+SOURCE_GROUP( ziplib FILES ${ziplib_SRCS} )
+
 SET ( openddl_parser_SRCS
   ../contrib/openddlparser/code/OpenDDLParser.cpp
   ../contrib/openddlparser/code/DDLNode.cpp
@@ -849,6 +864,7 @@ SET( assimp_src
   ${Clipper_SRCS}
   ${openddl_parser_SRCS}
   ${open3dgc_SRCS}
+  ${ziplib_SRCS}
   # Necessary to show the headers in the project when using the VC++ generator:
 
   ${PUBLIC_HEADERS}
@@ -912,8 +928,27 @@ SET_TARGET_PROPERTIES( assimp PROPERTIES
 )
 
 if (APPLE)
-  SET_TARGET_PROPERTIES( assimp PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${ASSIMP_LIB_INSTALL_DIR}")
-endif()
+  SET_TARGET_PROPERTIES( assimp PROPERTIES
+    INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${ASSIMP_LIB_INSTALL_DIR}"
+  )
+
+  if (BUILD_FRAMEWORK)
+    SET_TARGET_PROPERTIES( assimp PROPERTIES
+      FRAMEWORK TRUE
+      FRAMEWORK_VERSION C
+      MACOSX_FRAMEWORK_IDENTIFIER net.sf.assimp
+      PUBLIC_HEADER "${PUBLIC_HEADERS}"
+    )
+
+    # PUBLIC_HEADER option does not support directory structure creation
+    # add ./Compiler/*.h to assimp.framework via copy command
+    ADD_CUSTOM_COMMAND(TARGET assimp POST_BUILD
+      COMMAND "${CMAKE_COMMAND}" -E copy_directory
+         "../${HEADER_PATH}/Compiler"
+         assimp.framework/Headers/Compiler
+      COMMENT "Copying public ./Compiler/ header files to framework bundle's Headers/Compiler/")
+  ENDIF(BUILD_FRAMEWORK)
+ENDIF(APPLE)
 
 # Build against external unzip, or add ../contrib/unzip so
 # assimp can #include "unzip.h"
@@ -933,14 +968,16 @@ INSTALL( TARGETS assimp
   LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
   ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
   RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
+  FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
   COMPONENT ${LIBASSIMP_COMPONENT})
 INSTALL( FILES ${PUBLIC_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp COMPONENT assimp-dev)
 INSTALL( FILES ${COMPILER_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp/Compiler COMPONENT assimp-dev)
+
 if (ASSIMP_ANDROID_JNIIOSYSTEM)
   INSTALL(FILES ${HEADER_PATH}/${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/AndroidJNIIOSystem.h
     DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}
     COMPONENT assimp-dev)
-endif(ASSIMP_ANDROID_JNIIOSYSTEM)
+ENDIF(ASSIMP_ANDROID_JNIIOSYSTEM)
 
 if(MSVC AND ASSIMP_INSTALL_PDB)
   IF(CMAKE_GENERATOR MATCHES "^Visual Studio")
@@ -962,7 +999,7 @@ if(MSVC AND ASSIMP_INSTALL_PDB)
       CONFIGURATIONS RelWithDebInfo
     )
   ENDIF()
-endif ()
+ENDIF ()
 
 if (ASSIMP_COVERALLS)
     include(Coveralls)
@@ -974,4 +1011,4 @@ if (ASSIMP_COVERALLS)
         "${COVERAGE_SRCS}" # The source files.
         ON                 # If we should upload.
         "${PROJECT_SOURCE_DIR}/cmake-modules/") # (Optional) Alternate project cmake module path.
-endif()
+ENDIF()

+ 260 - 8
code/ColladaExporter.cpp

@@ -68,13 +68,17 @@ namespace Assimp
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
-void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
+void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
 {
     std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
     std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
 
     // invoke the exporter
     ColladaExporter iDoTheExportThing( pScene, pIOSystem, path, file);
+    
+    if (iDoTheExportThing.mOutput.fail()) {
+        throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
+    }
 
     // we're still here - export successfully completed. Write result to the given IOSYstem
     std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
@@ -104,7 +108,7 @@ ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, co
     // set up strings
     endstr = "\n";
 
-    // start writing
+    // start writing the file
     WriteFile();
 }
 
@@ -137,6 +141,9 @@ void ColladaExporter::WriteFile()
     WriteControllerLibrary();
 
     WriteSceneLibrary();
+	
+	// customized, Writes the animation library
+	WriteAnimationsLibrary();
 
     // useless Collada fu at the end, just in case we haven't had enough indirections, yet.
     mOutput << startstr << "<scene>" << endstr;
@@ -1125,6 +1132,7 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
         case FloatType_Color: floatsPerElement = 3; break;
         case FloatType_Mat4x4: floatsPerElement = 16; break;
         case FloatType_Weight: floatsPerElement = 1; break;
+		case FloatType_Time: floatsPerElement = 1; break;
         default:
             return;
     }
@@ -1201,7 +1209,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
         case FloatType_Weight:
             mOutput << startstr << "<param name=\"WEIGHT\" type=\"float\" />" << endstr;
             break;
-    }
+
+		// customized, add animation related
+		case FloatType_Time:
+			mOutput << startstr << "<param name=\"TIME\" type=\"float\" />" << endstr;
+			break;
+
+	}
 
     PopTag();
     mOutput << startstr << "</accessor>" << endstr;
@@ -1231,7 +1245,172 @@ void ColladaExporter::WriteSceneLibrary()
     PopTag();
     mOutput << startstr << "</library_visual_scenes>" << endstr;
 }
-
+// ------------------------------------------------------------------------------------------------
+void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
+{
+	const aiAnimation * anim = mScene->mAnimations[pIndex];
+	
+	if ( anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels ==0 )
+		return;
+	
+	const std::string animation_name_escaped = XMLEscape( anim->mName.C_Str() );
+	std::string idstr = anim->mName.C_Str();
+	std::string ending = std::string( "AnimId" ) + to_string(pIndex);
+	if (idstr.length() >= ending.length()) {
+		if (0 != idstr.compare (idstr.length() - ending.length(), ending.length(), ending)) {
+			idstr = idstr + ending;
+		}
+	} else {
+		idstr = idstr + ending;
+	}
+
+	const std::string idstrEscaped = XMLEscape(idstr);
+	
+	mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << endstr;
+	PushTag();
+	
+	for (size_t a = 0; a < anim->mNumChannels; ++a) {
+		const aiNodeAnim * nodeAnim = anim->mChannels[a];
+		
+		// sanity check
+		if ( nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys ||  nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys ) continue;
+		
+		{
+			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-input");
+
+			std::vector<ai_real> frames;
+			for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+				frames.push_back(static_cast<ai_real>(nodeAnim->mPositionKeys[i].mTime));
+			}
+			
+			WriteFloatArray( node_idstr , FloatType_Time, (const ai_real*) frames.data(), frames.size());
+			frames.clear();
+		}
+		
+		{
+			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-output");
+			
+			std::vector<ai_real> keyframes;
+			keyframes.reserve(nodeAnim->mNumPositionKeys * 16);
+			for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+				
+				aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue;
+				aiMatrix4x4 ScalingM;  // identity
+				ScalingM[0][0] = Scaling.x; ScalingM[1][1] = Scaling.y; ScalingM[2][2] = Scaling.z;
+				
+				aiQuaternion RotationQ = nodeAnim->mRotationKeys[i].mValue;
+				aiMatrix4x4 s = aiMatrix4x4( RotationQ.GetMatrix() );
+				aiMatrix4x4 RotationM(s.a1, s.a2, s.a3, 0, s.b1, s.b2, s.b3, 0, s.c1, s.c2, s.c3, 0, 0, 0, 0, 1);
+				
+				aiVector3D Translation = nodeAnim->mPositionKeys[i].mValue;
+				aiMatrix4x4 TranslationM;	// identity
+				TranslationM[0][3] = Translation.x; TranslationM[1][3] = Translation.y; TranslationM[2][3] = Translation.z;
+				
+				// Combine the above transformations
+				aiMatrix4x4 mat = TranslationM * RotationM * ScalingM;
+				
+				for( unsigned int j = 0; j < 4; ++j) {
+					keyframes.insert(keyframes.end(), mat[j], mat[j] + 4);
+                }
+			}
+			
+			WriteFloatArray( node_idstr, FloatType_Mat4x4, (const ai_real*) keyframes.data(), keyframes.size() / 16);
+		}
+		
+		{
+			std::vector<std::string> names;
+			for ( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+				if ( nodeAnim->mPreState == aiAnimBehaviour_DEFAULT
+					|| nodeAnim->mPreState == aiAnimBehaviour_LINEAR
+					|| nodeAnim->mPreState == aiAnimBehaviour_REPEAT
+					) {
+					names.push_back( "LINEAR" );
+				} else if (nodeAnim->mPostState == aiAnimBehaviour_CONSTANT) {
+					names.push_back( "STEP" );
+				}
+			}
+			
+			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation");
+			std::string arrayId = node_idstr + "-array";
+			
+			mOutput << startstr << "<source id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			PushTag();
+			
+			// source array
+			mOutput << startstr << "<Name_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\"> ";
+			for( size_t a = 0; a < names.size(); ++a ) {
+				mOutput << names[a] << " ";
+            }
+			mOutput << "</Name_array>" << endstr;
+			
+			mOutput << startstr << "<technique_common>" << endstr;
+			PushTag();
+
+			mOutput << startstr << "<accessor source=\"#" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
+			PushTag();
+			
+			mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
+			
+			PopTag();
+			mOutput << startstr << "</accessor>" << endstr;
+			
+			PopTag();
+			mOutput << startstr << "</technique_common>" << endstr;
+
+			PopTag();
+			mOutput << startstr << "</source>" << endstr;
+		}
+		
+	}
+	
+	for (size_t a = 0; a < anim->mNumChannels; ++a) {
+		const aiNodeAnim * nodeAnim = anim->mChannels[a];
+		
+		{
+		// samplers
+			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler");
+			mOutput << startstr << "<sampler id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			PushTag();
+			
+			mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
+			
+			PopTag();
+			mOutput << startstr << "</sampler>" << endstr;
+		}
+	}
+	
+	for (size_t a = 0; a < anim->mNumChannels; ++a) {
+		const aiNodeAnim * nodeAnim = anim->mChannels[a];
+		
+		{
+		// channels
+			mOutput << startstr << "<channel source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+		}
+	}
+	
+	PopTag();
+	mOutput << startstr << "</animation>" << endstr;
+	
+}
+// ------------------------------------------------------------------------------------------------
+void ColladaExporter::WriteAnimationsLibrary()
+{
+	const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
+	
+	if ( mScene->mNumAnimations > 0 ) {
+		mOutput << startstr << "<library_animations>" << endstr;
+		PushTag();
+		
+		// start recursive write at the root node
+		for( size_t a = 0; a < mScene->mNumAnimations; ++a)
+			WriteAnimationLibrary( a );
+
+		PopTag();
+		mOutput << startstr << "</library_animations>" << endstr;
+	}
+}
 // ------------------------------------------------------------------------------------------------
 // Helper to find a bone by name in the scene
 aiBone* findBone( const aiScene* scene, const char * name) {
@@ -1247,6 +1426,59 @@ aiBone* findBone( const aiScene* scene, const char * name) {
     return NULL;
 }
 
+// ------------------------------------------------------------------------------------------------
+const aiNode * findBoneNode( const aiNode* aNode, const aiBone* bone)
+{
+	if ( aNode && bone && aNode->mName == bone->mName ) {
+		return aNode;
+	}
+	
+	if ( aNode && bone ) {
+		for (unsigned int i=0; i < aNode->mNumChildren; ++i) {
+			aiNode * aChild = aNode->mChildren[i];
+			const aiNode * foundFromChild = 0;
+			if ( aChild ) {
+				foundFromChild = findBoneNode( aChild, bone );
+				if ( foundFromChild ) return foundFromChild;
+			}
+		}
+	}
+	
+	return NULL;
+}
+
+const aiNode * findSkeletonRootNode( const aiScene* scene, const aiMesh * mesh)
+{
+	std::set<const aiNode*> topParentBoneNodes;
+	if ( mesh && mesh->mNumBones > 0 ) {
+		for (unsigned int i=0; i < mesh->mNumBones; ++i) {
+			aiBone * bone = mesh->mBones[i];
+
+			const aiNode * node = findBoneNode( scene->mRootNode, bone);
+			if ( node ) {
+				while ( node->mParent && findBone(scene, node->mParent->mName.C_Str() ) != 0 ) {
+					node = node->mParent;
+				}
+				topParentBoneNodes.insert( node );
+			}
+		}
+	}
+	
+	if ( !topParentBoneNodes.empty() ) {
+		const aiNode * parentBoneNode = *topParentBoneNodes.begin();
+		if ( topParentBoneNodes.size() == 1 ) {
+			return parentBoneNode;
+		} else {
+			for (auto it : topParentBoneNodes) {
+				if ( it->mParent ) return it->mParent;
+			}
+			return parentBoneNode;
+		}
+	}
+	
+	return NULL;
+}
+
 // ------------------------------------------------------------------------------------------------
 // Recursively writes the given node
 void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
@@ -1274,12 +1506,22 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
     }
 
     const std::string node_name_escaped = XMLEscape(pNode->mName.data);
+	/* // customized, Note! the id field is crucial for inter-xml look up, it cannot be replaced with sid ?!
     mOutput << startstr
             << "<node ";
     if(is_skeleton_root)
         mOutput << "id=\"" << "skeleton_root" << "\" "; // For now, only support one skeleton in a scene.
     mOutput << (is_joint ? "s" : "") << "id=\"" << node_name_escaped;
-    mOutput << "\" name=\"" << node_name_escaped
+	 */
+	mOutput << startstr << "<node ";
+	if(is_skeleton_root) {
+		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"" : "") ; // For now, only support one skeleton in a scene.
+		mFoundSkeletonRootNodeID = node_name_escaped;
+	} else {
+		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"": "") ;
+	}
+	
+    mOutput << " name=\"" << node_name_escaped
             << "\" type=\"" << node_type
             << "\">" << endstr;
     PushTag();
@@ -1287,7 +1529,11 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
     // write transformation - we can directly put the matrix there
     // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards
     const aiMatrix4x4& mat = pNode->mTransformation;
-    mOutput << startstr << "<matrix sid=\"transform\">";
+	
+	// customized, sid should be 'matrix' to match with loader code.
+    //mOutput << startstr << "<matrix sid=\"transform\">";
+	mOutput << startstr << "<matrix sid=\"matrix\">";
+	
     mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
     mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " ";
     mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " ";
@@ -1315,7 +1561,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
     for( size_t a = 0; a < pNode->mNumMeshes; ++a )
     {
         const aiMesh* mesh = mScene->mMeshes[pNode->mMeshes[a]];
-        // do not instanciate mesh if empty. I wonder how this could happen
+        // do not instantiate mesh if empty. I wonder how this could happen
         if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
             continue;
 
@@ -1331,7 +1577,13 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
                     << endstr;
             PushTag();
 
-            mOutput << startstr << "<skeleton>#skeleton_root</skeleton>" << endstr;
+			// note! this mFoundSkeletonRootNodeID some how affects animation, it makes the mesh attaches to armature skeleton root node.
+			// use the first bone to find skeleton root
+			const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh );
+			if ( skeletonRootBoneNode ) {
+				mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() );
+			}
+            mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
         }
         mOutput << startstr << "<bind_material>" << endstr;
         PushTag();

+ 8 - 1
code/ColladaExporter.h

@@ -114,7 +114,9 @@ protected:
     /// Writes the given mesh
     void WriteGeometry( size_t pIndex);
 
-    enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight };
+    //enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight };
+    // customized to add animation related type
+	enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight, FloatType_Time };
 
     /// Writes a float array of the given type
     void WriteFloatArray( const std::string& pIdString, FloatDataType pType, const ai_real* pData, size_t pElementCount);
@@ -122,6 +124,11 @@ protected:
     /// Writes the scene library
     void WriteSceneLibrary();
 
+	// customized, Writes the animation library
+	void WriteAnimationsLibrary();
+	void WriteAnimationLibrary( size_t pIndex);
+	std::string mFoundSkeletonRootNodeID = "skeleton_root";	 	// will be replaced by found node id in the WriteNode call.
+	
     /// Recursively writes the given node
     void WriteNode( const aiScene* scene, aiNode* pNode);
 

+ 1 - 1
code/ColladaHelper.h

@@ -302,7 +302,7 @@ struct Accessor
     size_t mOffset;  // in number of values
     size_t mStride;  // Stride in number of values
     std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
-    size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, thats XYZ, for a color RGBA and so on.
+    size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, that's XYZ, for a color RGBA and so on.
                           // For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
     std::string mSource;   // URL of the source array
     mutable const Data* mData; // Pointer to the source array, if resolved. NULL else

+ 6 - 1
code/ColladaLoader.cpp

@@ -1619,7 +1619,7 @@ void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pSce
         mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
 
         // transparency, a very hard one. seemingly not all files are following the
-        // specification here (1.0 transparency => completly opaque)...
+        // specification here (1.0 transparency => completely opaque)...
         // therefore, we let the opportunity for the user to manually invert
         // the transparency if necessary and we add preliminary support for RGB_ZERO mode
         if(effect.mTransparency >= 0.f && effect.mTransparency <= 1.f) {
@@ -1778,6 +1778,11 @@ aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pPars
         tex->pcData = (aiTexel*)new char[tex->mWidth];
         memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
 
+        // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
+        // In FBX files textures are now stored internally by Assimp with their filename included
+        // Now Assimp can lookup thru the loaded textures after all data is processed
+        // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it
+        // This may occur on this case too, it has to be studied
         // setup texture reference string
         result.data[0] = '*';
         result.length = 1 + ASSIMP_itoa10(result.data+1,static_cast<unsigned int>(MAXLEN-1),static_cast<int32_t>(mTextures.size()));

+ 2 - 3
code/ColladaParser.cpp

@@ -224,7 +224,7 @@ void ColladaParser::ReadStructure()
 }
 
 // ------------------------------------------------------------------------------------------------
-// Reads asset informations such as coordinate system informations and legal blah
+// Reads asset information such as coordinate system information and legal blah
 void ColladaParser::ReadAssetInfo()
 {
     if( mReader->isEmptyElement())
@@ -2469,8 +2469,7 @@ void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t n
     size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
 
     // don't overrun the boundaries of the index list
-    size_t maxIndexRequested = baseOffset + numOffsets - 1;
-    ai_assert(maxIndexRequested < indices.size());
+    ai_assert((baseOffset + numOffsets - 1) < indices.size());
 
     // extract per-vertex channels using the global per-vertex offset
     for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)

+ 1 - 1
code/ColladaParser.h

@@ -77,7 +77,7 @@ namespace Assimp
         /** Reads the structure of the file */
         void ReadStructure();
 
-        /** Reads asset informations such as coordinate system informations and legal blah */
+        /** Reads asset information such as coordinate system information and legal blah */
         void ReadAssetInfo();
 
         /** Reads the animation library */

+ 328 - 0
code/D3MFExporter.cpp

@@ -0,0 +1,328 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
+
+#include "D3MFExporter.h"
+
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/DefaultLogger.hpp>
+
+#include "Exceptional.h"
+#include "3MFXmlTags.h"
+#include "D3MFOpcPackage.h"
+
+#include <contrib/zip/src/zip.h>
+
+namespace Assimp {
+
+void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) {
+    if ( nullptr == pIOSystem ) {
+        throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
+    }
+    D3MF::D3MFExporter myExporter( pFile, pScene );
+    if ( myExporter.validate() ) {
+        if ( pIOSystem->Exists( pFile ) ) {
+            if ( !pIOSystem->DeleteFile( pFile ) ) {
+                throw DeadlyExportError( "File exists, cannot override : " + std::string( pFile ) );
+            }
+        }
+        bool ok = myExporter.exportArchive(pFile);
+        if ( !ok ) {
+            throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
+        }
+    }
+}
+
+namespace D3MF {
+
+D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene )
+: mArchiveName( pFile )
+, m_zipArchive( nullptr )
+, mScene( pScene )
+, mModelOutput()
+, mRelOutput()
+, mContentOutput()
+, mBuildItems()
+, mRelations() {
+    // empty
+}
+
+D3MFExporter::~D3MFExporter() {
+    for ( size_t i = 0; i < mRelations.size(); ++i ) {
+        delete mRelations[ i ];
+    }
+    mRelations.clear();
+}
+
+bool D3MFExporter::validate() {
+    if ( mArchiveName.empty() ) {
+        return false;
+    }
+
+    if ( nullptr == mScene ) {
+        return false;
+    }
+
+    return true;
+}
+
+bool D3MFExporter::exportArchive( const char *file ) {
+    bool ok( true );
+
+    m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' );
+    if ( nullptr == m_zipArchive ) {
+        return false;
+    }
+    ok |= exportContentTypes();
+    ok |= export3DModel();
+    ok |= exportRelations();
+
+    zip_close( m_zipArchive );
+    m_zipArchive = nullptr;
+
+    return ok;
+}
+
+
+bool D3MFExporter::exportContentTypes() {
+    mContentOutput.clear();
+
+    mContentOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+    mContentOutput << std::endl;
+    mContentOutput << "<Types xmlns = \"http://schemas.openxmlformats.org/package/2006/content-types\">";
+    mContentOutput << std::endl;
+    mContentOutput << "<Default Extension = \"rels\" ContentType = \"application/vnd.openxmlformats-package.relationships+xml\" />";
+    mContentOutput << std::endl;
+    mContentOutput << "<Default Extension = \"model\" ContentType = \"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\" />";
+    mContentOutput << std::endl;
+    mContentOutput << "</Types>";
+    mContentOutput << std::endl;
+    exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE );
+
+    return true;
+}
+
+bool D3MFExporter::exportRelations() {
+    mRelOutput.clear();
+
+    mRelOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+    mRelOutput << std::endl;
+    mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
+
+    for ( size_t i = 0; i < mRelations.size(); ++i ) {
+        mRelOutput << "<Relationship Target=\"/" << mRelations[ i ]->target << "\" ";
+        mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
+        mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />";
+        mRelOutput << std::endl;
+    }
+    mRelOutput << "</Relationships>";
+    mRelOutput << std::endl;
+
+    writeRelInfoToFile( "_rels", ".rels" );
+    mRelOutput.flush();
+
+    return true;
+}
+
+bool D3MFExporter::export3DModel() {
+    mModelOutput.clear();
+
+    writeHeader();
+    mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\""
+            << "xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
+            << std::endl;
+    mModelOutput << "<" << XmlTag::resources << ">";
+    mModelOutput << std::endl;
+
+    writeObjects();
+
+
+    mModelOutput << "</" << XmlTag::resources << ">";
+    mModelOutput << std::endl;
+    writeBuild();
+
+    mModelOutput << "</" << XmlTag::model << ">\n";
+
+    OpcPackageRelationship *info = new OpcPackageRelationship;
+    info->id = "rel0";
+    info->target = "/3D/3DModel.model";
+    info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
+    mRelations.push_back( info );
+
+    writeModelToArchive( "3D", "3DModel.model" );
+    mModelOutput.flush();
+
+    return true;
+}
+
+void D3MFExporter::writeHeader() {
+    mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF - 8\"?>";
+    mModelOutput << std::endl;
+}
+
+void D3MFExporter::writeObjects() {
+    if ( nullptr == mScene->mRootNode ) {
+        return;
+    }
+
+    aiNode *root = mScene->mRootNode;
+    for ( unsigned int i = 0; i < root->mNumChildren; ++i ) {
+        aiNode *currentNode( root->mChildren[ i ] );
+        if ( nullptr == currentNode ) {
+            continue;
+        }
+        mModelOutput << "<" << XmlTag::object << " id=\"" << currentNode->mName.C_Str() << "\" type=\"model\">";
+        mModelOutput << std::endl;
+        for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) {
+            aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ];
+            if ( nullptr == currentMesh ) {
+                continue;
+            }
+            writeMesh( currentMesh );
+        }
+        mBuildItems.push_back( i );
+
+        mModelOutput << "</" << XmlTag::object << ">";
+        mModelOutput << std::endl;
+    }
+}
+
+void D3MFExporter::writeMesh( aiMesh *mesh ) {
+    if ( nullptr == mesh ) {
+        return;
+    }
+
+    mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
+    mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
+    for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) {
+        writeVertex( mesh->mVertices[ i ] );
+    }
+    mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
+
+    writeFaces( mesh );
+
+    mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
+}
+
+void D3MFExporter::writeVertex( const aiVector3D &pos ) {
+    mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
+    mModelOutput << std::endl;
+}
+
+void D3MFExporter::writeFaces( aiMesh *mesh ) {
+    if ( nullptr == mesh ) {
+        return;
+    }
+
+    if ( !mesh->HasFaces() ) {
+        return;
+    }
+    mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
+    for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) {
+        aiFace &currentFace = mesh->mFaces[ i ];
+        mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\""
+                << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] << "\"/>";
+        mModelOutput << std::endl;
+    }
+    mModelOutput << "</" << XmlTag::triangles << ">";
+    mModelOutput << std::endl;
+}
+
+void D3MFExporter::writeBuild() {
+    mModelOutput << "<" << XmlTag::build << ">" << std::endl;
+
+    for ( size_t i = 0; i < mBuildItems.size(); ++i ) {
+        mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 1 << "\"/>";
+        mModelOutput << std::endl;
+    }
+    mModelOutput << "</" << XmlTag::build << ">";
+    mModelOutput << std::endl;
+}
+
+void D3MFExporter::exportContentTyp( const std::string &filename ) {
+    if ( nullptr == m_zipArchive ) {
+        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+    }
+    const std::string entry = filename;
+    zip_entry_open( m_zipArchive, entry.c_str() );
+
+    const std::string &exportTxt( mContentOutput.str() );
+    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+
+    zip_entry_close( m_zipArchive );
+}
+
+void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) {
+    if ( nullptr == m_zipArchive ) {
+        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+    }
+    const std::string entry = folder + "/" + modelName;
+    zip_entry_open( m_zipArchive, entry.c_str() );
+
+    const std::string &exportTxt( mModelOutput.str() );
+    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+
+    zip_entry_close( m_zipArchive );
+}
+
+void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) {
+    if ( nullptr == m_zipArchive ) {
+        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+    }
+    const std::string entry = folder + "/" + relName;
+    zip_entry_open( m_zipArchive, entry.c_str() );
+
+    const std::string &exportTxt( mRelOutput.str() );
+    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+
+    zip_entry_close( m_zipArchive );
+}
+
+
+} // Namespace D3MF
+} // Namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_3MF_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 103 - 0
code/D3MFExporter.h

@@ -0,0 +1,103 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <memory>
+#include <sstream>
+#include <vector>
+#include <assimp/vector3.h>
+
+struct aiScene;
+struct aiNode;
+struct aiMaterial;
+struct aiMesh;
+
+struct zip_t;
+
+namespace Assimp {
+
+class IOStream;
+
+namespace D3MF {
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
+
+struct OpcPackageRelationship;
+
+class D3MFExporter {
+public:
+    D3MFExporter( const char* pFile, const aiScene* pScene );
+    ~D3MFExporter();
+    bool validate();
+    bool exportArchive( const char *file );
+    bool exportContentTypes();
+    bool exportRelations();
+    bool export3DModel();
+
+protected:
+    void writeHeader();
+    void writeObjects();
+    void writeMesh( aiMesh *mesh );
+    void writeVertex( const aiVector3D &pos );
+    void writeFaces( aiMesh *mesh );
+    void writeBuild();
+    void exportContentTyp( const std::string &filename );
+    void writeModelToArchive( const std::string &folder, const std::string &modelName );
+    void writeRelInfoToFile( const std::string &folder, const std::string &relName );
+
+private:
+    std::string mArchiveName;
+    zip_t *m_zipArchive;
+    const aiScene *mScene;
+    std::ostringstream mModelOutput;
+    std::ostringstream mRelOutput;
+    std::ostringstream mContentOutput;
+    std::vector<unsigned int> mBuildItems;
+    std::vector<OpcPackageRelationship*> mRelations;
+};
+
+#endif // ASSIMP_BUILD_NO_3MF_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
+
+} // Namespace D3MF
+} // Namespace Assimp
+

+ 29 - 66
code/D3MFImporter.cpp

@@ -59,65 +59,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "D3MFOpcPackage.h"
 #include <contrib/unzip/unzip.h>
 #include "irrXMLWrapper.h"
+#include "3MFXmlTags.h"
 
 namespace Assimp {
 namespace D3MF {
 
-namespace XmlTag {
-    static const std::string model     = "model";
-    static const std::string metadata  = "metadata";
-    static const std::string resources = "resources";
-    static const std::string object    = "object";
-    static const std::string mesh      = "mesh";
-    static const std::string vertices  = "vertices";
-    static const std::string vertex    = "vertex";
-    static const std::string triangles = "triangles";
-    static const std::string triangle  = "triangle";
-    static const std::string x         = "x";
-    static const std::string y         = "y";
-    static const std::string z         = "z";
-    static const std::string v1        = "v1";
-    static const std::string v2        = "v2";
-    static const std::string v3        = "v3";
-    static const std::string id        = "id";
-    static const std::string name      = "name";
-    static const std::string type      = "type";
-    static const std::string build     = "build";
-    static const std::string item      = "item";
-    static const std::string objectid  = "objectid";
-    static const std::string transform = "transform";
-}
-
-
-class XmlSerializer
-{
+class XmlSerializer {
 public:
     XmlSerializer(XmlReader* xmlReader)
-        : xmlReader(xmlReader)
-    {
+    : xmlReader(xmlReader) {
 		// empty
     }
 
+    ~XmlSerializer() {
+        // empty
+    }
+
     void ImportXml(aiScene* scene) {
         scene->mRootNode = new aiNode();
         std::vector<aiNode*> children;
 
-        while(ReadToEndElement(D3MF::XmlTag::model))
-        {
-
-            if(xmlReader->getNodeName() == D3MF::XmlTag::object)
-            {
+        while(ReadToEndElement(D3MF::XmlTag::model)) {
+            if(xmlReader->getNodeName() == D3MF::XmlTag::object) {
                 children.push_back(ReadObject(scene));
-            }
-            else if(xmlReader->getNodeName() == D3MF::XmlTag::build)
-            {
+            } else if(xmlReader->getNodeName() == D3MF::XmlTag::build) {
 
             }
         }
 
-        if(scene->mRootNode->mName.length == 0)
-            scene->mRootNode->mName.Set("3MF");
-
+        if ( scene->mRootNode->mName.length == 0 ) {
+            scene->mRootNode->mName.Set( "3MF" );
+        }
 
         scene->mNumMeshes = static_cast<unsigned int>(meshes.size());
         scene->mMeshes = new aiMesh*[scene->mNumMeshes]();
@@ -128,13 +100,12 @@ public:
         scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren]();
 
         std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
-
     }
 
 private:
     aiNode* ReadObject(aiScene* scene)
     {
-        ScopeGuard<aiNode> node(new aiNode());
+        std::unique_ptr<aiNode> node(new aiNode());
 
         std::vector<unsigned long> meshIds;
 
@@ -174,14 +145,12 @@ private:
 
         std::copy(meshIds.begin(), meshIds.end(), node->mMeshes);
 
-        return node.dismiss();
+        return node.release();
 
     }
 
-    aiMesh* ReadMesh()
-    {
+    aiMesh* ReadMesh() {
         aiMesh* mesh = new aiMesh();
-
         while(ReadToEndElement(D3MF::XmlTag::mesh))
         {
             if(xmlReader->getNodeName() == D3MF::XmlTag::vertices)
@@ -192,10 +161,8 @@ private:
             {
                 ImportTriangles(mesh);
             }
-
         }
 
-
         return mesh;
     }
 
@@ -216,6 +183,7 @@ private:
         std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
 
     }
+
     aiVector3D ReadVertex()
     {
         aiVector3D vertex;
@@ -261,7 +229,6 @@ private:
     }
 
 private:
-
     bool ReadToStartElement(const std::string& startTag)
     {
         while(xmlReader->read())
@@ -305,6 +272,7 @@ private:
 
 } //namespace D3MF
 
+static const std::string Extension = "3mf";
 
 static const aiImporterDesc desc = {
     "3mf Importer",
@@ -316,24 +284,22 @@ static const aiImporterDesc desc = {
     0,
     0,
     0,
-    "3mf"
+    Extension.c_str()
 };
 
 
 D3MFImporter::D3MFImporter()
-{
-
+: BaseImporter() {
+    // empty
 }
 
-D3MFImporter::~D3MFImporter()
-{
-
+D3MFImporter::~D3MFImporter() {
+    // empty
 }
 
-bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const
-{
+bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
     const std::string extension = GetExtension(pFile);
-    if(extension == "3mf") {
+    if(extension == Extension ) {
         return true;
     } else if ( !extension.length() || checkSig ) {
         if (nullptr == pIOHandler ) {
@@ -344,18 +310,15 @@ bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool
     return false;
 }
 
-void D3MFImporter::SetupProperties(const Importer *pImp)
-{
-
+void D3MFImporter::SetupProperties(const Importer * /*pImp*/) {
+    // empty
 }
 
-const aiImporterDesc *D3MFImporter::GetInfo() const
-{
+const aiImporterDesc *D3MFImporter::GetInfo() const {
     return &desc;
 }
 
-void D3MFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler)
-{
+void D3MFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
     D3MF::D3MFOpcPackage opcPackage(pIOHandler, pFile);
 
     std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream()));

+ 5 - 7
code/D3MFImporter.h

@@ -46,21 +46,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
-class D3MFImporter : public BaseImporter
-{
+class D3MFImporter : public BaseImporter {
 public:
+    // BaseImporter interface
     D3MFImporter();
     ~D3MFImporter();
-
-    // BaseImporter interface
-public:
     bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
     void SetupProperties(const Importer *pImp);
     const aiImporterDesc *GetInfo() const;
 
 protected:
     void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
-
 };
-}
+
+} // Namespace Assimp
+
 #endif // AI_D3MFLOADER_H_INCLUDED

+ 78 - 141
code/D3MFOpcPackage.cpp

@@ -55,49 +55,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <map>
 #include <algorithm>
 #include <cassert>
-
 #include <contrib/unzip/unzip.h>
+#include "3MFXmlTags.h"
 
 namespace Assimp {
 
 namespace D3MF {
 
-namespace XmlTag {
-    static const std::string CONTENT_TYPES_ARCHIVE  = "[Content_Types].xml";
-    static const std::string ROOT_RELATIONSHIPS_ARCHIVE  = "_rels/.rels";
-    static const std::string SCHEMA_CONTENTTYPES         = "http://schemas.openxmlformats.org/package/2006/content-types";
-    static const std::string SCHEMA_RELATIONSHIPS        = "http://schemas.openxmlformats.org/package/2006/relationships";
-    static const std::string RELS_RELATIONSHIP_CONTAINER = "Relationships";
-    static const std::string RELS_RELATIONSHIP_NODE      = "Relationship";
-    static const std::string RELS_ATTRIB_TARGET         = "Target";
-    static const std::string RELS_ATTRIB_TYPE            = "Type";
-    static const std::string RELS_ATTRIB_ID              = "Id";
-    static const std::string PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel";
-    static const std::string PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket";
-    static const std::string PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture";
-    static const std::string PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
-    static const std::string PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail";
-}
-
 class IOSystem2Unzip {
-
-    public:
-
-        static voidpf open(voidpf opaque, const char* filename, int mode);
-
-        static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
-
-        static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
-
-        static long tell(voidpf opaque, voidpf stream);
-
-        static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
-
-        static int close(voidpf opaque, voidpf stream);
-
-        static int testerror(voidpf opaque, voidpf stream);
-
-        static zlib_filefunc_def get(IOSystem* pIOHandler);
+public:
+    static voidpf open(voidpf opaque, const char* filename, int mode);
+    static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
+    static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
+    static long tell(voidpf opaque, voidpf stream);
+    static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
+    static int close(voidpf opaque, voidpf stream);
+    static int testerror(voidpf opaque, voidpf stream);
+    static zlib_filefunc_def get(IOSystem* pIOHandler);
 };
 
 voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
@@ -116,7 +90,6 @@ voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
         }
     }
 
-
     return (voidpf) io_system->Open(filename, mode_fopen);
 }
 
@@ -186,44 +159,33 @@ zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
     return mapping;
 }
 
-
-class ZipFile : public IOStream
-{
+class ZipFile : public IOStream {
     friend class D3MFZipArchive;
 
 public:
     explicit ZipFile(size_t size);
-
-    ~ZipFile();
-
+    virtual ~ZipFile();
     size_t Read(void* pvBuffer, size_t pSize, size_t pCount );
-
     size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/);
-
     size_t FileSize() const;
-
     aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/);
-
     size_t Tell() const;
-
     void Flush();
 
 private:
-
-    void* m_Buffer;
-
+    void *m_Buffer;
     size_t m_Size;
-
 };
 
-ZipFile::ZipFile(size_t size) : m_Size(size) {
+ZipFile::ZipFile(size_t size)
+: m_Buffer( nullptr )
+, m_Size(size) {
     ai_assert(m_Size != 0);
-
-    m_Buffer = malloc(m_Size);
+    m_Buffer = ::malloc(m_Size);
 }
 
 ZipFile::~ZipFile() {
-    free(m_Buffer);
+    ::free(m_Buffer);
     m_Buffer = NULL;
 }
 
@@ -236,8 +198,12 @@ size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
     return size;
 }
 
-size_t ZipFile::Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) {
-    return 0;
+size_t ZipFile::Write(const void* pvBuffer, size_t size, size_t pCount ) {
+    const size_t size_to_write( size * pCount );
+    if ( 0 == size_to_write ) {
+        return 0U;
+    }
+    return 0U;
 }
 
 size_t ZipFile::FileSize() const {
@@ -256,55 +222,36 @@ void ZipFile::Flush() {
     // empty
 }
 
-
-class D3MFZipArchive : public IOSystem
-{
+class D3MFZipArchive : public IOSystem {
 public:
-
     static const unsigned int FileNameSize = 256;
 
-public:
-
     D3MFZipArchive(IOSystem* pIOHandler, const std::string & rFile);
-
     ~D3MFZipArchive();
-
     bool Exists(const char* pFile) const;
-
     char getOsSeparator() const;
-
     IOStream* Open(const char* pFile, const char* pMode = "rb");
-
     void Close(IOStream* pFile);
-
     bool isOpen() const;
-
     void getFileList(std::vector<std::string> &rFileList);
 
 private:
-
     bool mapArchive();
 
 private:
-
     unzFile m_ZipFileHandle;
-
     std::map<std::string, ZipFile*> m_ArchiveMap;
-
 };
 
-
 // ------------------------------------------------------------------------------------------------
 //  Constructor.
 D3MFZipArchive::D3MFZipArchive(IOSystem* pIOHandler, const std::string& rFile)
-    : m_ZipFileHandle(NULL), m_ArchiveMap()
-{
-    if (! rFile.empty())
-    {                
+: m_ZipFileHandle(NULL)
+, m_ArchiveMap() {
+    if (! rFile.empty()) {                
         zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);            
 
         m_ZipFileHandle = unzOpen2(rFile.c_str(), &mapping);
-
         if(m_ZipFileHandle != NULL) {            
             mapArchive();
         }
@@ -379,6 +326,7 @@ IOStream *D3MFZipArchive::Open(const char* pFile, const char* /*pMode*/) {
 // ------------------------------------------------------------------------------------------------
 //  Close a filestream.
 void D3MFZipArchive::Close(IOStream *pFile) {
+    (void)(pFile);
     ai_assert(pFile != NULL);
 
     // We don't do anything in case the file would be opened again in the future
@@ -433,24 +381,12 @@ bool D3MFZipArchive::mapArchive() {
 
 // ------------------------------------------------------------------------------------------------
 
-struct OpcPackageRelationship
-{
-    std::string id;
-    std::string type;
-    std::string target;
-};
-
 typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
 
-class OpcPackageRelationshipReader
-{
+class OpcPackageRelationshipReader {
 public:
-
-    OpcPackageRelationshipReader(XmlReader* xmlReader)
-    {        
-
-        while(xmlReader->read())
-        {
+    OpcPackageRelationshipReader(XmlReader* xmlReader) {        
+        while(xmlReader->read()) {
             if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
                xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_CONTAINER)
             {
@@ -473,57 +409,68 @@ public:
         }
     }
 
-    void ParseAttributes(XmlReader*)
-    {
+    void ParseAttributes(XmlReader*) {
+        // empty
+    }
 
+    bool validateRels( OpcPackageRelationshipPtr &relPtr ) {
+        if ( relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty() ) {
+            return false;
+        }
+        return true;
     }
 
-    void ParseChildNode(XmlReader* xmlReader)
-    {        
+    void ParseChildNode(XmlReader* xmlReader) {        
         OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
 
-        relPtr->id = xmlReader->getAttributeValue(XmlTag::RELS_ATTRIB_ID.c_str());
-        relPtr->type = xmlReader->getAttributeValue(XmlTag::RELS_ATTRIB_TYPE.c_str());
-        relPtr->target = xmlReader->getAttributeValue(XmlTag::RELS_ATTRIB_TARGET.c_str());
-
-        m_relationShips.push_back(relPtr);
+        relPtr->id = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_ID.c_str());
+        relPtr->type = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TYPE.c_str());
+        relPtr->target = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TARGET.c_str());
+        if ( validateRels( relPtr ) ) {
+            m_relationShips.push_back( relPtr );
+        }
     }
+
     std::vector<OpcPackageRelationshipPtr> m_relationShips;
 };
 // ------------------------------------------------------------------------------------------------
 
 D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
-    : m_RootStream(nullptr)
-{    
-    zipArchive.reset(new D3MF::D3MFZipArchive( pIOHandler, rFile ));    
-    if(!zipArchive->isOpen()) {
+: mRootStream(nullptr)
+, mZipArchive() {    
+    mZipArchive.reset( new D3MF::D3MFZipArchive( pIOHandler, rFile ) );    
+    if(!mZipArchive->isOpen()) {
         throw DeadlyImportError("Failed to open file " + rFile+ ".");
     }
 
     std::vector<std::string> fileList;
-    zipArchive->getFileList(fileList);
+    mZipArchive->getFileList(fileList);
 
-    for(auto& file: fileList){
+    for (auto& file: fileList) {
         if(file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) {
             //PkgRelationshipReader pkgRelReader(file, archive);
-            ai_assert(zipArchive->Exists(file.c_str()));
+            ai_assert(mZipArchive->Exists(file.c_str()));
 
-            IOStream *fileStream = zipArchive->Open(file.c_str());
+            IOStream *fileStream = mZipArchive->Open(file.c_str());
 
             ai_assert(fileStream != nullptr);
 
             std::string rootFile = ReadPackageRootRelationship(fileStream);
-            if(rootFile.size() > 0 && rootFile[0] == '/')
-                rootFile = rootFile.substr(1);
+            if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) {
+                rootFile = rootFile.substr( 1 );
+                if ( rootFile[ 0 ] == '/' ) {
+                    // deal with zipbug
+                    rootFile = rootFile.substr( 1 );
+                }
+            }
 
             DefaultLogger::get()->debug(rootFile);
 
-            m_RootStream = zipArchive->Open(rootFile.c_str());
-
-            ai_assert(m_RootStream != nullptr);
-
-
-
+            mRootStream = mZipArchive->Open(rootFile.c_str());
+            ai_assert( mRootStream != nullptr );
+            if ( nullptr == mRootStream ) {
+                throw DeadlyExportError( "Cannot open rootfile in archive : " + rootFile );
+            }
 
         //    const size_t size = zipArchive->FileSize();
         //    m_Data.resize( size );
@@ -534,50 +481,40 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
         //        m_Data.clear();
         //        return false;
         //    }
-            zipArchive->Close( fileStream );
+            mZipArchive->Close( fileStream );
 
-        }
-        else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE)
-        {
+        } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
 
         }
     }
 }
 
-D3MFOpcPackage::~D3MFOpcPackage()
-{
-
+D3MFOpcPackage::~D3MFOpcPackage() {
+    // empty
 }
 
-IOStream* D3MFOpcPackage::RootStream() const
-{
-    return m_RootStream;
+IOStream* D3MFOpcPackage::RootStream() const {
+    return mRootStream;
 }
 
-
-std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream)
-{
-
+std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) {
     std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(stream));
     std::unique_ptr<XmlReader> xml(irr::io::createIrrXMLReader(xmlStream.get()));
 
     OpcPackageRelationshipReader reader(xml.get());
 
-
     auto itr = std::find_if(reader.m_relationShips.begin(), reader.m_relationShips.end(), [](const OpcPackageRelationshipPtr& rel){
         return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
     });
 
-
-
     if(itr == reader.m_relationShips.end())
-        throw DeadlyImportError("Cannot find" + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
+        throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
 
     return (*itr)->target;
 }
 
-} //namespace D3MF
+} // Namespace D3MF
 
-}
+} // Namespace Assimp
 
 #endif //ASSIMP_BUILD_NO_3MF_IMPORTER

+ 12 - 7
code/D3MFOpcPackage.h

@@ -48,26 +48,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "irrXMLWrapper.h"
 
 namespace Assimp {
-
 namespace D3MF {
 
 typedef irr::io::IrrXMLReader XmlReader;
 typedef std::shared_ptr<XmlReader> XmlReaderPtr;
 
+struct OpcPackageRelationship {
+    std::string id;
+    std::string type;
+    std::string target;
+};
+
 class D3MFZipArchive;
 
-class D3MFOpcPackage
-{
+class D3MFOpcPackage {
 public:
     D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile);
     ~D3MFOpcPackage();
-
     IOStream* RootStream() const;
-private:
+
+protected:
     std::string ReadPackageRootRelationship(IOStream* stream);
+
 private:
-    IOStream* m_RootStream;
-    std::unique_ptr<D3MFZipArchive> zipArchive;
+    IOStream* mRootStream;
+    std::unique_ptr<D3MFZipArchive> mZipArchive;
 };
 
 }

+ 1 - 1
code/DeboneProcess.h

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

+ 2 - 1
code/DefaultIOStream.cpp

@@ -123,7 +123,8 @@ size_t DefaultIOStream::FileSize() const
         // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file
 #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
         struct __stat64 fileStat;
-        int err = _stat64(  mFilename.c_str(), &fileStat );
+        //using fileno + fstat avoids having to handle the filename
+        int err = _fstat64(  _fileno(mFile), &fileStat );
         if (0 != err)
             return 0;
         mCachedSize = (size_t) (fileStat.st_size);

+ 75 - 31
code/DefaultIOSystem.cpp

@@ -49,37 +49,54 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ai_assert.h>
 #include <stdlib.h>
 
-
 #ifdef __unix__
 #include <sys/param.h>
 #include <stdlib.h>
 #endif
 
-using namespace Assimp;
+#ifdef _WIN32
+#include <windows.h>
+#endif
 
-// ------------------------------------------------------------------------------------------------
-// Constructor.
-DefaultIOSystem::DefaultIOSystem()
-{
-    // nothing to do here
-}
+using namespace Assimp;
 
-// ------------------------------------------------------------------------------------------------
-// Destructor.
-DefaultIOSystem::~DefaultIOSystem()
-{
-    // nothing to do here
-}
+// maximum path length
+// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
+#ifdef PATH_MAX
+#   define PATHLIMIT PATH_MAX
+#else
+#   define PATHLIMIT 4096
+#endif
 
 // ------------------------------------------------------------------------------------------------
 // Tests for the existence of a file at the given path.
 bool DefaultIOSystem::Exists( const char* pFile) const
 {
+#ifdef _WIN32
+    wchar_t fileName16[PATHLIMIT];
+
+    bool isUnicode = IsTextUnicode(pFile, strlen(pFile), NULL);
+    if (isUnicode) {
+
+        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT);
+        struct _stat64 filestat;
+        if (0 != _wstat64(fileName16, &filestat)) {
+            return false;
+        }
+    } else {
+        FILE* file = ::fopen(pFile, "rb");
+        if (!file)
+            return false;
+
+        ::fclose(file);
+    }
+#else
     FILE* file = ::fopen( pFile, "rb");
     if( !file)
         return false;
 
     ::fclose( file);
+#endif
     return true;
 }
 
@@ -89,10 +106,22 @@ IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode)
 {
     ai_assert(NULL != strFile);
     ai_assert(NULL != strMode);
-
-    FILE* file = ::fopen( strFile, strMode);
-    if( NULL == file)
-        return NULL;
+    FILE* file;
+#ifdef _WIN32
+    wchar_t fileName16[PATHLIMIT];
+    bool isUnicode = IsTextUnicode(strFile, strlen(strFile), NULL );
+    if (isUnicode) {
+        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT);
+        std::string mode8(strMode);
+        file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str());
+    } else {
+        file = ::fopen(strFile, strMode);
+    }
+#else
+    file = ::fopen(strFile, strMode);
+#endif
+    if (nullptr == file)
+        return nullptr;
 
     return new DefaultIOStream(file, (std::string) strFile);
 }
@@ -122,32 +151,47 @@ bool IOSystem::ComparePaths (const char* one, const char* second) const
     return !ASSIMP_stricmp(one,second);
 }
 
-// maximum path length
-// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
-#ifdef PATH_MAX
-#   define PATHLIMIT PATH_MAX
-#else
-#   define PATHLIMIT 4096
-#endif
-
 // ------------------------------------------------------------------------------------------------
 // Convert a relative path into an absolute path
-inline void MakeAbsolutePath (const char* in, char* _out)
+inline static void MakeAbsolutePath (const char* in, char* _out)
 {
     ai_assert(in && _out);
-    char* ret;
 #if defined( _MSC_VER ) || defined( __MINGW32__ )
-    ret = ::_fullpath( _out, in, PATHLIMIT );
+    bool isUnicode = IsTextUnicode(in, strlen(in), NULL);
+    if (isUnicode) {
+        wchar_t out16[PATHLIMIT];
+        wchar_t in16[PATHLIMIT];
+        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT);
+        wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT);
+        if (ret) {
+            WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr);
+        }
+        if (!ret) {
+            // preserve the input path, maybe someone else is able to fix
+            // the path before it is accessed (e.g. our file system filter)
+            DefaultLogger::get()->warn("Invalid path: " + std::string(in));
+            strcpy(_out, in);
+        }
+
+    } else {
+        char* ret = :: _fullpath(_out, in, PATHLIMIT);
+        if (!ret) {
+            // preserve the input path, maybe someone else is able to fix
+            // the path before it is accessed (e.g. our file system filter)
+            DefaultLogger::get()->warn("Invalid path: " + std::string(in));
+            strcpy(_out, in);
+        }
+    }
 #else
     // use realpath
-    ret = realpath(in, _out);
-#endif
+    char* ret = realpath(in, _out);
     if(!ret) {
         // preserve the input path, maybe someone else is able to fix
         // the path before it is accessed (e.g. our file system filter)
         DefaultLogger::get()->warn("Invalid path: "+std::string(in));
         strcpy(_out,in);
     }
+#endif
 }
 
 // ------------------------------------------------------------------------------------------------

+ 147 - 0
code/EmbedTexturesProcess.cpp

@@ -0,0 +1,147 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "EmbedTexturesProcess.h"
+#include "ParsingUtils.h"
+#include "ProcessHelper.h"
+
+#include <fstream>
+
+using namespace Assimp;
+
+EmbedTexturesProcess::EmbedTexturesProcess()
+: BaseProcess() {
+}
+
+EmbedTexturesProcess::~EmbedTexturesProcess() {
+}
+
+bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
+    return (pFlags & aiProcess_EmbedTextures) != 0;
+}
+
+void EmbedTexturesProcess::SetupProperties(const Importer* pImp) {
+    mRootPath = pImp->GetPropertyString("sourceFilePath");
+    mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u);
+}
+
+void EmbedTexturesProcess::Execute(aiScene* pScene) {
+    if (pScene == nullptr || pScene->mRootNode == nullptr) return;
+
+    aiString path;
+
+    uint32_t embeddedTexturesCount = 0u;
+
+    for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) {
+        auto material = pScene->mMaterials[matId];
+
+        for (auto ttId = 1u; ttId < AI_TEXTURE_TYPE_MAX; ++ttId) {
+            auto tt = static_cast<aiTextureType>(ttId);
+            auto texturesCount = material->GetTextureCount(tt);
+
+            for (auto texId = 0u; texId < texturesCount; ++texId) {
+                material->GetTexture(tt, texId, &path);
+                if (path.data[0] == '*') continue; // Already embedded
+
+                // Indeed embed
+                if (addTexture(pScene, path.data)) {
+                    auto embeddedTextureId = pScene->mNumTextures - 1u;
+                    ::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId);
+                    material->AddProperty(&path, AI_MATKEY_TEXTURE(tt, texId));
+                    embeddedTexturesCount++;
+                }
+            }
+        }
+    }
+
+    char stringBuffer[128];
+    ::ai_snprintf(stringBuffer, 128, "EmbedTexturesProcess finished. Embedded %u textures.", embeddedTexturesCount);
+    DefaultLogger::get()->info(stringBuffer);
+}
+
+bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
+    uint32_t imageSize = 0;
+    std::string imagePath = path;
+
+    // Test path directly
+    std::ifstream file(imagePath, std::ios::binary | std::ios::ate);
+    if ((imageSize = file.tellg()) == -1u) {
+        DefaultLogger::get()->warn("EmbedTexturesProcess: Cannot find image: " + imagePath + ". Will try to find it in root folder.");
+
+        // Test path in root path
+        imagePath = mRootPath + path;
+        file.open(imagePath, std::ios::binary | std::ios::ate);
+        if ((imageSize = file.tellg()) == -1u) {
+            // Test path basename in root path
+            imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u);
+            file.open(imagePath, std::ios::binary | std::ios::ate);
+            if ((imageSize = file.tellg()) == -1u) {
+                DefaultLogger::get()->error("EmbedTexturesProcess: Unable to embed texture: " + path + ".");
+                return false;
+            }
+        }
+    }
+
+    aiTexel* imageContent = new aiTexel[1u + imageSize / sizeof(aiTexel)];
+    file.seekg(0, std::ios::beg);
+    file.read(reinterpret_cast<char*>(imageContent), imageSize);
+
+    // Enlarging the textures table
+    auto textureId = pScene->mNumTextures++;
+    auto oldTextures = pScene->mTextures;
+    pScene->mTextures = new aiTexture*[pScene->mNumTextures];
+    memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u));
+
+    // Add the new texture
+    auto pTexture = new aiTexture();
+    pTexture->mHeight = 0; // Means that this is still compressed
+    pTexture->mWidth = imageSize;
+    pTexture->pcData = imageContent;
+
+    auto extension = path.substr(path.find_last_of('.') + 1u);
+    std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+    if (extension == "jpeg") extension = "jpg";
+    strcpy(pTexture->achFormatHint, extension.c_str());
+
+    pScene->mTextures[textureId] = pTexture;
+
+    return true;
+}

+ 84 - 0
code/EmbedTexturesProcess.h

@@ -0,0 +1,84 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#pragma once
+
+#include "BaseProcess.h"
+
+#include <string>
+
+struct aiNode;
+
+namespace Assimp {
+
+/**
+ *  Force embedding of textures (using the path = "*1" convention).
+ *  If a texture's file does not exist at the specified path
+ *  (due, for instance, to an absolute path generated on another system),
+ *  it will check if a file with the same name exists at the root folder
+ *  of the imported model. And if so, it uses that.
+ */
+class ASSIMP_API EmbedTexturesProcess : public BaseProcess {
+public:
+    /// The default class constructor.
+    EmbedTexturesProcess();
+
+    /// The class destructor.
+    virtual ~EmbedTexturesProcess();
+
+    /// Overwritten, @see BaseProcess
+    virtual bool IsActive(unsigned int pFlags) const;
+
+    /// Overwritten, @see BaseProcess
+    virtual void SetupProperties(const Importer* pImp);
+
+    /// Overwritten, @see BaseProcess
+    virtual void Execute(aiScene* pScene);
+
+private:
+    // Resolve the path and add the file content to the scene as a texture.
+    bool addTexture(aiScene* pScene, std::string path) const;
+
+private:
+    std::string mRootPath;
+};
+
+} // namespace Assimp

+ 24 - 13
code/Exporter.cpp

@@ -83,6 +83,7 @@ void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportPrope
 void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*);
 void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*);
@@ -91,30 +92,34 @@ void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperti
 void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
 
 // ------------------------------------------------------------------------------------------------
 // global array of all export formats which Assimp supports in its current build
 Exporter::ExportFormatEntry gExporters[] =
 {
 #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
-    Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada),
+    Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_X_EXPORTER
     Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile,
-        aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs),
+        aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
-    Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0),
+    Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
     Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
-        aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */),
+        aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
+    Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
+        aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_STL_EXPORTER
@@ -137,28 +142,34 @@ Exporter::ExportFormatEntry gExporters[] =
 
 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
     Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS,
-        aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices),
+        aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
     Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType),
+        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
     Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType),
+        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
     Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf2", &ExportSceneGLTF2,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType),
+        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
+    Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb2", &ExportSceneGLB2,
+        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
-    Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0),
+    Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0 ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
-    Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0),
+    Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0 ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
-	Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0),
+    Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
+    Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 )
 #endif
 };
 
@@ -241,7 +252,7 @@ bool Exporter::IsDefaultIOHandler() const {
 
 // ------------------------------------------------------------------------------------------------
 const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
-                                                unsigned int, const ExportProperties* pProperties ) {
+                                                unsigned int, const ExportProperties* /*pProperties*/ ) {
     if (pimpl->blob) {
         delete pimpl->blob;
         pimpl->blob = NULL;
@@ -412,7 +423,7 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
 
     pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId;
     ASSIMP_END_EXCEPTION_REGION(aiReturn);
-    
+
     return AI_FAILURE;
 }
 

+ 23 - 22
code/FBXBinaryTokenizer.cpp

@@ -129,29 +129,26 @@ AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offse
 
 
 // ------------------------------------------------------------------------------------------------
-uint32_t Offset(const char* begin, const char* cursor)
-{
+uint32_t Offset(const char* begin, const char* cursor) {
     ai_assert(begin <= cursor);
+
     return static_cast<unsigned int>(cursor - begin);
 }
 
-
 // ------------------------------------------------------------------------------------------------
-void TokenizeError(const std::string& message, const char* begin, const char* cursor)
-{
+void TokenizeError(const std::string& message, const char* begin, const char* cursor) {
     TokenizeError(message, Offset(begin, cursor));
 }
 
-
 // ------------------------------------------------------------------------------------------------
-uint32_t ReadWord(const char* input, const char*& cursor, const char* end)
-{
+uint32_t ReadWord(const char* input, const char*& cursor, const char* end) {
     const size_t k_to_read = sizeof( uint32_t );
     if(Offset(cursor, end) < k_to_read ) {
         TokenizeError("cannot ReadWord, out of bounds",input, cursor);
     }
 
-    uint32_t word = *reinterpret_cast<const uint32_t*>(cursor);
+    uint32_t word;
+    ::memcpy(&word, cursor, 4);
     AI_SWAP4(word);
 
     cursor += k_to_read;
@@ -166,7 +163,8 @@ uint64_t ReadDoubleWord(const char* input, const char*& cursor, const char* end)
         TokenizeError("cannot ReadDoubleWord, out of bounds",input, cursor);
     }
 
-    uint64_t dword = *reinterpret_cast<const uint64_t*>(cursor);
+    uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/;
+    ::memcpy( &dword, cursor, sizeof( uint64_t ) );
     AI_SWAP8(dword);
 
     cursor += k_to_read;
@@ -175,24 +173,21 @@ uint64_t ReadDoubleWord(const char* input, const char*& cursor, const char* end)
 }
 
 // ------------------------------------------------------------------------------------------------
-uint8_t ReadByte(const char* input, const char*& cursor, const char* end)
-{
+uint8_t ReadByte(const char* input, const char*& cursor, const char* end) {
     if(Offset(cursor, end) < sizeof( uint8_t ) ) {
         TokenizeError("cannot ReadByte, out of bounds",input, cursor);
     }
 
-    uint8_t word = *reinterpret_cast<const uint8_t*>(cursor);
+    uint8_t word;/* = *reinterpret_cast< const uint8_t* >( cursor )*/
+    ::memcpy( &word, cursor, sizeof( uint8_t ) );
     ++cursor;
 
     return word;
 }
 
-
 // ------------------------------------------------------------------------------------------------
-unsigned int ReadString(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end,
-    bool long_length = false,
-    bool allow_null = false)
-{
+unsigned int ReadString(const char*& sbegin_out, const char*& send_out, const char* input,
+        const char*& cursor, const char* end, bool long_length = false, bool allow_null = false) {
     const uint32_t len_len = long_length ? 4 : 1;
     if(Offset(cursor, end) < len_len) {
         TokenizeError("cannot ReadString, out of bounds reading length",input, cursor);
@@ -221,8 +216,7 @@ unsigned int ReadString(const char*& sbegin_out, const char*& send_out, const ch
 }
 
 // ------------------------------------------------------------------------------------------------
-void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end)
-{
+void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end) {
     if(Offset(cursor, end) < 1) {
         TokenizeError("cannot ReadData, out of bounds reading length",input, cursor);
     }
@@ -421,8 +415,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
     return true;
 }
 
-
-}
+} // anonymous namespace
 
 // ------------------------------------------------------------------------------------------------
 // TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
@@ -434,6 +427,14 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int le
         TokenizeError("file is too short",0);
     }
 
+    //uint32_t offset = 0x15;
+/*    const char* cursor = input + 0x15;
+
+    const uint32_t flags = ReadWord(input, cursor, input + length);
+
+    const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused
+    const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/
+
     if (strncmp(input,"Kaydara FBX Binary",18)) {
         TokenizeError("magic bytes not found",0);
     }

+ 38 - 38
code/FBXConverter.cpp

@@ -436,19 +436,6 @@ private:
 
     aiScene* const out;
     const FBX::Document& doc;
-
-	bool FindTextureIndexByFilename(const Video& video, unsigned int& index) {
-		index = 0;
-		const char* videoFileName = video.FileName().c_str();
-		for (auto texture = textures_converted.begin(); texture != textures_converted.end(); ++texture)
-		{
-			if (!strcmp(texture->first->FileName().c_str(), videoFileName)) {
-				return true;
-			}
-			index++;
-		}
-		return false;
-	}
 };
 
 Converter::Converter( aiScene* out, const Document& doc )
@@ -566,7 +553,6 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa
 
                 if ( !name_carrier ) {
                     nodes_chain.push_back( new aiNode( original_name ) );
-                    name_carrier = nodes_chain.back();
                 }
 
                 //setup metadata on newest node
@@ -645,7 +631,6 @@ void Converter::ConvertCameras( const Model& model )
     }
 }
 
-
 void Converter::ConvertLight( const Model& model, const Light& light )
 {
     lights.push_back( new aiLight() );
@@ -783,7 +768,6 @@ const char* Converter::NameTransformationComp( TransformationComp comp )
     return NULL;
 }
 
-
 const char* Converter::NameTransformationCompProperty( TransformationComp comp )
 {
     switch ( comp )
@@ -1780,6 +1764,8 @@ unsigned int Converter::ConvertVideo( const Video& video )
         memcpy( out_tex->achFormatHint, ext.c_str(), ext.size() );
     }
 
+    out_tex->mFilename.Set(video.FileName().c_str());
+
     return static_cast<unsigned int>( textures.size() - 1 );
 }
 
@@ -1814,15 +1800,19 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap&
 					textures_converted[media] = index;
 					textureReady = true;
 				}
-				else if (doc.Settings().searchEmbeddedTextures) { //try to find the texture on the already-loaded textures by the filename, if the flag is on					
-					textureReady = FindTextureIndexByFilename(*media, index);
-				}
 			}
 
 			// setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready
-			if (textureReady) {
-				path.data[0] = '*';
-				path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+			if (doc.Settings().useLegacyEmbeddedTextureNaming) {
+                if (textureReady) {
+                    // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
+                    // In FBX files textures are now stored internally by Assimp with their filename included
+                    // Now Assimp can lookup thru the loaded textures after all data is processed
+                    // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it
+                    // This may occur on this case too, it has to be studied
+                    path.data[0] = '*';
+                    path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+                }
 			}
 		}  
 
@@ -2239,9 +2229,17 @@ void Converter::ConvertAnimations()
     }
 }
 
+void Converter::RenameNode( const std::string& fixed_name, const std::string& new_name ) {
+    if ( node_names.find( fixed_name ) == node_names.end() ) {
+        FBXImporter::LogError( "Cannot rename node " + fixed_name + ", not existing.");
+        return;
+    }
+
+    if ( node_names.find( new_name ) != node_names.end() ) {
+        FBXImporter::LogError( "Cannot rename node " + fixed_name + " to " + new_name +", name already existing." );
+        return;
+    }
 
-void Converter::RenameNode( const std::string& fixed_name, const std::string& new_name )
-{
     ai_assert( node_names.find( fixed_name ) != node_names.end() );
     ai_assert( node_names.find( new_name ) == node_names.end() );
 
@@ -2429,6 +2427,7 @@ void Converter::ConvertAnimationStack( const AnimationStack& st )
     anim->mTicksPerSecond = anim_fps;
 }
 
+#ifdef ASSIMP_BUILD_DEBUG
 // ------------------------------------------------------------------------------------------------
 // sanity check whether the input is ok
 static void validateAnimCurveNodes( const std::vector<const AnimationCurveNode*>& curves,
@@ -2446,6 +2445,7 @@ static void validateAnimCurveNodes( const std::vector<const AnimationCurveNode*>
         }
     }
 }
+#endif // ASSIMP_BUILD_DEBUG
 
 // ------------------------------------------------------------------------------------------------
 void Converter::GenerateNodeAnimations( std::vector<aiNodeAnim*>& node_anims,
@@ -2737,10 +2737,10 @@ aiNodeAnim* Converter::GenerateRotationNodeAnim( const std::string& name,
     double& max_time,
     double& min_time )
 {
-    ScopeGuard<aiNodeAnim> na( new aiNodeAnim() );
+    std::unique_ptr<aiNodeAnim> na( new aiNodeAnim() );
     na->mNodeName.Set( name );
 
-    ConvertRotationKeys( na, curves, layer_map, start, stop, max_time, min_time, target.RotationOrder() );
+    ConvertRotationKeys( na.get(), curves, layer_map, start, stop, max_time, min_time, target.RotationOrder() );
 
     // dummy scaling key
     na->mScalingKeys = new aiVectorKey[ 1 ];
@@ -2756,7 +2756,7 @@ aiNodeAnim* Converter::GenerateRotationNodeAnim( const std::string& name,
     na->mPositionKeys[ 0 ].mTime = 0.;
     na->mPositionKeys[ 0 ].mValue = aiVector3D();
 
-    return na.dismiss();
+    return na.release();
 }
 
 aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name,
@@ -2767,10 +2767,10 @@ aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name,
     double& max_time,
     double& min_time )
 {
-    ScopeGuard<aiNodeAnim> na( new aiNodeAnim() );
+    std::unique_ptr<aiNodeAnim> na( new aiNodeAnim() );
     na->mNodeName.Set( name );
 
-    ConvertScaleKeys( na, curves, layer_map, start, stop, max_time, min_time );
+    ConvertScaleKeys( na.get(), curves, layer_map, start, stop, max_time, min_time );
 
     // dummy rotation key
     na->mRotationKeys = new aiQuatKey[ 1 ];
@@ -2786,7 +2786,7 @@ aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name,
     na->mPositionKeys[ 0 ].mTime = 0.;
     na->mPositionKeys[ 0 ].mValue = aiVector3D();
 
-    return na.dismiss();
+    return na.release();
 }
 
 
@@ -2799,10 +2799,10 @@ aiNodeAnim* Converter::GenerateTranslationNodeAnim( const std::string& name,
     double& min_time,
     bool inverse )
 {
-    ScopeGuard<aiNodeAnim> na( new aiNodeAnim() );
+    std::unique_ptr<aiNodeAnim> na( new aiNodeAnim() );
     na->mNodeName.Set( name );
 
-    ConvertTranslationKeys( na, curves, layer_map, start, stop, max_time, min_time );
+    ConvertTranslationKeys( na.get(), curves, layer_map, start, stop, max_time, min_time );
 
     if ( inverse ) {
         for ( unsigned int i = 0; i < na->mNumPositionKeys; ++i ) {
@@ -2824,7 +2824,7 @@ aiNodeAnim* Converter::GenerateTranslationNodeAnim( const std::string& name,
     na->mRotationKeys[ 0 ].mTime = 0.;
     na->mRotationKeys[ 0 ].mValue = aiQuaternion();
 
-    return na.dismiss();
+    return na.release();
 }
 
 aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
@@ -2838,7 +2838,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
     bool reverse_order )
 
 {
-    ScopeGuard<aiNodeAnim> na( new aiNodeAnim() );
+    std::unique_ptr<aiNodeAnim> na( new aiNodeAnim() );
     na->mNodeName.Set( name );
 
     const PropertyTable& props = target.Props();
@@ -2910,7 +2910,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
         // which requires all of rotation, scaling and translation
         // to be set.
         if ( chain[ TransformationComp_Scaling ] != iter_end ) {
-            ConvertScaleKeys( na, ( *chain[ TransformationComp_Scaling ] ).second,
+            ConvertScaleKeys( na.get(), ( *chain[ TransformationComp_Scaling ] ).second,
                 layer_map,
                 start, stop,
                 max_time,
@@ -2926,7 +2926,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
         }
 
         if ( chain[ TransformationComp_Rotation ] != iter_end ) {
-            ConvertRotationKeys( na, ( *chain[ TransformationComp_Rotation ] ).second,
+            ConvertRotationKeys( na.get(), ( *chain[ TransformationComp_Rotation ] ).second,
                 layer_map,
                 start, stop,
                 max_time,
@@ -2944,7 +2944,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
         }
 
         if ( chain[ TransformationComp_Translation ] != iter_end ) {
-            ConvertTranslationKeys( na, ( *chain[ TransformationComp_Translation ] ).second,
+            ConvertTranslationKeys( na.get(), ( *chain[ TransformationComp_Translation ] ).second,
                 layer_map,
                 start, stop,
                 max_time,
@@ -2960,7 +2960,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
         }
 
     }
-    return na.dismiss();
+    return na.release();
 }
 
 Converter::KeyFrameListList Converter::GetKeyframeList( const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop )

+ 2 - 2
code/FBXDocument.cpp

@@ -619,10 +619,10 @@ std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_
 }
 
 // ------------------------------------------------------------------------------------------------
-std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest, const char* classname) const
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const
 {
     const char* arr[] = {classname};
-    return GetConnectionsBySourceSequenced(dest, arr,1);
+    return GetConnectionsBySourceSequenced(src, arr,1);
 }
 
 // ------------------------------------------------------------------------------------------------

+ 4 - 4
code/FBXImportSettings.h

@@ -63,7 +63,7 @@ struct ImportSettings
         , readWeights(true)
         , preservePivots(true)
         , optimizeEmptyAnimationCurves(true)
-		, searchEmbeddedTextures(false)
+        , useLegacyEmbeddedTextureNaming(false)
     {}
 
 
@@ -139,9 +139,9 @@ struct ImportSettings
      *  The default value is true. */
     bool optimizeEmptyAnimationCurves;
 
-	/** search for embedded loaded textures, where no embedded texture data is provided.
-	*  The default value is false. */
-	bool searchEmbeddedTextures;
+    /** use legacy naming for embedded textures eg: (*0, *1, *2)
+    **/
+    bool useLegacyEmbeddedTextureNaming;
 };
 
 

+ 1 - 1
code/FBXImporter.cpp

@@ -135,7 +135,7 @@ void FBXImporter::SetupProperties(const Importer* pImp)
     settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
     settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
     settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
-	settings.searchEmbeddedTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES, false);
+    settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
 }
 
 // ------------------------------------------------------------------------------------------------

+ 28 - 28
code/FBXMaterial.cpp

@@ -291,40 +291,40 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
 
     if(FileName) {
         fileName = ParseTokenAsString(GetRequiredToken(*FileName,0));
-	}
+    }
 
     if(RelativeFilename) {
         relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0));
     }
 
     if(Content) {
-		//this field is ommited when the embedded texture is already loaded, let's ignore if it´s not found
-		try {
-			const Token& token = GetRequiredToken(*Content, 0);
-			const char* data = token.begin();
-			if (!token.IsBinary()) {
-				DOMWarning("video content is not binary data, ignoring", &element);
-			}
-			else if (static_cast<size_t>(token.end() - data) < 5) {
-				DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element);
-			}
-			else if (*data != 'R') {
-				DOMWarning("video content is not raw binary data, ignoring", &element);
-			}
-			else {
-				// read number of elements
-				uint32_t len = 0;
-				::memcpy(&len, data + 1, sizeof(len));
-				AI_SWAP4(len);
-
-				contentLength = len;
-
-				content = new uint8_t[len];
-				::memcpy(content, data + 5, len);
-			}
-		} catch (runtime_error runtimeError) {
-			//we don´t need the content data for contents that has already been loaded
-		}
+        //this field is ommited when the embedded texture is already loaded, let's ignore if it's not found
+        try {
+            const Token& token = GetRequiredToken(*Content, 0);
+            const char* data = token.begin();
+            if (!token.IsBinary()) {
+                DOMWarning("video content is not binary data, ignoring", &element);
+            }
+            else if (static_cast<size_t>(token.end() - data) < 5) {
+                DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element);
+            }
+            else if (*data != 'R') {
+                DOMWarning("video content is not raw binary data, ignoring", &element);
+            }
+            else {
+                // read number of elements
+                uint32_t len = 0;
+                ::memcpy(&len, data + 1, sizeof(len));
+                AI_SWAP4(len);
+
+                contentLength = len;
+
+                content = new uint8_t[len];
+                ::memcpy(content, data + 5, len);
+            }
+        } catch (runtime_error runtimeError) {
+            //we don't need the content data for contents that has already been loaded
+        }
     }
 
     props = GetPropertyTable(doc,"Video.FbxVideo",element,sc);

+ 8 - 2
code/FBXMeshGeometry.cpp

@@ -433,7 +433,11 @@ void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
     // deal with this more elegantly and with less redundancy, but right
     // now it seems unavoidable.
     if (MappingInformationType == "ByVertice" && ReferenceInformationType == "Direct") {
-		std::vector<T> tempData;
+        if ( !HasElement( source, indexDataElementName ) ) {
+            return;
+        }
+
+        std::vector<T> tempData;
 		ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
 
         data_out.resize(vertex_count);
@@ -450,10 +454,12 @@ void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
 		ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
 
         data_out.resize(vertex_count);
+        if ( !HasElement( source, indexDataElementName ) ) {
+            return;
+        }
 
         std::vector<int> uvIndices;
         ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
-
         for (size_t i = 0, e = uvIndices.size(); i < e; ++i) {
 
             const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];

+ 9 - 0
code/FBXParser.cpp

@@ -103,6 +103,7 @@ namespace {
     T SafeParse(const char* data, const char* end) {
         // Actual size validation happens during Tokenization so
         // this is valid as an assertion.
+        (void)(end);
         ai_assert(static_cast<size_t>(end - data) >= sizeof(T));
         T result = static_cast<T>(0);
         ::memcpy(&result, data, sizeof(T));
@@ -1196,6 +1197,14 @@ std::string ParseTokenAsString(const Token& t)
     return i;
 }
 
+bool HasElement( const Scope& sc, const std::string& index ) {
+    const Element* el = sc[ index ];
+    if ( nullptr == el ) {
+        return false;
+    }
+
+    return true;
+}
 
 // ------------------------------------------------------------------------------------------------
 // extract a required element from a scope, abort if the element cannot be found

+ 2 - 0
code/FBXParser.h

@@ -218,6 +218,8 @@ void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el);
 void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& e);
 void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el);
 
+bool HasElement( const Scope& sc, const std::string& index );
+
 // extract a required element from a scope, abort if the element cannot be found
 const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL);
 

+ 26 - 17
code/FIReader.cpp

@@ -45,19 +45,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
 
+#include "FIReader.hpp"
+#include "StringUtils.h"
+
 // Workaround for issue #1361
 // https://github.com/assimp/assimp/issues/1361
 #ifdef __ANDROID__
-#define _GLIBCXX_USE_C99 1
+#  define _GLIBCXX_USE_C99 1
 #endif
 
-#include "FIReader.hpp"
 #include "Exceptional.h"
 #include <assimp/IOStream.hpp>
 #include <assimp/types.h>
 #include "MemoryIOWrapper.h"
 #include "irrXMLWrapper.h"
 #include "../contrib/utf8cpp/source/utf8.h"
+#include "fast_atof.h"
 #include <stack>
 #include <map>
 #include <iostream>
@@ -485,7 +488,9 @@ struct FIFloatDecoder: public FIDecoder {
         value.reserve(numFloats);
         for (size_t i = 0; i < numFloats; ++i) {
             int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
-            value.push_back(*(float*)&v);
+            float f;
+            memcpy(&f, &v, 4);
+            value.push_back(f);
             data += 4;
         }
         return FIFloatValue::create(std::move(value));
@@ -503,7 +508,9 @@ struct FIDoubleDecoder: public FIDecoder {
         for (size_t i = 0; i < numDoubles; ++i) {
             long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7];
             long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
-            value.push_back(*(double*)&v);
+            double f;
+            memcpy(&f, &v, 8);
+            value.push_back(f);
             data += 8;
         }
         return FIDoubleValue::create(std::move(value));
@@ -685,7 +692,7 @@ public:
         if (intValue) {
             return intValue->value.size() == 1 ? intValue->value.front() : 0;
         }
-        return stoi(attr->value->toString());
+        return atoi(attr->value->toString().c_str());
     }
 
     virtual int getAttributeValueAsInt(int idx) const /*override*/ {
@@ -696,7 +703,7 @@ public:
         if (intValue) {
             return intValue->value.size() == 1 ? intValue->value.front() : 0;
         }
-        return stoi(attributes[idx].value->toString());
+        return atoi(attributes[idx].value->toString().c_str());
     }
 
     virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
@@ -708,7 +715,8 @@ public:
         if (floatValue) {
             return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
         }
-        return stof(attr->value->toString());
+
+        return fast_atof(attr->value->toString().c_str());
     }
 
     virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
@@ -719,7 +727,7 @@ public:
         if (floatValue) {
             return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
         }
-        return stof(attributes[idx].value->toString());
+        return fast_atof(attributes[idx].value->toString().c_str());
     }
 
     virtual const char* getNodeName() const /*override*/ {
@@ -984,13 +992,13 @@ private:
         if (index < 32) {
             FIDecoder *decoder = defaultDecoder[index];
             if (!decoder) {
-                throw DeadlyImportError("Invalid encoding algorithm index " + std::to_string(index));
+                throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
             }
             return decoder->decode(dataP, len);
         }
         else {
             if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) {
-                throw DeadlyImportError("Invalid encoding algorithm index " + std::to_string(index));
+                throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
             }
             std::string uri = vocabulary.encodingAlgorithmTable[index - 32];
             auto it = decoderMap.find(uri);
@@ -1014,12 +1022,12 @@ private:
                 alphabet = "0123456789-:TZ ";
                 break;
             default:
-                throw DeadlyImportError("Invalid restricted alphabet index " + std::to_string(index));
+                throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
             }
         }
         else {
             if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) {
-                throw DeadlyImportError("Invalid restricted alphabet index " + std::to_string(index));
+                throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
             }
             alphabet = vocabulary.restrictedAlphabetTable[index - 16];
         }
@@ -1027,7 +1035,7 @@ private:
         utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32));
         std::string::size_type alphabetLength = alphabetUTF32.size();
         if (alphabetLength < 2) {
-            throw DeadlyImportError("Invalid restricted alphabet length " + std::to_string(alphabetLength));
+            throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength));
         }
         std::string::size_type bitsPerCharacter = 1;
         while ((1ull << bitsPerCharacter) <= alphabetLength) {
@@ -1776,17 +1784,18 @@ public:
         return reader->getParserFormat();
     }
 
-    virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const /*override*/ {
+    virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int /*idx*/) const /*override*/ {
         return nullptr;
     }
 
-    virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* name) const /*override*/ {
+    virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* /*name*/) const /*override*/ {
         return nullptr;
     }
 
-    virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) /*override*/ {}
+    virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr<FIDecoder> /*decoder*/) /*override*/ {}
+
 
-    virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ {}
+    virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {}
 
 private:
 

+ 19 - 4
code/FIReader.hpp

@@ -46,16 +46,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef INCLUDED_AI_FI_READER_H
 #define INCLUDED_AI_FI_READER_H
 
-#include <irrXML.h>
-#include <memory>
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+//#include <wchar.h>
 #include <string>
+#include <memory>
+#include <cerrno>
+#include <cwchar>
 #include <vector>
-#include <cstdint>
+//#include <stdio.h>
+//#include <cstdint>
+#include <irrXML.h>
 
 namespace Assimp {
 
 struct FIValue {
     virtual const std::string &toString() const = 0;
+    virtual ~FIValue() {}
 };
 
 struct FIStringValue: public FIValue {
@@ -115,6 +122,7 @@ struct FICDATAValue: public FIStringValue {
 
 struct FIDecoder {
     virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) = 0;
+    virtual ~FIDecoder() {}
 };
 
 struct FIQName {
@@ -154,7 +162,7 @@ class IOStream;
 
 class FIReader: public irr::io::IIrrXMLReader<char, irr::io::IXMLBase> {
 public:
-
+	virtual ~FIReader();
     virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const = 0;
 
     virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char *name) const = 0;
@@ -167,6 +175,13 @@ public:
 
 };// class IFIReader
 
+inline
+FIReader::~FIReader() {
+	// empty
+}
+
 }// namespace Assimp
 
+#endif // #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
 #endif // INCLUDED_AI_FI_READER_H

+ 76 - 36
code/FindDegenerates.cpp

@@ -56,98 +56,138 @@ using namespace Assimp;
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 FindDegeneratesProcess::FindDegeneratesProcess()
-: configRemoveDegenerates (false)
-{}
+: mConfigRemoveDegenerates( false )
+, mConfigCheckAreaOfTriangle( false ){
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-FindDegeneratesProcess::~FindDegeneratesProcess()
-{
+FindDegeneratesProcess::~FindDegeneratesProcess() {
     // nothing to do here
 }
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const
-{
+bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
     return 0 != (pFlags & aiProcess_FindDegenerates);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup import configuration
-void FindDegeneratesProcess::SetupProperties(const Importer* pImp)
-{
+void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
     // Get the current value of AI_CONFIG_PP_FD_REMOVE
-    configRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
+    mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
+    mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-void FindDegeneratesProcess::Execute( aiScene* pScene)
-{
+void FindDegeneratesProcess::Execute( aiScene* pScene) {
     DefaultLogger::get()->debug("FindDegeneratesProcess begin");
     for (unsigned int i = 0; i < pScene->mNumMeshes;++i){
-        ExecuteOnMesh( pScene->mMeshes[i]);
+        ExecuteOnMesh( pScene->mMeshes[ i ] );
     }
     DefaultLogger::get()->debug("FindDegeneratesProcess finished");
 }
 
+static ai_real heron( ai_real a, ai_real b, ai_real c ) {
+    ai_real s = (a + b + c) / 2;
+    ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
+    return area;
+}
+
+static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) {
+    const ai_real lx = ( vB.x - vA.x );
+    const ai_real ly = ( vB.y - vA.y );
+    const ai_real lz = ( vB.z - vA.z );
+    ai_real a = lx*lx + ly*ly + lz*lz;
+    ai_real d = pow( a, (ai_real)0.5 );
+
+    return d;
+}
+
+static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
+    ai_real area = 0;
+
+    aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
+    aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
+    aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
+
+    ai_real a( distance3D( vA, vB ) );
+    ai_real b( distance3D( vB, vC ) );
+    ai_real c( distance3D( vC, vA ) );
+    area = heron( a, b, c );
+
+    return area;
+}
+
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported mesh
-void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh)
-{
+void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
     mesh->mPrimitiveTypes = 0;
 
     std::vector<bool> remove_me;
-    if (configRemoveDegenerates)
-        remove_me.resize(mesh->mNumFaces,false);
+    if (mConfigRemoveDegenerates) {
+        remove_me.resize( mesh->mNumFaces, false );
+    }
 
     unsigned int deg = 0, limit;
-    for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
-    {
+    for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
         aiFace& face = mesh->mFaces[a];
         bool first = true;
 
         // check whether the face contains degenerated entries
-        for (unsigned int i = 0; i < face.mNumIndices; ++i)
-        {
+        for (unsigned int i = 0; i < face.mNumIndices; ++i) {
             // Polygons with more than 4 points are allowed to have double points, that is
             // simulating polygons with holes just with concave polygons. However,
             // double points may not come directly after another.
             limit = face.mNumIndices;
-            if (face.mNumIndices > 4)
-                limit = std::min(limit,i+2);
+            if (face.mNumIndices > 4) {
+                limit = std::min( limit, i+2 );
+            }
 
-            for (unsigned int t = i+1; t < limit; ++t)
-            {
-                if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]])
-                {
+            for (unsigned int t = i+1; t < limit; ++t) {
+                if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
                     // we have found a matching vertex position
                     // remove the corresponding index from the array
-                    --face.mNumIndices;--limit;
-                    for (unsigned int m = t; m < face.mNumIndices; ++m)
-                    {
-                        face.mIndices[m] = face.mIndices[m+1];
+                    --face.mNumIndices;
+                    --limit;
+                    for (unsigned int m = t; m < face.mNumIndices; ++m) {
+                        face.mIndices[ m ] = face.mIndices[ m+1 ];
                     }
                     --t;
 
                     // NOTE: we set the removed vertex index to an unique value
                     // to make sure the developer gets notified when his
                     // application attemps to access this data.
-                    face.mIndices[face.mNumIndices] = 0xdeadbeef;
+                    face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
 
-                    if(first)
-                    {
+                    if(first) {
                         ++deg;
                         first = false;
                     }
 
-                    if (configRemoveDegenerates) {
-                        remove_me[a] = true;
+                    if ( mConfigRemoveDegenerates ) {
+                        remove_me[ a ] = true;
                         goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
                     }
                 }
             }
+
+            if ( mConfigCheckAreaOfTriangle ) {
+                if ( face.mNumIndices == 3 ) {
+                    ai_real area = calculateAreaOfTriangle( face, mesh );
+                    if ( area < 1e-6 ) {
+                        if ( mConfigRemoveDegenerates ) {
+                            remove_me[ a ] = true;
+                            goto evil_jump_outside;
+                        }
+
+                        // todo: check for index which is corrupt.
+                    }
+                }
+            }
         }
 
         // We need to update the primitive flags array of the mesh.
@@ -171,7 +211,7 @@ evil_jump_outside:
     }
 
     // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
-    if (configRemoveDegenerates && deg) {
+    if (mConfigRemoveDegenerates && deg) {
         unsigned int n = 0;
         for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
         {

+ 40 - 19
code/FindDegenerates.h

@@ -54,15 +54,11 @@ namespace Assimp    {
 // ---------------------------------------------------------------------------
 /** FindDegeneratesProcess: Searches a mesh for degenerated triangles.
 */
-class ASSIMP_API FindDegeneratesProcess : public BaseProcess
-{
+class ASSIMP_API FindDegeneratesProcess : public BaseProcess {
 public:
-
     FindDegeneratesProcess();
     ~FindDegeneratesProcess();
 
-public:
-
     // -------------------------------------------------------------------
     // Check whether step is active
     bool IsActive( unsigned int pFlags) const;
@@ -79,28 +75,53 @@ public:
     // Execute step on a given mesh
     void ExecuteOnMesh( aiMesh* mesh);
 
+    // -------------------------------------------------------------------
+    /// @brief Enable the instant removal of degenerated primitives
+    /// @param enabled  true for enabled.
+    void EnableInstantRemoval(bool enabled);
+
+    // -------------------------------------------------------------------
+    /// @brief Check whether instant removal is currently enabled
+    /// @return The instant removal state.
+    bool IsInstantRemoval() const;
 
     // -------------------------------------------------------------------
-    /** @brief Enable the instant removal of degenerated primitives
-     *  @param d hm ... difficult to guess what this means, hu!?
-     */
-    void EnableInstantRemoval(bool d) {
-        configRemoveDegenerates = d;
-    }
+    /// @brief Enable the area check for triangles.
+    /// @param enabled  true for enabled.
+    void EnableAreaCheck( bool enabled );
 
     // -------------------------------------------------------------------
-    /** @brief Check whether instant removal is currently enabled
-     *  @return ...
-     */
-    bool IsInstantRemoval() const {
-        return configRemoveDegenerates;
-    }
+    /// @brief Check whether the area check is enabled.
+    /// @return The area check state.
+    bool isAreaCheckEnabled() const;
 
 private:
-
     //! Configuration option: remove degenerates faces immediately
-    bool configRemoveDegenerates;
+    bool mConfigRemoveDegenerates;
+    //! Configuration option: check for area
+    bool mConfigCheckAreaOfTriangle;
 };
+
+inline
+void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) {
+    mConfigRemoveDegenerates = enabled;
 }
 
+inline
+bool FindDegeneratesProcess::IsInstantRemoval() const {
+    return mConfigRemoveDegenerates;
+}
+
+inline
+void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) {
+    mConfigCheckAreaOfTriangle = enabled;
+}
+
+inline
+bool FindDegeneratesProcess::isAreaCheckEnabled() const {
+    return mConfigCheckAreaOfTriangle;
+}
+
+} // Namespace Assimp
+
 #endif // !! AI_FINDDEGENERATESPROCESS_H_INC

+ 15 - 10
code/FindInvalidDataProcess.cpp

@@ -169,8 +169,8 @@ void FindInvalidDataProcess::Execute( aiScene* pScene)
 
 // ------------------------------------------------------------------------------------------------
 template <typename T>
-inline const char* ValidateArrayContents(const T* arr, unsigned int size,
-    const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true)
+inline const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/,
+    const std::vector<bool>& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/)
 {
     return NULL;
 }
@@ -339,32 +339,37 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim)
 int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh)
 {
     bool ret = false;
-    std::vector<bool> dirtyMask(pMesh->mNumVertices,(pMesh->mNumFaces ? true : false));
+    std::vector<bool> dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0);
 
     // Ignore elements that are not referenced by vertices.
     // (they are, for example, caused by the FindDegenerates step)
-    for (unsigned int m = 0; m < pMesh->mNumFaces;++m)  {
+    for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) {
         const aiFace& f = pMesh->mFaces[m];
 
-        for (unsigned int i = 0; i < f.mNumIndices;++i) {
+        for (unsigned int i = 0; i < f.mNumIndices; ++i) {
             dirtyMask[f.mIndices[i]] = false;
         }
     }
 
     // Process vertex positions
-    if(pMesh->mVertices && ProcessArray(pMesh->mVertices,pMesh->mNumVertices,"positions",dirtyMask))    {
+    if (pMesh->mVertices && ProcessArray(pMesh->mVertices, pMesh->mNumVertices, "positions", dirtyMask)) {
         DefaultLogger::get()->error("Deleting mesh: Unable to continue without vertex positions");
+
         return 2;
     }
 
     // process texture coordinates
-    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i];++i)    {
-        if (ProcessArray(pMesh->mTextureCoords[i],pMesh->mNumVertices,"uvcoords",dirtyMask))    {
+    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i]; ++i) {
+        if (ProcessArray(pMesh->mTextureCoords[i], pMesh->mNumVertices, "uvcoords", dirtyMask)) {
+            pMesh->mNumUVComponents[i] = 0;
 
             // delete all subsequent texture coordinate sets.
-            for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a)  {
-                delete[] pMesh->mTextureCoords[a]; pMesh->mTextureCoords[a] = NULL;
+            for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+                delete[] pMesh->mTextureCoords[a];
+                pMesh->mTextureCoords[a] = NULL;
+                pMesh->mNumUVComponents[a] = 0;
             }
+
             ret = true;
         }
     }

+ 1 - 4
code/FixNormalsStep.h

@@ -56,14 +56,11 @@ namespace Assimp
  * vectors of an object are facing inwards. In this case they will be
  * flipped.
  */
-class FixInfacingNormalsProcess : public BaseProcess
-{
+class FixInfacingNormalsProcess : public BaseProcess {
 public:
-
     FixInfacingNormalsProcess();
     ~FixInfacingNormalsProcess();
 
-public:
     // -------------------------------------------------------------------
     /** Returns whether the processing step is present in the given flag field.
      * @param pFlags The processing flags the importer was called with. A bitwise

+ 1 - 1
code/IFCBoolean.cpp

@@ -272,7 +272,6 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const
         const IfcVector3& b0 = boundary[i];
         const IfcVector3& b1 = boundary[(i + 1) % bcount];
         IfcVector3 b = b1 - b0;
-        IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
 
         // segment-segment intersection
         // solve b0 + b*s = e0 + e*t for (s,t)
@@ -281,6 +280,7 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const
             // no solutions (parallel lines)
             continue;
         }
+        IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
 
         const IfcFloat x = b0.x - e0.x;
         const IfcFloat y = b0.y - e0.y;

+ 51 - 100
code/IFCCurve.cpp

@@ -43,28 +43,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Read profile and curves entities from IFC files
  */
 
-
-
 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
 #include "IFCUtil.h"
 
 namespace Assimp {
-    namespace IFC {
-        namespace {
+namespace IFC {
+namespace {
 
 
 // --------------------------------------------------------------------------------
 // Conic is the base class for Circle and Ellipse
 // --------------------------------------------------------------------------------
-class Conic : public Curve
-{
-
+class Conic : public Curve {
 public:
-
     // --------------------------------------------------
     Conic(const IfcConic& entity, ConversionData& conv)
-        : Curve(entity,conv)
-    {
+    : Curve(entity,conv) {
         IfcMatrix4 trafo;
         ConvertAxisPlacement(trafo,*entity.Position,conv);
 
@@ -75,8 +69,6 @@ public:
         p[2] = IfcVector3(trafo.a3,trafo.b3,trafo.c3);
     }
 
-public:
-
     // --------------------------------------------------
     bool IsClosed() const {
         return true;
@@ -84,7 +76,8 @@ public:
 
     // --------------------------------------------------
     size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
-        ai_assert(InRange(a) && InRange(b));
+        ai_assert( InRange( a ) );
+        ai_assert( InRange( b ) );
 
         a *= conv.angle_scale;
         b *= conv.angle_scale;
@@ -104,15 +97,11 @@ protected:
     IfcVector3 location, p[3];
 };
 
-
 // --------------------------------------------------------------------------------
 // Circle
 // --------------------------------------------------------------------------------
-class Circle : public Conic
-{
-
+class Circle : public Conic {
 public:
-
     // --------------------------------------------------
     Circle(const IfcCircle& entity, ConversionData& conv)
         : Conic(entity,conv)
@@ -120,8 +109,6 @@ public:
     {
     }
 
-public:
-
     // --------------------------------------------------
     IfcVector3 Eval(IfcFloat u) const {
         u = -conv.angle_scale * u;
@@ -137,20 +124,15 @@ private:
 // --------------------------------------------------------------------------------
 // Ellipse
 // --------------------------------------------------------------------------------
-class Ellipse : public Conic
-{
-
+class Ellipse : public Conic {
 public:
-
     // --------------------------------------------------
     Ellipse(const IfcEllipse& entity, ConversionData& conv)
-        : Conic(entity,conv)
-        , entity(entity)
-    {
+    : Conic(entity,conv)
+    , entity(entity) {
+        // empty
     }
 
-public:
-
     // --------------------------------------------------
     IfcVector3 Eval(IfcFloat u) const {
         u = -conv.angle_scale * u;
@@ -162,25 +144,18 @@ private:
     const IfcEllipse& entity;
 };
 
-
 // --------------------------------------------------------------------------------
 // Line
 // --------------------------------------------------------------------------------
-class Line : public Curve
-{
-
+class Line : public Curve {
 public:
-
     // --------------------------------------------------
     Line(const IfcLine& entity, ConversionData& conv)
-        : Curve(entity,conv)
-    {
+    : Curve(entity,conv) {
         ConvertCartesianPoint(p,entity.Pnt);
         ConvertVector(v,entity.Dir);
     }
 
-public:
-
     // --------------------------------------------------
     bool IsClosed() const {
         return false;
@@ -193,16 +168,17 @@ public:
 
     // --------------------------------------------------
     size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
-        ai_assert(InRange(a) && InRange(b));
+        ai_assert( InRange( a ) );
+        ai_assert( InRange( b ) );
         // two points are always sufficient for a line segment
         return a==b ? 1 : 2;
     }
 
 
     // --------------------------------------------------
-    void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
-    {
-        ai_assert(InRange(a) && InRange(b));
+    void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
+        ai_assert( InRange( a ) );
+        ai_assert( InRange( b ) );
 
         if (a == b) {
             out.verts.push_back(Eval(a));
@@ -227,18 +203,14 @@ private:
 // --------------------------------------------------------------------------------
 // CompositeCurve joins multiple smaller, bounded curves
 // --------------------------------------------------------------------------------
-class CompositeCurve : public BoundedCurve
-{
-
+class CompositeCurve : public BoundedCurve {
     typedef std::pair< std::shared_ptr< BoundedCurve >, bool > CurveEntry;
 
 public:
-
     // --------------------------------------------------
     CompositeCurve(const IfcCompositeCurve& entity, ConversionData& conv)
-        : BoundedCurve(entity,conv)
-        , total()
-    {
+    : BoundedCurve(entity,conv)
+    , total() {
         curves.reserve(entity.Segments.size());
         for(const IfcCompositeCurveSegment& curveSegment :entity.Segments) {
             // according to the specification, this must be a bounded curve
@@ -263,8 +235,6 @@ public:
         }
     }
 
-public:
-
     // --------------------------------------------------
     IfcVector3 Eval(IfcFloat u) const {
         if (curves.empty()) {
@@ -287,7 +257,8 @@ public:
 
     // --------------------------------------------------
     size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
-        ai_assert(InRange(a) && InRange(b));
+        ai_assert( InRange( a ) );
+        ai_assert( InRange( b ) );
         size_t cnt = 0;
 
         IfcFloat acc = 0;
@@ -306,9 +277,9 @@ public:
     }
 
     // --------------------------------------------------
-    void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
-    {
-        ai_assert(InRange(a) && InRange(b));
+    void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
+        ai_assert( InRange( a ) );
+        ai_assert( InRange( b ) );
 
         const size_t cnt = EstimateSampleCount(a,b);
         out.verts.reserve(out.verts.size() + cnt);
@@ -330,19 +301,14 @@ public:
 
 private:
     std::vector< CurveEntry > curves;
-
     IfcFloat total;
 };
 
-
 // --------------------------------------------------------------------------------
 // TrimmedCurve can be used to trim an unbounded curve to a bounded range
 // --------------------------------------------------------------------------------
-class TrimmedCurve : public BoundedCurve
-{
-
+class TrimmedCurve : public BoundedCurve {
 public:
-
     // --------------------------------------------------
     TrimmedCurve(const IfcTrimmedCurve& entity, ConversionData& conv)
         : BoundedCurve(entity,conv)
@@ -409,8 +375,6 @@ public:
         ai_assert(maxval >= 0);
     }
 
-public:
-
     // --------------------------------------------------
     IfcVector3 Eval(IfcFloat p) const {
         ai_assert(InRange(p));
@@ -419,7 +383,8 @@ public:
 
     // --------------------------------------------------
     size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
-        ai_assert(InRange(a) && InRange(b));
+        ai_assert( InRange( a ) );
+        ai_assert( InRange( b ) );
         return base->EstimateSampleCount(TrimParam(a),TrimParam(b));
     }
 
@@ -435,13 +400,11 @@ public:
     }
 
 private:
-
     // --------------------------------------------------
     IfcFloat TrimParam(IfcFloat f) const {
         return agree_sense ? f + range.first :  range.second - f;
     }
 
-
 private:
     ParamRange range;
     IfcFloat maxval;
@@ -454,11 +417,8 @@ private:
 // --------------------------------------------------------------------------------
 // PolyLine is a 'curve' defined by linear interpolation over a set of discrete points
 // --------------------------------------------------------------------------------
-class PolyLine : public BoundedCurve
-{
-
+class PolyLine : public BoundedCurve {
 public:
-
     // --------------------------------------------------
     PolyLine(const IfcPolyline& entity, ConversionData& conv)
         : BoundedCurve(entity,conv)
@@ -472,8 +432,6 @@ public:
         }
     }
 
-public:
-
     // --------------------------------------------------
     IfcVector3 Eval(IfcFloat p) const {
         ai_assert(InRange(p));
@@ -502,13 +460,10 @@ private:
     std::vector<IfcVector3> points;
 };
 
-
 } // anon
 
-
 // ------------------------------------------------------------------------------------------------
-Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv)
-{
+Curve* Curve::Convert(const IFC::IfcCurve& curve,ConversionData& conv) {
     if(curve.ToPtr<IfcBoundedCurve>()) {
         if(const IfcPolyline* c = curve.ToPtr<IfcPolyline>()) {
             return new PolyLine(*c,conv);
@@ -519,9 +474,6 @@ Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv)
         if(const IfcCompositeCurve* c = curve.ToPtr<IfcCompositeCurve>()) {
             return new CompositeCurve(*c,conv);
         }
-        //if(const IfcBSplineCurve* c = curve.ToPtr<IfcBSplineCurve>()) {
-        //  return new BSplineCurve(*c,conv);
-        //}
     }
 
     if(curve.ToPtr<IfcConic>()) {
@@ -543,8 +495,7 @@ Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv)
 
 #ifdef ASSIMP_BUILD_DEBUG
 // ------------------------------------------------------------------------------------------------
-bool Curve :: InRange(IfcFloat u) const
-{
+bool Curve::InRange(IfcFloat u) const {
     const ParamRange range = GetParametricRange();
     if (IsClosed()) {
         return true;
@@ -555,24 +506,24 @@ bool Curve :: InRange(IfcFloat u) const
 #endif
 
 // ------------------------------------------------------------------------------------------------
-IfcFloat Curve :: GetParametricRangeDelta() const
-{
+IfcFloat Curve::GetParametricRangeDelta() const {
     const ParamRange& range = GetParametricRange();
     return std::abs(range.second - range.first);
 }
 
 // ------------------------------------------------------------------------------------------------
-size_t Curve :: EstimateSampleCount(IfcFloat a, IfcFloat b) const
-{
-    ai_assert(InRange(a) && InRange(b));
+size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const {
+    (void)(a); (void)(b);  
+    ai_assert( InRange( a ) );
+    ai_assert( InRange( b ) );
 
     // arbitrary default value, deriving classes should supply better suited values
     return 16;
 }
 
 // ------------------------------------------------------------------------------------------------
-IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, IfcFloat b, unsigned int samples, IfcFloat threshold, unsigned int recurse = 0, unsigned int max_recurse = 15)
-{
+IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, IfcFloat b,
+        unsigned int samples, IfcFloat threshold, unsigned int recurse = 0, unsigned int max_recurse = 15) {
     ai_assert(samples>1);
 
     const IfcFloat delta = (b-a)/samples, inf = std::numeric_limits<IfcFloat>::infinity();
@@ -594,7 +545,8 @@ IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, Ifc
         }
     }
 
-    ai_assert(min_diff[0] != inf && min_diff[1] != inf);
+    ai_assert( min_diff[ 0 ] != inf );
+    ai_assert( min_diff[ 1 ] != inf );
     if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) {
         return min_point[0];
     }
@@ -615,15 +567,15 @@ IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, Ifc
 }
 
 // ------------------------------------------------------------------------------------------------
-bool Curve :: ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const
+bool Curve::ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const
 {
     // note: the following algorithm is not guaranteed to find the 'right' parameter value
     // in all possible cases, but it will always return at least some value so this function
     // will never fail in the default implementation.
 
     // XXX derive threshold from curve topology
-    const IfcFloat threshold = 1e-4f;
-    const unsigned int samples = 16;
+    static const IfcFloat threshold = 1e-4f;
+    static const unsigned int samples = 16;
 
     const ParamRange& range = GetParametricRange();
     paramOut = RecursiveSearch(this,val,range.first,range.second,samples,threshold);
@@ -632,9 +584,9 @@ bool Curve :: ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const
 }
 
 // ------------------------------------------------------------------------------------------------
-void Curve :: SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
-{
-    ai_assert(InRange(a) && InRange(b));
+void Curve::SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
+    ai_assert( InRange( a ) );
+    ai_assert( InRange( b ) );
 
     const size_t cnt = std::max(static_cast<size_t>(0),EstimateSampleCount(a,b));
     out.verts.reserve( out.verts.size() + cnt + 1);
@@ -646,16 +598,15 @@ void Curve :: SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
 }
 
 // ------------------------------------------------------------------------------------------------
-bool BoundedCurve :: IsClosed() const
-{
+bool BoundedCurve::IsClosed() const {
     return false;
 }
 
 // ------------------------------------------------------------------------------------------------
-void BoundedCurve :: SampleDiscrete(TempMesh& out) const
-{
+void BoundedCurve::SampleDiscrete(TempMesh& out) const {
     const ParamRange& range = GetParametricRange();
-    ai_assert(range.first != std::numeric_limits<IfcFloat>::infinity() && range.second != std::numeric_limits<IfcFloat>::infinity());
+    ai_assert( range.first != std::numeric_limits<IfcFloat>::infinity() );
+    ai_assert( range.second != std::numeric_limits<IfcFloat>::infinity() );
 
     return SampleDiscrete(out,range.first,range.second);
 }

+ 5 - 5
code/IFCGeometry.cpp

@@ -330,7 +330,11 @@ void ProcessSweptDiskSolid(const IfcSweptDiskSolid solid, TempMesh& result, Conv
     const unsigned int cnt_segments = conv.settings.cylindricalTessellation;
     const IfcFloat deltaAngle = AI_MATH_TWO_PI/cnt_segments;
 
-    const size_t samples = curve->EstimateSampleCount(solid.StartParam,solid.EndParam);
+	TempMesh temp;
+	curve->SampleDiscrete(temp, solid.StartParam, solid.EndParam);
+	const std::vector<IfcVector3>& curve_points = temp.verts;
+
+    const size_t samples = curve_points.size();
 
     result.verts.reserve(cnt_segments * samples * 4);
     result.vertcnt.reserve((cnt_segments - 1) * samples);
@@ -338,10 +342,6 @@ void ProcessSweptDiskSolid(const IfcSweptDiskSolid solid, TempMesh& result, Conv
     std::vector<IfcVector3> points;
     points.reserve(cnt_segments * samples);
 
-    TempMesh temp;
-    curve->SampleDiscrete(temp,solid.StartParam,solid.EndParam);
-    const std::vector<IfcVector3>& curve_points = temp.verts;
-
     if(curve_points.empty()) {
         IFCImporter::LogWarn("curve evaluation yielded no points (IfcSweptDiskSolid)");
         return;

+ 1 - 40
code/IFCOpenings.cpp

@@ -1499,7 +1499,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
 
 
     IfcVector3 wall_extrusion;
-    bool do_connections = false, first = true;
+    bool first = true;
 
     try {
 
@@ -1527,7 +1527,6 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
                 if (first) {
                     first = false;
                     if (dot > 0.f) {
-                        do_connections = true;
                         wall_extrusion = t.extrusionDir;
                         if (is_extruded_side) {
                             wall_extrusion = - wall_extrusion;
@@ -1607,44 +1606,6 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
     old_verts.swap(curmesh.verts);
     old_vertcnt.swap(curmesh.vertcnt);
 
-
-    // add connection geometry to close the adjacent 'holes' for the openings
-    // this should only be done from one side of the wall or the polygons
-    // would be emitted twice.
-    if (false && do_connections) {
-
-        std::vector<IfcVector3> tmpvec;
-        for(ClipperLib::Polygon& opening : holes_union) {
-
-            ai_assert(ClipperLib::Orientation(opening));
-
-            tmpvec.clear();
-
-            for(ClipperLib::IntPoint& point : opening) {
-
-                tmpvec.push_back( minv * IfcVector3(
-                    vmin.x + from_int64(point.X) * vmax.x,
-                    vmin.y + from_int64(point.Y) * vmax.y,
-                    coord));
-            }
-
-            for(size_t i = 0, size = tmpvec.size(); i < size; ++i) {
-                const size_t next = (i+1)%size;
-
-                curmesh.vertcnt.push_back(4);
-
-                const IfcVector3& in_world = tmpvec[i];
-                const IfcVector3& next_world = tmpvec[next];
-
-                // Assumptions: no 'partial' openings, wall thickness roughly the same across the wall
-                curmesh.verts.push_back(in_world);
-                curmesh.verts.push_back(in_world+wall_extrusion);
-                curmesh.verts.push_back(next_world+wall_extrusion);
-                curmesh.verts.push_back(next_world);
-            }
-        }
-    }
-
     std::vector< std::vector<p2t::Point*> > contours;
     for(ClipperLib::ExPolygon& clip : clipped) {
 

+ 4 - 3
code/IFCReaderGen1.cpp

@@ -1045,7 +1045,7 @@ void IFC::GetSchema(EXPRESS::ConversionSchema& out)
 namespace STEP {
 
 // -----------------------------------------------------------------------------------------------------------
-template <> size_t GenericFill<NotImplemented>(const STEP::DB& db, const LIST& params, NotImplemented* in)
+template <> size_t GenericFill<NotImplemented>(const STEP::DB& /*db*/, const LIST& /*params*/, NotImplemented* /*in*/)
 {
 	return 0;
 }
@@ -1253,7 +1253,7 @@ template <> size_t GenericFill<IfcPerformanceHistory>(const DB& db, const LIST&
 	return base;
 }
 // -----------------------------------------------------------------------------------------------------------
-template <> size_t GenericFill<IfcRepresentationItem>(const DB& db, const LIST& params, IfcRepresentationItem* in)
+template <> size_t GenericFill<IfcRepresentationItem>(const DB& /*db*/, const LIST& /*params*/, IfcRepresentationItem* /*in*/)
 {
 	size_t base = 0;
 	return base;
@@ -1715,7 +1715,7 @@ template <> size_t GenericFill<IfcPlateType>(const DB& db, const LIST& params, I
 	return base;
 }
 // -----------------------------------------------------------------------------------------------------------
-template <> size_t GenericFill<IfcObjectPlacement>(const DB& db, const LIST& params, IfcObjectPlacement* in)
+template <> size_t GenericFill<IfcObjectPlacement>(const DB& /*db*/, const LIST& /*params*/, IfcObjectPlacement* /*in*/)
 {
 	size_t base = 0;
 	return base;
@@ -2652,6 +2652,7 @@ template <> size_t GenericFill<IfcNamedUnit>(const DB& db, const LIST& params, I
 	size_t base = 0;
 	if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcNamedUnit"); }    do { // convert the 'Dimensions' argument
         std::shared_ptr<const DataType> arg = params[base++];
+		if (dynamic_cast<const UNSET*>(&*arg)) break;
         if (dynamic_cast<const ISDERIVED*>(&*arg)) { in->ObjectHelper<Assimp::IFC::IfcNamedUnit,2>::aux_is_derived[0]=true; break; }
         try { GenericConvert( in->Dimensions, arg, db ); break; } 
         catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); }

+ 1 - 1
code/IRRLoader.cpp

@@ -394,7 +394,7 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNode
                 angles[1] %= 360;
                 angles[2] %= 360;
 
-                if ((angles[0]*angles[1]) && (angles[1]*angles[2]))
+                if ( (angles[0]*angles[1]) != 0 && (angles[1]*angles[2]) != 0 )
                 {
                     FindSuitableMultiple(angles[0]);
                     FindSuitableMultiple(angles[1]);

+ 2 - 4
code/Importer.cpp

@@ -274,10 +274,6 @@ aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
 
     if (it != pimpl->mImporter.end())   {
         pimpl->mImporter.erase(it);
-
-        std::set<std::string> st;
-        pImp->GetExtensionList(st);
-
         DefaultLogger::get()->info("Unregistering custom importer: ");
         return AI_SUCCESS;
     }
@@ -681,6 +677,8 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
             profiler->EndRegion("import");
         }
 
+        SetPropertyString("sourceFilePath", pFile);
+
         // If successful, apply all active post processing steps to the imported data
         if( pimpl->mScene)  {
 

+ 1 - 0
code/ImproveCacheLocality.cpp

@@ -223,6 +223,7 @@ float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int mesh
             iMaxRefTris = std::max(iMaxRefTris,*piCur);
         }
     }
+    ai_assert(iMaxRefTris > 0);
     unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
     unsigned int iCacheMisses = 0;
 

+ 1 - 3
code/LWOAnimation.cpp

@@ -446,8 +446,6 @@ void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
 
     // Iterate through all three arrays at once - it's tricky, but
     // rather interesting to implement.
-    double lasttime = std::min(envl_x->keys[0].time,std::min(envl_y->keys[0].time,envl_z->keys[0].time));
-
     cur_x = envl_x->keys.begin();
     cur_y = envl_y->keys.begin();
     cur_z = envl_z->keys.begin();
@@ -503,7 +501,7 @@ void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
                 InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time);
             }
         }
-        lasttime = fill.mTime;
+        double lasttime = fill.mTime;
         out.push_back(fill);
 
         if (lasttime >= (*cur_x).time) {

+ 1 - 1
code/LWOMaterial.cpp

@@ -483,7 +483,7 @@ void LWOImporter::FindVCChannels(const LWO::Surface& surf, LWO::SortedRep& sorte
         const LWO::VColorChannel& vc = layer.mVColorChannels[i];
 
         if (surf.mVCMap == vc.name) {
-            // The vertex color map is explicitely requested by the surface so we need to take special care of it
+            // The vertex color map is explicitly requested by the surface so we need to take special care of it
             for (unsigned int a = 0; a < std::min(next,AI_MAX_NUMBER_OF_COLOR_SETS-1u); ++a) {
                 out[a+1] = out[a];
             }

+ 1 - 1
code/LWSLoader.cpp

@@ -471,7 +471,7 @@ void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector<Attachm
 // Determine the exact location of a LWO file
 std::string LWSImporter::FindLWOFile(const std::string& in)
 {
-    // insert missing directory seperator if necessary
+    // insert missing directory separator if necessary
     std::string tmp;
     if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/')
     {

+ 8 - 12
code/LineSplitter.h

@@ -69,27 +69,23 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
 
     std::cout << "Current line is: " << splitter.get_index() << std::endl;
 }
-@endcode */
+@endcode
+*/
 // ------------------------------------------------------------------------------------------------
-class LineSplitter
-{
+class LineSplitter {
 public:
-
     typedef size_t line_idx;
 
-public:
-
     // -----------------------------------------
     /** construct from existing stream reader
     note: trim is *always* assumed true if skyp_empty_lines==true
     */
     LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true)
-        : idx( 0 )
-        , stream(stream)
-        , swallow()
-        , skip_empty_lines(skip_empty_lines)
-        , trim(trim)
-    {
+    : idx( 0 )
+    , stream(stream)
+    , swallow()
+    , skip_empty_lines(skip_empty_lines)
+    , trim(trim) {
         cur.reserve(1024);
         operator++();
 

+ 4 - 4
code/LogAux.h

@@ -66,28 +66,28 @@ public:
     // ------------------------------------------------------------------------------------------------
     static void LogWarn(const Formatter::format& message)   {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->warn(Prefix() +(std::string)message);
+            DefaultLogger::get()->warn(Prefix()+(std::string)message);
         }
     }
 
     // ------------------------------------------------------------------------------------------------
     static void LogError(const Formatter::format& message)  {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->error(Prefix() +(std::string)message);
+            DefaultLogger::get()->error(Prefix()+(std::string)message);
         }
     }
 
     // ------------------------------------------------------------------------------------------------
     static void LogInfo(const Formatter::format& message)   {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->info(Prefix() +(std::string)message);
+            DefaultLogger::get()->info(Prefix()+(std::string)message);
         }
     }
 
     // ------------------------------------------------------------------------------------------------
     static void LogDebug(const Formatter::format& message)  {
         if (!DefaultLogger::isNullLogger()) {
-            DefaultLogger::get()->debug(Prefix() +(std::string)message);
+            DefaultLogger::get()->debug(Prefix()+(std::string)message);
         }
     }
 

+ 3 - 5
code/MD2Loader.cpp

@@ -274,11 +274,9 @@ void MD2Importer::InternReadFile( const std::string& pFile,
     aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
     pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
 
-    // navigate to the begin of the frame data
-    BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*)
-        m_pcHeader + m_pcHeader->offsetFrames);
-
-    pcFrame += configFrameID;
+    // navigate to the begin of the current frame data
+	BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*)
+		m_pcHeader + m_pcHeader->offsetFrames + (m_pcHeader->frameSize * configFrameID));
 
     // navigate to the begin of the triangle data
     MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*)

+ 3 - 3
code/MD3Loader.cpp

@@ -1018,11 +1018,11 @@ void MD3Importer::InternReadFile( const std::string& pFile,
 
                 // Convert the normal vector to uncompressed float3 format
                 aiVector3D& nor = pcMesh->mNormals[iCurrent];
-                LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,(ai_real*)&nor);
+                LatLngNormalToVec3(pcVertices[index].NORMAL,(ai_real*)&nor);
 
                 // Read texture coordinates
-                pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
-                pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
+                pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[index].U;
+                pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[index].V;
             }
             // Flip face order if necessary
             if (!shader || shader->cull == Q3Shader::CULL_CW) {

+ 1 - 1
code/MD5Loader.h

@@ -145,7 +145,7 @@ protected:
 
     // -------------------------------------------------------------------
     /** Load the contents of a specific file into memory and
-     *  alocates a buffer to keep it.
+     *  allocates a buffer to keep it.
      *
      *  mBuffer is modified to point to this buffer.
      *  @param pFile File stream to be read

+ 6 - 11
code/MDCLoader.cpp

@@ -283,9 +283,8 @@ void MDCImporter::InternReadFile(
         pcMesh->mNumVertices = pcMesh->mNumFaces * 3;
 
         // store the name of the surface for use as node name.
-        // FIX: make sure there is a 0 termination
-        const_cast<char&>(pcSurface->ucName[AI_MDC_MAXQPATH-1]) = '\0';
-        pcMesh->mTextureCoords[3] = (aiVector3D*)pcSurface->ucName;
+        pcMesh->mName.Set(std::string(pcSurface->ucName
+                                    , strnlen(pcSurface->ucName, AI_MDC_MAXQPATH - 1)));
 
         // go to the first shader in the file. ignore the others.
         if (pcSurface->ulNumShaders)
@@ -294,8 +293,8 @@ void MDCImporter::InternReadFile(
             pcMesh->mMaterialIndex = (unsigned int)aszShaders.size();
 
             // create a new shader
-            aszShaders.push_back(std::string( pcShader->ucName, std::min(
-                ::strlen(pcShader->ucName),sizeof(pcShader->ucName)) ));
+            aszShaders.push_back(std::string( pcShader->ucName, 
+                ::strnlen(pcShader->ucName, sizeof(pcShader->ucName)) ));
         }
         // need to create a default material
         else if (UINT_MAX == iDefaultMatIndex)
@@ -432,7 +431,7 @@ void MDCImporter::InternReadFile(
     else if (1 == pScene->mNumMeshes)
     {
         pScene->mRootNode = new aiNode();
-        pScene->mRootNode->mName.Set(std::string((const char*)pScene->mMeshes[0]->mTextureCoords[3]));
+        pScene->mRootNode->mName = pScene->mMeshes[0]->mName;
         pScene->mRootNode->mNumMeshes = 1;
         pScene->mRootNode->mMeshes = new unsigned int[1];
         pScene->mRootNode->mMeshes[0] = 0;
@@ -447,17 +446,13 @@ void MDCImporter::InternReadFile(
         {
             aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode();
             pcNode->mParent = pScene->mRootNode;
-            pcNode->mName.Set(std::string((const char*)pScene->mMeshes[i]->mTextureCoords[3]));
+            pcNode->mName = pScene->mMeshes[i]->mName;
             pcNode->mNumMeshes = 1;
             pcNode->mMeshes = new unsigned int[1];
             pcNode->mMeshes[0] = i;
         }
     }
 
-    // make sure we invalidate the pointer to the mesh name
-    for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
-        pScene->mMeshes[i]->mTextureCoords[3] = NULL;
-
     // create materials
     pScene->mNumMaterials = (unsigned int)aszShaders.size();
     pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];

+ 7 - 0
code/MDLLoader.cpp

@@ -415,8 +415,15 @@ void MDLImporter::InternReadFile_Quake1( )
     else
     {
         // get the first frame in the group
+
+#if 1
+        // FIXME: the cast is wrong and causea a warning on clang 5.0
+        // disable thi code for now, fix it later
+        ai_assert(false && "Bad pointer cast");
+#else
         BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames;
         pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type);
+#endif
     }
     BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
     VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts));

+ 13 - 13
code/MDLMaterialLoader.cpp

@@ -56,6 +56,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Defines.h>
 #include "qnan.h"
 
+#include <memory>
+
 
 using namespace Assimp;
 static aiTexel* const bad_texel = reinterpret_cast<aiTexel*>(SIZE_MAX);
@@ -489,7 +491,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
     unsigned int iWidth,
     unsigned int iHeight)
 {
-    aiTexture* pcNew = nullptr;
+    std::unique_ptr<aiTexture> pcNew;
 
     // get the type of the skin
     unsigned int iMasked = (unsigned int)(iType & 0xF);
@@ -509,7 +511,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
                 "but texture height is not equal to 1, which is not supported by MED");
         }
 
-        pcNew = new aiTexture();
+        pcNew.reset(new aiTexture());
         pcNew->mHeight = 0;
         pcNew->mWidth = iWidth;
 
@@ -546,7 +548,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
     }
     else if (iMasked || !iType || (iType && iWidth && iHeight))
     {
-        pcNew = new aiTexture();
+        pcNew.reset(new aiTexture());
         if (!iHeight || !iWidth)
         {
             DefaultLogger::get()->warn("Found embedded texture, but its width "
@@ -577,7 +579,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
             pcNew->mHeight = iHeight;
 
             unsigned int iSkip = 0;
-            ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew);
+            ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew.get());
 
             // skip length of texture data
             szCurrent += iSkip;
@@ -588,7 +590,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
     // texture instead of material colors ... posssible they have
     // been converted to MDL7 from other formats, such as MDL5
     aiColor4D clrTexture;
-    if (pcNew)clrTexture = ReplaceTextureWithColor(pcNew);
+    if (pcNew)clrTexture = ReplaceTextureWithColor(pcNew.get());
     else clrTexture.r = get_qnan();
 
     // check whether a material definition is contained in the skin
@@ -665,7 +667,9 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
         if (0.0f != pcMatIn->Power)
         {
             iShadingMode = (int)aiShadingMode_Phong;
-            pcMatOut->AddProperty<float>(&pcMatIn->Power,1,AI_MATKEY_SHININESS);
+            // pcMatIn is packed, we can't form pointers to its members
+            float power = pcMatIn->Power;
+            pcMatOut->AddProperty<float>(&power,1,AI_MATKEY_SHININESS);
         }
         pcMatOut->AddProperty<int>(&iShadingMode,1,AI_MATKEY_SHADING_MODEL);
     }
@@ -678,8 +682,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
     // we don't need the texture anymore
     if (is_not_qnan(clrTexture.r))
     {
-        delete pcNew;
-        pcNew = NULL;
+        pcNew.reset();
     }
 
     // If an ASCII effect description (HLSL?) is contained in the file,
@@ -714,7 +717,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
         {
             pScene->mNumTextures = 1;
             pScene->mTextures = new aiTexture*[1];
-            pScene->mTextures[0] = pcNew;
+            pScene->mTextures[0] = pcNew.release();
         }
         else
         {
@@ -724,16 +727,13 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
                 pScene->mTextures[i] = pc[i];
             }
 
-            pScene->mTextures[pScene->mNumTextures] = pcNew;
+            pScene->mTextures[pScene->mNumTextures] = pcNew.release();
             pScene->mNumTextures++;
             delete[] pc;
         }
     }
     VALIDATE_FILE_SIZE(szCurrent);
     *szCurrentOut = szCurrent;
-    if ( nullptr != pcNew ) {
-        delete pcNew;
-    }
 }
 
 // ------------------------------------------------------------------------------------------------

+ 8 - 8
code/MMDImporter.cpp

@@ -107,7 +107,7 @@ const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; }
 // ------------------------------------------------------------------------------------------------
 //  MMD import implementation
 void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
-                                 IOSystem *pIOHandler) {
+                                 IOSystem * /*pIOHandler*/) {
   // Read file by istream
   std::filebuf fb;
   if (!fb.open(file, std::ios::in | std::ios::binary)) {
@@ -141,8 +141,6 @@ void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel,
   aiNode *pNode = new aiNode;
   if (!pModel->model_name.empty()) {
     pNode->mName.Set(pModel->model_name);
-  } else {
-    ai_assert(false);
   }
 
   pScene->mRootNode = pNode;
@@ -170,7 +168,7 @@ void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel,
   }
 
   // create node hierarchy for bone position
-  aiNode **ppNode = new aiNode *[pModel->bone_count];
+  std::unique_ptr<aiNode *[]> ppNode(new aiNode *[pModel->bone_count]);
   for (auto i = 0; i < pModel->bone_count; i++) {
     ppNode[i] = new aiNode(pModel->bones[i].bone_name);
   }
@@ -179,9 +177,9 @@ void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel,
     const pmx::PmxBone &bone = pModel->bones[i];
 
     if (bone.parent_index < 0) {
-      pScene->mRootNode->addChildren(1, ppNode + i);
+      pScene->mRootNode->addChildren(1, ppNode.get() + i);
     } else {
-      ppNode[bone.parent_index]->addChildren(1, ppNode + i);
+      ppNode[bone.parent_index]->addChildren(1, ppNode.get() + i);
 
       aiVector3D v3 = aiVector3D(
           bone.position[0] - pModel->bones[bone.parent_index].position[0],
@@ -326,8 +324,10 @@ aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
     auto it = bone_vertex_map.find(ii);
     if (it != bone_vertex_map.end()) {
       pBone->mNumWeights = static_cast<unsigned int>(it->second.size());
-      pBone->mWeights = it->second.data();
-      it->second.swap(*(new vector<aiVertexWeight>));
+      pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
+      for (unsigned int j = 0; j < pBone->mNumWeights; j++) {
+          pBone->mWeights[j] = it->second[j];
+      }
     }
     bone_ptr_ptr[ii] = pBone;
   }

+ 1 - 1
code/MMDPmxParser.cpp

@@ -471,7 +471,7 @@ namespace pmx
 		stream->read((char*) &this->is_near, sizeof(uint8_t));
 	}
 
-	void PmxSoftBody::Read(std::istream *stream, PmxSetting *setting)
+    void PmxSoftBody::Read(std::istream * /*stream*/, PmxSetting * /*setting*/)
 	{
 		// 未実装
 		std::cerr << "Not Implemented Exception" << std::endl;

+ 1 - 0
code/MMDPmxParser.h

@@ -87,6 +87,7 @@ namespace pmx
 	{
 	public:
 		virtual void Read(std::istream *stream, PmxSetting *setting) = 0;
+		virtual ~PmxVertexSkinning() {}
 	};
 
 	class PmxVertexSkinningBDEF1 : public PmxVertexSkinning

+ 1 - 1
code/MMDVmdParser.h

@@ -302,7 +302,7 @@ namespace vmd
 			return result;
 		}
 
-		bool SaveToFile(const std::u16string& filename)
+        bool SaveToFile(const std::u16string& /*filename*/)
 		{
 			// TODO: How to adapt u16string to string?
 			/*

+ 11 - 8
code/NFFLoader.cpp

@@ -243,8 +243,6 @@ void NFFImporter::InternReadFile( const std::string& pFile,
     if( !file.get())
         throw DeadlyImportError( "Failed to open NFF file " + pFile + ".");
 
-    unsigned int m = (unsigned int)file->FileSize();
-
     // allocate storage and copy the contents of the file to a memory buffer
     // (terminate it with zero)
     std::vector<char> mBuffer2;
@@ -469,7 +467,7 @@ void NFFImporter::InternReadFile( const std::string& pFile,
                         for (unsigned int a = 0; a < numIdx;++a)
                         {
                             SkipSpaces(sz,&sz);
-                            m = ::strtoul10(sz,&sz);
+                            unsigned int m = ::strtoul10(sz,&sz);
                             if (m >= (unsigned int)tempPositions.size())
                             {
                                 DefaultLogger::get()->error("NFF2: Vertex index overflow");
@@ -635,7 +633,7 @@ void NFFImporter::InternReadFile( const std::string& pFile,
                         for (std::vector<unsigned int>::const_iterator it = tempIdx.begin(), end = tempIdx.end();
                             it != end;++it)
                         {
-                            m = *it;
+                            unsigned int m = *it;
 
                             // copy colors -vertex color specifications override polygon color specifications
                             if (hasColor)
@@ -735,7 +733,7 @@ void NFFImporter::InternReadFile( const std::string& pFile,
                     sz = &line[1];out = currentMesh;
                 }
                 SkipSpaces(sz,&sz);
-                m = strtoul10(sz);
+                unsigned int m = strtoul10(sz);
 
                 // ---- flip the face order
                 out->vertices.resize(out->vertices.size()+m);
@@ -1081,7 +1079,9 @@ void NFFImporter::InternReadFile( const std::string& pFile,
     // generate the camera
     if (hasCam)
     {
-        aiNode* nd = *ppcChildren = new aiNode();
+        ai_assert(ppcChildren);
+        aiNode* nd = new aiNode();
+        *ppcChildren = nd;
         nd->mName.Set("<NFF_Camera>");
         nd->mParent = root;
 
@@ -1105,13 +1105,15 @@ void NFFImporter::InternReadFile( const std::string& pFile,
     // generate light sources
     if (!lights.empty())
     {
+        ai_assert(ppcChildren);
         pScene->mNumLights = (unsigned int)lights.size();
         pScene->mLights = new aiLight*[pScene->mNumLights];
         for (unsigned int i = 0; i < pScene->mNumLights;++i,++ppcChildren)
         {
             const Light& l = lights[i];
 
-            aiNode* nd = *ppcChildren  = new aiNode();
+            aiNode* nd = new aiNode();
+            *ppcChildren = nd;
             nd->mParent = root;
 
             nd->mName.length = ::ai_snprintf(nd->mName.data,1024,"<NFF_Light%u>",i);
@@ -1128,7 +1130,8 @@ void NFFImporter::InternReadFile( const std::string& pFile,
     if (!pScene->mNumMeshes)throw DeadlyImportError("NFF: No meshes loaded");
     pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
     pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes];
-    for (it = meshes.begin(), m = 0; it != end;++it)
+    unsigned int m = 0;
+    for (it = meshes.begin(); it != end;++it)
     {
         if ((*it).faces.empty())continue;
 

+ 45 - 15
code/ObjExporter.cpp

@@ -58,10 +58,14 @@ namespace Assimp {
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Wavefront OBJ. Prototyped and registered in Exporter.cpp
-void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) {
+void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
     // invoke the exporter
     ObjExporter exporter(pFile, pScene);
 
+    if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) {
+        throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
+    }
+
     // we're still here - export successfully completed. Write both the main OBJ file and the material script
     {
         std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
@@ -79,18 +83,40 @@ void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene
     }
 }
 
+// ------------------------------------------------------------------------------------------------
+// Worker function for exporting a scene to Wavefront OBJ without the material file. Prototyped and registered in Exporter.cpp
+void ExportSceneObjNoMtl(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) {
+    // invoke the exporter
+    ObjExporter exporter(pFile, pScene, true);
+
+    if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) {
+        throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
+    }
+
+    // we're still here - export successfully completed. Write both the main OBJ file and the material script
+    {
+        std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
+        if(outfile == NULL) {
+            throw DeadlyExportError("could not open output .obj file: " + std::string(pFile));
+        }
+        outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1);
+    }
+
+
+}
+
 } // end of namespace Assimp
 
 static const std::string MaterialExt = ".mtl";
 
 // ------------------------------------------------------------------------------------------------
-ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene)
+ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl)
 : filename(_filename)
 , pScene(pScene)
 , vp()
 , vn()
 , vt()
-, vc() 
+, vc()
 , mVpMap()
 , mVnMap()
 , mVtMap()
@@ -104,8 +130,9 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene)
     mOutputMat.imbue(l);
     mOutputMat.precision(16);
 
-    WriteGeometryFile();
-    WriteMaterialFile();
+    WriteGeometryFile(noMtl);
+    if (!noMtl)
+        WriteMaterialFile();
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -128,7 +155,7 @@ std::string ObjExporter :: GetMaterialLibName()
 
 // ------------------------------------------------------------------------------------------------
 std::string ObjExporter::GetMaterialLibFileName() {
-    // Remove existing .obj file extention so that the final material file name will be fileName.mtl and not fileName.obj.mtl
+    // Remove existing .obj file extension so that the final material file name will be fileName.mtl and not fileName.obj.mtl
     size_t lastdot = filename.find_last_of('.');
     if (lastdot != std::string::npos)
         return filename.substr(0, lastdot) + MaterialExt;
@@ -189,7 +216,7 @@ void ObjExporter::WriteMaterialFile()
         if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_TRANSPARENT,c)) {
             mOutputMat << "Tf " << c.r << " " << c.g << " " << c.b << endl;
         }
-        
+
         ai_real o;
         if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) {
             mOutputMat << "d " << o << endl;
@@ -231,10 +258,10 @@ void ObjExporter::WriteMaterialFile()
     }
 }
 
-// ------------------------------------------------------------------------------------------------
-void ObjExporter::WriteGeometryFile() {
+void ObjExporter::WriteGeometryFile(bool noMtl) {
     WriteHeader(mOutput);
-    mOutput << "mtllib "  << GetMaterialLibName() << endl << endl;
+    if (!noMtl)
+        mOutput << "mtllib "  << GetMaterialLibName() << endl << endl;
 
     // collect mesh geometry
     aiMatrix4x4 mBase;
@@ -252,8 +279,10 @@ void ObjExporter::WriteGeometryFile() {
         mOutput << "# " << vp.size() << " vertex positions and colors" << endl;
         size_t colIdx = 0;
         for ( const aiVector3D& v : vp ) {
-            mOutput << "v  " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl;
-            colIdx++;
+            if ( colIdx < vc.size() ) {
+                mOutput << "v  " << v.x << " " << v.y << " " << v.z << " " << vc[ colIdx ].r << " " << vc[ colIdx ].g << " " << vc[ colIdx ].b << endl;
+            }
+            ++colIdx;
         }
     }
     mOutput << endl;
@@ -280,7 +309,8 @@ void ObjExporter::WriteGeometryFile() {
         if (!m.name.empty()) {
             mOutput << "g " << m.name << endl;
         }
-        mOutput << "usemtl " << m.matname << endl;
+        if (!noMtl)
+            mOutput << "usemtl " << m.matname << endl;
 
         for(const Face& f : m.faces) {
             mOutput << f.kind << ' ';
@@ -337,7 +367,7 @@ int ObjExporter::colIndexMap::getIndex( const aiColor4D& col ) {
     colMap[ col ] = mNextIndex;
     int ret = mNextIndex;
     mNextIndex++;
-    
+
     return ret;
 }
 
@@ -354,7 +384,7 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
     mMeshes.push_back(MeshInstance());
     MeshInstance& mesh = mMeshes.back();
 
-    mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_" + std::string(m->mName.data,m->mName.length) : "");
+    mesh.name = std::string( name.data, name.length );
     mesh.matname = GetMaterialName(m->mMaterialIndex);
 
     mesh.faces.resize(m->mNumFaces);

+ 2 - 2
code/ObjExporter.h

@@ -62,7 +62,7 @@ namespace Assimp {
 class ObjExporter {
 public:
     /// Constructor for a specific scene to export
-    ObjExporter(const char* filename, const aiScene* pScene);
+    ObjExporter(const char* filename, const aiScene* pScene, bool noMtl=false);
     ~ObjExporter();
     std::string GetMaterialLibName();
     std::string GetMaterialLibFileName();
@@ -97,7 +97,7 @@ private:
 
     void WriteHeader(std::ostringstream& out);
     void WriteMaterialFile();
-    void WriteGeometryFile();
+    void WriteGeometryFile(bool noMtl=false);
     std::string GetMaterialName(unsigned int index);
     void AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat);
     void AddNode(const aiNode* nd, const aiMatrix4x4& mParent);

+ 13 - 10
code/ObjFileImporter.cpp

@@ -264,8 +264,12 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile
     {
         unsigned int meshId = pObject->m_Meshes[ i ];
         aiMesh *pMesh = createTopology( pModel, pObject, meshId );
-        if( pMesh && pMesh->mNumFaces > 0 ) {
-            MeshArray.push_back( pMesh );
+        if( pMesh ) {
+            if (pMesh->mNumFaces > 0) {
+                MeshArray.push_back( pMesh );
+            } else {
+                delete pMesh;
+            }
         }
     }
 
@@ -317,7 +321,7 @@ aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const Obj
         return NULL;
     }
 
-    aiMesh* pMesh = new aiMesh;
+    std::unique_ptr<aiMesh> pMesh(new aiMesh);
     if( !pObjMesh->m_name.empty() ) {
         pMesh->mName.Set( pObjMesh->m_name );
     }
@@ -382,9 +386,9 @@ aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const Obj
     }
 
     // Create mesh vertices
-    createVertexArray(pModel, pData, meshIndex, pMesh, uiIdxCount);
+    createVertexArray(pModel, pData, meshIndex, pMesh.get(), uiIdxCount);
 
-    return pMesh;
+    return pMesh.release();
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -444,6 +448,10 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
                 throw DeadlyImportError( "OBJ: vertex index out of range" );
             }
 
+            if ( pMesh->mNumVertices <= newIndex ) {
+                throw DeadlyImportError("OBJ: bad vertex index");
+            }
+
             pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
 
             // Copy all normals
@@ -466,7 +474,6 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
             if ( !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_texturCoords.size())
             {
                 const unsigned int tex = pSourceFace->m_texturCoords.at( vertexIndex );
-                ai_assert( tex < pModel->m_TextureCoord.size() );
 
                 if ( tex >= pModel->m_TextureCoord.size() )
                     throw DeadlyImportError("OBJ: texture coordinate index out of range");
@@ -475,10 +482,6 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
                 pMesh->mTextureCoords[ 0 ][ newIndex ] = aiVector3D( coord3d.x, coord3d.y, coord3d.z );
             }
 
-            if ( pMesh->mNumVertices <= newIndex ) {
-                throw DeadlyImportError("OBJ: bad vertex index");
-            }
-
             // Get destination face
             aiFace *pDestFace = &pMesh->mFaces[ outIndex ];
 

+ 12 - 16
code/ObjFileParser.cpp

@@ -60,7 +60,7 @@ const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
 ObjFileParser::ObjFileParser()
 : m_DataIt()
 , m_DataItEnd()
-, m_pModel( NULL )
+, m_pModel( nullptr )
 , m_uiLine( 0 )
 , m_pIO( nullptr )
 , m_progress( nullptr )
@@ -73,7 +73,7 @@ ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::str
                               const std::string &originalObjFileName) :
     m_DataIt(),
     m_DataItEnd(),
-    m_pModel(NULL),
+    m_pModel(nullptr),
     m_uiLine(0),
     m_pIO( io ),
     m_progress(progress),
@@ -82,7 +82,7 @@ ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::str
     std::fill_n(m_buffer,Buffersize,0);
 
     // Create the model instance to store all the data
-    m_pModel = new ObjFile::Model();
+    m_pModel.reset(new ObjFile::Model());
     m_pModel->m_ModelName = modelName;
 
     // create default material and store it
@@ -96,8 +96,6 @@ ObjFileParser::ObjFileParser( IOStreamBuffer<char> &streamBuffer, const std::str
 }
 
 ObjFileParser::~ObjFileParser() {
-    delete m_pModel;
-    m_pModel = NULL;
 }
 
 void ObjFileParser::setBuffer( std::vector<char> &buffer ) {
@@ -106,7 +104,7 @@ void ObjFileParser::setBuffer( std::vector<char> &buffer ) {
 }
 
 ObjFile::Model *ObjFileParser::GetModel() const {
-    return m_pModel;
+    return m_pModel.get();
 }
 
 void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
@@ -353,14 +351,13 @@ void ObjFileParser::getHomogeneousVector3( std::vector<aiVector3D> &point3d_arra
     copyNextWord( m_buffer, Buffersize );
     w = ( ai_real ) fast_atof( m_buffer );
 
-    ai_assert( w != 0 );
+    if (w == 0)
+      throw DeadlyImportError("OBJ: Invalid component in homogeneous vector (Division by zero)");
 
     point3d_array.push_back( aiVector3D( x/w, y/w, z/w ) );
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
 
-// -------------------------------------------------------------------
-//  Get values for two 3D vectors on the same line
 void ObjFileParser::getTwoVectors3( std::vector<aiVector3D> &point3d_array_a, std::vector<aiVector3D> &point3d_array_b ) {
     ai_real x, y, z;
     copyNextWord(m_buffer, Buffersize);
@@ -388,8 +385,6 @@ void ObjFileParser::getTwoVectors3( std::vector<aiVector3D> &point3d_array_a, st
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
 
-// -------------------------------------------------------------------
-//  Get values for a new 2D vector instance
 void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) {
     ai_real x, y;
     copyNextWord(m_buffer, Buffersize);
@@ -405,8 +400,6 @@ void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array ) {
 
 static const std::string DefaultObjName = "defaultobject";
 
-// -------------------------------------------------------------------
-//  Get values for a new face instance
 void ObjFileParser::getFace( aiPrimitiveType type ) {
     m_DataIt = getNextToken<DataArrayIt>( m_DataIt, m_DataItEnd );
     if ( m_DataIt == m_DataItEnd || *m_DataIt == '\0' ) {
@@ -481,7 +474,12 @@ void ObjFileParser::getFace( aiPrimitiveType type ) {
                 } else {
                     reportErrorTokenInFace();
                 }
+            } else {
+                //On error, std::atoi will return 0 which is not a valid value
+                delete face;
+                throw DeadlyImportError("OBJ: Invalid face indice");
             }
+
         }
         m_DataIt += iStep;
     }
@@ -522,8 +520,6 @@ void ObjFileParser::getFace( aiPrimitiveType type ) {
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
 
-// -------------------------------------------------------------------
-//  Get values for a new material description
 void ObjFileParser::getMaterialDesc() {
     // Get next data for material data
     m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
@@ -642,7 +638,7 @@ void ObjFileParser::getMaterialLib() {
     m_pIO->Close( pFile );
 
     // Importing the material library
-    ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel );
+    ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel.get() );
 }
 
 // -------------------------------------------------------------------

+ 2 - 1
code/ObjFileParser.h

@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 #include <string>
 #include <map>
+#include <memory>
 #include <assimp/vector2.h>
 #include <assimp/vector3.h>
 #include <assimp/mesh.h>
@@ -145,7 +146,7 @@ private:
     //! Iterator to end position of buffer
     DataArrayIt m_DataItEnd;
     //! Pointer to model instance
-    ObjFile::Model *m_pModel;
+    std::unique_ptr<ObjFile::Model> m_pModel;
     //! Current line (for debugging)
     unsigned int m_uiLine;
     //! Helper buffer

+ 1 - 1
code/OgreMaterial.cpp

@@ -258,7 +258,7 @@ aiMaterial* OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSyste
                 ReadTechnique(Trim(techniqueName), ss, material);
             }
 
-            // Read informations from a custom material
+            // Read information from a custom material
             /** @todo This "set $x y" does not seem to be a official Ogre material system feature.
                 Materials can inherit other materials and override texture units by using the (unique)
                 parent texture unit name in your cloned material.

+ 1 - 1
code/OpenGEXExporter.cpp

@@ -51,7 +51,7 @@ OpenGEXExporter::OpenGEXExporter() {
 OpenGEXExporter::~OpenGEXExporter() {
 }
 
-bool OpenGEXExporter::exportScene( const char *filename, const aiScene* pScene ) {
+bool OpenGEXExporter::exportScene( const char * /*filename*/, const aiScene* /*pScene*/ ) {
     return true;
 }
 

+ 25 - 11
code/OpenGEXImporter.cpp

@@ -431,7 +431,7 @@ void OpenGEXImporter::handleNodes( DDLNode *node, aiScene *pScene ) {
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == node || nullptr == m_ctx ) {
         return;
     }
@@ -467,7 +467,7 @@ void OpenGEXImporter::handleMetricNode( DDLNode *node, aiScene *pScene ) {
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleNameNode( DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == m_currentNode ) {
         throw DeadlyImportError( "No current node for name." );
         return;
@@ -512,7 +512,7 @@ static void getRefNames( DDLNode *node, std::vector<std::string> &names ) {
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == m_currentNode ) {
         throw DeadlyImportError( "No parent node for name." );
         return;
@@ -536,7 +536,7 @@ void OpenGEXImporter::handleObjectRefNode( DDLNode *node, aiScene *pScene ) {
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleMaterialRefNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == m_currentNode ) {
         throw DeadlyImportError( "No parent node for name." );
         return;
@@ -652,6 +652,8 @@ static void setMatrix( aiNode *node, DataArrayList *transformData ) {
         i++;
     }
 
+    ai_assert(i == 16);
+
     node->mTransformation.a1 = m[ 0 ];
     node->mTransformation.a2 = m[ 4 ];
     node->mTransformation.a3 = m[ 8 ];
@@ -674,7 +676,7 @@ static void setMatrix( aiNode *node, DataArrayList *transformData ) {
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == m_currentNode ) {
         throw DeadlyImportError( "No parent node for name." );
         return;
@@ -776,10 +778,22 @@ static void fillColor4( aiColor4D *col4, Value *vals ) {
     Value *next( vals );
     col4->r = next->getFloat();
     next = next->m_next;
+    if (!next) {
+        throw DeadlyImportError( "OpenGEX: Not enough values to fill 4-element color, only 1" );
+    }
+
     col4->g = next->getFloat();
     next = next->m_next;
+    if (!next) {
+        throw DeadlyImportError( "OpenGEX: Not enough values to fill 4-element color, only 2" );
+    }
+
     col4->b = next->getFloat();
     next = next->m_next;
+    if (!next) {
+        throw DeadlyImportError( "OpenGEX: Not enough values to fill 4-element color, only 3" );
+    }
+
     col4->a = next->getFloat();
 }
 
@@ -819,7 +833,7 @@ static void copyColor4DArray( size_t numItems, DataArrayList *vaList, aiColor4D
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == node ) {
         throw DeadlyImportError( "No parent node for name." );
         return;
@@ -862,7 +876,7 @@ void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == node ) {
         throw DeadlyImportError( "No parent node for name." );
         return;
@@ -1001,7 +1015,7 @@ void OpenGEXImporter::handleMaterialNode( ODDLParser::DDLNode *node, aiScene *pS
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == node ) {
         return;
     }
@@ -1040,7 +1054,7 @@ void OpenGEXImporter::handleColorNode( ODDLParser::DDLNode *node, aiScene *pScen
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if( nullptr == node ) {
         return;
     }
@@ -1074,7 +1088,7 @@ void OpenGEXImporter::handleTextureNode( ODDLParser::DDLNode *node, aiScene *pSc
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if ( nullptr == node ) {
         return;
     }
@@ -1103,7 +1117,7 @@ void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene *pScen
 }
 
 //------------------------------------------------------------------------------------------------
-void OpenGEXImporter::handleAttenNode( ODDLParser::DDLNode *node, aiScene *pScene ) {
+void OpenGEXImporter::handleAttenNode( ODDLParser::DDLNode *node, aiScene * /*pScene*/ ) {
     if ( nullptr == node ) {
         return;
     }

+ 6 - 4
code/OptimizeGraph.cpp

@@ -233,10 +233,12 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& no
 
     nd->mNumChildren = static_cast<unsigned int>(child_nodes.size());
 
-    aiNode** tmp = nd->mChildren;
-    for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
-        aiNode* node = *tmp++ = *it;
-        node->mParent = nd;
+    if (nd->mChildren) {
+        aiNode** tmp = nd->mChildren;
+        for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
+            aiNode* node = *tmp++ = *it;
+            node->mParent = nd;
+        }
     }
 
     nodes_out += static_cast<unsigned int>(child_nodes.size());

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