瀏覽代碼

Merge branch 'master' into master

gstanlo 7 年之前
父節點
當前提交
74e8200a49
共有 100 個文件被更改,包括 2798 次插入1122 次删除
  1. 7 0
      .gitignore
  2. 38 5
      CMakeLists.txt
  3. 9 10
      CONTRIBUTING.md
  4. 3 0
      INSTALL
  5. 4 0
      appveyor.yml
  6. 5 3
      code/3DSConverter.cpp
  7. 2 0
      code/Assimp.cpp
  8. 60 29
      code/BVHLoader.cpp
  9. 23 13
      code/BaseImporter.cpp
  10. 185 0
      code/BlenderCustomData.cpp
  11. 89 0
      code/BlenderCustomData.h
  12. 33 0
      code/BlenderDNA.h
  13. 102 0
      code/BlenderDNA.inl
  14. 67 8
      code/BlenderLoader.cpp
  15. 44 0
      code/BlenderScene.cpp
  16. 79 1
      code/BlenderScene.h
  17. 11 0
      code/BlenderSceneGen.h
  18. 8 0
      code/CMakeLists.txt
  19. 4 3
      code/ColladaHelper.h
  20. 23 25
      code/ColladaLoader.cpp
  21. 14 14
      code/ColladaParser.cpp
  22. 22 9
      code/ConvertToLHProcess.cpp
  23. 4 4
      code/Exporter.cpp
  24. 252 266
      code/FBXConverter.cpp
  25. 9 5
      code/FBXConverter.h
  26. 1 1
      code/FBXDocumentUtil.cpp
  27. 7 3
      code/FBXExporter.cpp
  28. 3 0
      code/FBXMeshGeometry.cpp
  29. 6 6
      code/FileSystemFilter.h
  30. 4 2
      code/GenFaceNormalsProcess.cpp
  31. 2 1
      code/GenFaceNormalsProcess.h
  32. 5 2
      code/GenVertexNormalsProcess.cpp
  33. 1 0
      code/GenVertexNormalsProcess.h
  34. 10 8
      code/Importer.cpp
  35. 1 1
      code/Importer/IFC/IFCGeometry.cpp
  36. 18 0
      code/JoinVerticesProcess.cpp
  37. 4 2
      code/LWOMaterial.cpp
  38. 9 37
      code/MMDPmxParser.cpp
  39. 59 26
      code/MaterialSystem.cpp
  40. 68 32
      code/ObjFileImporter.cpp
  41. 5 7
      code/ObjFileMtlImporter.cpp
  42. 200 222
      code/PlyLoader.cpp
  43. 6 3
      code/PostStepRegistry.cpp
  44. 39 16
      code/STLLoader.cpp
  45. 12 12
      code/SceneCombiner.cpp
  46. 10 6
      code/ScenePrivate.h
  47. 1 1
      code/SpatialSort.cpp
  48. 21 18
      code/TriangulateProcess.cpp
  49. 5 3
      code/Version.cpp
  50. 6 1
      code/XFileImporter.cpp
  51. 8 3
      code/XFileParser.cpp
  52. 7 0
      code/glTF2Asset.h
  53. 45 0
      code/glTF2Asset.inl
  54. 4 21
      code/glTF2Exporter.cpp
  55. 152 29
      code/glTF2Importer.cpp
  56. 71 12
      code/glTFImporter.cpp
  57. 6 0
      contrib/openddlparser/code/OpenDDLParser.cpp
  58. 2 1
      include/assimp/BaseImporter.h
  59. 1 1
      include/assimp/Macros.h
  60. 21 20
      include/assimp/anim.h
  61. 37 6
      include/assimp/material.h
  62. 2 2
      include/assimp/material.inl
  63. 4 1
      include/assimp/postprocess.h
  64. 14 6
      include/assimp/qnan.h
  65. 26 36
      packaging/windows-innosetup/script.iss
  66. 1 1
      port/AssimpNET/Readme.md
  67. 0 94
      port/PyAssimp/README.md
  68. 96 0
      port/PyAssimp/README.rst
  69. 11 5
      port/PyAssimp/pyassimp/helper.py
  70. 12 2
      port/PyAssimp/setup.py
  71. 6 8
      port/iOS/IPHONEOS_ARM64_TOOLCHAIN.cmake
  72. 6 8
      port/iOS/IPHONEOS_ARMV6_TOOLCHAIN.cmake
  73. 6 8
      port/iOS/IPHONEOS_ARMV7S_TOOLCHAIN.cmake
  74. 6 8
      port/iOS/IPHONEOS_ARMV7_TOOLCHAIN.cmake
  75. 5 8
      port/iOS/IPHONEOS_I386_TOOLCHAIN.cmake
  76. 5 8
      port/iOS/IPHONEOS_X86_64_TOOLCHAIN.cmake
  77. 98 29
      port/iOS/build.sh
  78. 1 0
      test/CMakeLists.txt
  79. 二進制
      test/models/BLEND/plane_2_textures_2_texcoords_279.blend
  80. 二進制
      test/models/BLEND/test_279.blend
  81. 二進制
      test/models/IRR/instancing.irr
  82. 7 0
      test/models/OpenGEX/empty_camera.ogex
  83. 21 0
      test/models/glTF2/glTF-Asset-Generator/LICENSE
  84. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.bin
  85. 63 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf
  86. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.bin
  87. 63 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf
  88. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.bin
  89. 63 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf
  90. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.bin
  91. 63 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf
  92. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.bin
  93. 63 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf
  94. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.bin
  95. 63 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf
  96. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.bin
  97. 62 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf
  98. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.bin
  99. 77 0
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf
  100. 二進制
      test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.bin

+ 7 - 0
.gitignore

@@ -21,6 +21,7 @@ revision.h
 contrib/zlib/zconf.h
 contrib/zlib/zlib.pc
 include/assimp/config.h
+unit.vcxproj.user
 
 # CMake
 CMakeCache.txt
@@ -87,3 +88,9 @@ lib64/assimp-vc120-mt.exp
 xcuserdata
 
 cmake-build-debug
+install_manifest.txt
+tools/assimp_qt_viewer/moc_glview.cpp
+tools/assimp_qt_viewer/moc_glview.cpp_parameters
+tools/assimp_qt_viewer/moc_mainwindow.cpp
+tools/assimp_qt_viewer/moc_mainwindow.cpp_parameters
+tools/assimp_qt_viewer/ui_mainwindow.h

+ 38 - 5
CMakeLists.txt

@@ -39,10 +39,12 @@ CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
 PROJECT( Assimp )
 
 # All supported options ###############################################
+
 OPTION( BUILD_SHARED_LIBS
   "Build package with shared libraries."
   ON
 )
+
 OPTION( BUILD_FRAMEWORK
   "Build package as Mac OS X Framework bundle."
   OFF
@@ -103,6 +105,16 @@ OPTION ( BUILD_DOCS
   "Build documentation using Doxygen."
   OFF
 )
+OPTION( INJECT_DEBUG_POSTFIX
+  "Inject debug postfix in .a/.so lib names"
+  ON
+)
+
+IF (IOS)
+	IF (NOT CMAKE_BUILD_TYPE)
+ 		SET(CMAKE_BUILD_TYPE "Release")
+	ENDIF (NOT CMAKE_BUILD_TYPE)
+ENDIF (IOS)
 
 # Use subset of Windows.h
 if (WIN32)
@@ -152,7 +164,7 @@ EXECUTE_PROCESS(
 
 # Get the latest abbreviated commit hash of the working branch
 EXECUTE_PROCESS(
-  COMMAND git log -1 --format=%h
+  COMMAND git log -1 --format=%h --no-show-signature
   WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
   OUTPUT_VARIABLE GIT_COMMIT_HASH
   OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -204,7 +216,7 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
 ELSEIF(MSVC)
   # enable multi-core compilation with MSVC
   ADD_COMPILE_OPTIONS(/MP)
-  IF("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
+  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
     ADD_COMPILE_OPTIONS( /bigobj )
   ENDIF()
   # disable "elements of array '' will be default initialized" warning on MSVC2013
@@ -220,11 +232,18 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW )
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
 ENDIF()
 
-IF (IOS)
-  SET(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -fembed-bitcode -O3")
+IF ( IOS )
+
+IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -Og")
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -Og")
+ELSE()
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
 ENDIF()
 
+ENDIF( IOS )
+
 IF (ASSIMP_COVERALLS)
   MESSAGE(STATUS "Coveralls enabled")
   INCLUDE(Coveralls)
@@ -320,6 +339,8 @@ IF( NOT ZLIB_FOUND )
   SET(ZLIB_FOUND 1)
   SET(ZLIB_LIBRARIES zlibstatic)
   SET(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/contrib/zlib ${CMAKE_CURRENT_BINARY_DIR}/contrib/zlib)
+  # need to ensure we don't link with system zlib or minizip as well.
+  SET(ASSIMP_BUILD_MINIZIP 1)
 ELSE(NOT ZLIB_FOUND)
   ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB)
   SET(ZLIB_LIBRARIES_LINKED -lz)
@@ -327,7 +348,17 @@ ENDIF(NOT ZLIB_FOUND)
 INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
 
 # Search for unzip
-use_pkgconfig(UNZIP minizip)
+IF ( NOT IOS )
+  IF( NOT ASSIMP_BUILD_MINIZIP )
+	  use_pkgconfig(UNZIP minizip)
+  ENDIF( NOT ASSIMP_BUILD_MINIZIP )
+ELSE ( NOT IOS )
+	IF(NOT BUILD_SHARED_LIBS)
+    IF( NOT ASSIMP_BUILD_MINIZIP )
+		  use_pkgconfig(UNZIP minizip)
+    ENDIF( NOT ASSIMP_BUILD_MINIZIP )
+	ENDIF (NOT BUILD_SHARED_LIBS)
+ENDIF ( NOT IOS )
 
 IF ( ASSIMP_NO_EXPORT )
   ADD_DEFINITIONS( -DASSIMP_BUILD_NO_EXPORT)
@@ -412,7 +443,9 @@ IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
   ENDIF ( WIN32 AND DirectX_D3DX9_LIBRARY )
 
   ADD_SUBDIRECTORY( tools/assimp_cmd/ )
+IF (NOT IOS)
   ADD_SUBDIRECTORY( tools/assimp_qt_viewer/ )
+ENDIF (NOT IOS)
 ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
 
 IF ( ASSIMP_BUILD_SAMPLES)

+ 9 - 10
CONTRIBUTING.md

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

+ 3 - 0
INSTALL

@@ -42,3 +42,6 @@ For Windows:
 1. Open a command prompt
 2. cmake CMakeLists.txt
 2. Open your default IDE and build it
+
+For iOS:
+Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS

+ 4 - 0
appveyor.yml

@@ -32,6 +32,9 @@ install:
   - 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%"
+  - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
+  - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
+  - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
   
 cache:
   - code\assimp.dir\%CONFIGURATION%
@@ -50,6 +53,7 @@ build:
   project: Assimp.sln
   
 after_build:
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" iscc packaging\windows-innosetup\script.iss
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
 
 test_script:

+ 5 - 3
code/3DSConverter.cpp

@@ -204,8 +204,9 @@ void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
         mat.AddProperty<ai_real>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
 
     // Setup the texture mapping mode
-    mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
-    mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
+    int mapMode = static_cast<int>(texture.mMapMode);
+    mat.AddProperty<int>(&mapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
+    mat.AddProperty<int>(&mapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
 
     // Mirroring - double the scaling values
     // FIXME: this is not really correct ...
@@ -313,7 +314,8 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
         case D3DS::Discreet3DS::Blinn :
             eShading = aiShadingMode_Blinn; break;
     }
-    mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
+    int eShading_ = static_cast<int>(eShading);
+    mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
 
     // DIFFUSE texture
     if( oldMat.sTexDiffuse.mMapName.length() > 0)

+ 2 - 0
code/Assimp.cpp

@@ -272,6 +272,8 @@ void aiReleaseImport( const aiScene* pScene)
 
     ASSIMP_BEGIN_EXCEPTION_REGION();
 
+    aiReleaseDefaultMaterial();
+
     // find the importer associated with this data
     const ScenePrivateData* priv = ScenePriv(pScene);
     if( !priv || !priv->mOrigImporter)  {

+ 60 - 29
code/BVHLoader.cpp

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

+ 23 - 13
code/BaseImporter.cpp

@@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include <assimp/BaseImporter.h>
+#include <assimp/ParsingUtils.h>
 #include "FileSystemFilter.h"
 #include "Importer.h"
 #include <assimp/ByteSwapper.h>
@@ -53,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Importer.hpp>
 #include <assimp/postprocess.h>
 #include <assimp/importerdesc.h>
+
 #include <ios>
 #include <list>
 #include <memory>
@@ -143,7 +145,8 @@ void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
     const char**        tokens,
     unsigned int        numTokens,
     unsigned int        searchBytes /* = 200 */,
-    bool                tokensSol /* false */)
+    bool                tokensSol /* false */,
+    bool                noAlphaBeforeTokens /* false */)
 {
     ai_assert( nullptr != tokens );
     ai_assert( 0 != numTokens );
@@ -157,15 +160,14 @@ void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
     if (pStream.get() ) {
         // read 200 characters from the file
         std::unique_ptr<char[]> _buffer (new char[searchBytes+1 /* for the '\0' */]);
-        char* buffer = _buffer.get();
-
-        const size_t read = pStream->Read(buffer,1,searchBytes);
-        if( !read ) {
+        char *buffer( _buffer.get() );
+        const size_t read( pStream->Read(buffer,1,searchBytes) );
+        if( 0 == read ) {
             return false;
         }
 
         for( size_t i = 0; i < read; ++i ) {
-            buffer[ i ] = ::tolower( buffer[ i ] );
+            buffer[ i ] = static_cast<char>( ::tolower( buffer[ i ] ) );
         }
 
         // It is not a proper handling of unicode files here ...
@@ -186,13 +188,18 @@ void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
             token.clear();
             const char *ptr( tokens[ i ] );
             for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) {
-                token.push_back( tolower( *ptr ) );
+                token.push_back( static_cast<char>( tolower( *ptr ) ) );
                 ++ptr;
             }
             const char* r = strstr( buffer, token.c_str() );
             if( !r ) {
                 continue;
             }
+            // We need to make sure that we didn't accidentially identify the end of another token as our token,
+            // e.g. in a previous version the "gltf " present in some gltf files was detected as "f "
+            if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) {
+                continue;
+            }
             // We got a match, either we don't care where it is, or it happens to
             // be in the beginning of the file / line
             if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') {
@@ -234,16 +241,19 @@ void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
 
 // ------------------------------------------------------------------------------------------------
 // Get file extension from path
-/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile)
-{
-    std::string::size_type pos = pFile.find_last_of('.');
+std::string BaseImporter::GetExtension( const std::string& file ) {
+    std::string::size_type pos = file.find_last_of('.');
 
     // no file extension at all
-    if( pos == std::string::npos)
+    if (pos == std::string::npos) {
         return "";
+    }
+
+
+    // thanks to Andy Maloney for the hint
+    std::string ret = file.substr( pos + 1 );
+    std::transform( ret.begin(), ret.end(), ret.begin(), ToLower<char>);
 
-    std::string ret = pFile.substr(pos+1);
-    std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint
     return ret;
 }
 

+ 185 - 0
code/BlenderCustomData.cpp

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

+ 89 - 0
code/BlenderCustomData.h

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

+ 33 - 0
code/BlenderDNA.h

@@ -309,6 +309,28 @@ public:
     void ReadField(T& out, const char* name,
         const FileDatabase& db) const;
 
+    // --------------------------------------------------------
+    /**
+    *   @brief  field parsing for dynamic vectors
+    *   @param[in]  out vector of struct to be filled
+    *   @param[in]  name of field
+    *   @param[in]  db to access the file, dna, ...
+    *   @return true when read was successful
+    */
+    template <int error_policy, template <typename> class TOUT, typename T>
+    bool ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const;
+
+    /**
+    *   @brief  parses raw customdata
+    *   @param[in]  out shared_ptr to be filled
+    *   @param[in]  cdtype customdata type to read
+    *   @param[in]  name of field ptr
+    *   @param[in]  db to access the file, dna, ...
+    *   @return true when read was successful
+    */
+    template <int error_policy>
+    bool ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const;
+
 private:
 
     // --------------------------------------------------------
@@ -803,6 +825,17 @@ private:
     FileDatabase& db;
 };
 
+/**
+*   @brief  read CustomData's data to ptr to mem
+*   @param[out] out memory ptr to set
+*   @param[in]  cdtype  to read
+*   @param[in]  cnt cnt of elements to read
+*   @param[in]  db to read elements from
+*   @return true when ok
+*/
+bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
+
+
     } // end Blend
 } // end Assimp
 

+ 102 - 0
code/BlenderDNA.inl

@@ -307,6 +307,108 @@ void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) co
 }
 
 
+//--------------------------------------------------------------------------------
+// field parsing for raw untyped data (like CustomDataLayer.data)
+template <int error_policy>
+bool Structure::ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, const char* name, const FileDatabase& db) const {
+
+	const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+
+	Pointer ptrval;
+	const Field* f;
+	try	{
+		f = &(*this)[name];
+
+		// sanity check, should never happen if the genblenddna script is right
+		if (!(f->flags & FieldFlag_Pointer)) {
+			throw Error((Formatter::format(), "Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer"));
+		}
+
+		db.reader->IncPtr(f->offset);
+		Convert(ptrval, db);
+		// actually it is meaningless on which Structure the Convert is called
+		// because the `Pointer` argument triggers a special implementation.
+	}
+	catch (const Error& e) {
+		_defaultInitializer<error_policy>()(out, e.what());
+		out.reset();
+	}
+
+	bool readOk = true;
+	if (ptrval.val)	{
+		// get block for ptr
+		const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
+		db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
+		// read block->num instances of given type to out
+		readOk = readCustomData(out, cdtype, block->num, db);
+	}
+
+	// and recover the previous stream position
+	db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+	++db.stats().fields_read;
+#endif
+
+	return readOk;
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T>
+bool Structure::ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const FileDatabase& db) const {
+	out.clear();
+
+	const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+
+	Pointer ptrval;
+	const Field* f;
+	try	{
+		f = &(*this)[name];
+
+		// sanity check, should never happen if the genblenddna script is right
+		if (!(f->flags & FieldFlag_Pointer)) {
+			throw Error((Formatter::format(), "Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer"));
+		}
+
+		db.reader->IncPtr(f->offset);
+		Convert(ptrval, db);
+		// actually it is meaningless on which Structure the Convert is called
+		// because the `Pointer` argument triggers a special implementation.
+	}
+	catch (const Error& e) {
+		_defaultInitializer<error_policy>()(out, e.what());
+		out.clear();
+		return false;
+	}
+
+
+	if (ptrval.val)	{
+		// find the file block the pointer is pointing to
+		const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db);
+		db.reader->SetCurrentPos(block->start + static_cast<size_t>((ptrval.val - block->address.val)));
+		// FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+		// I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+		const Structure& s = db.dna[f->type];
+		for (size_t i = 0; i < block->num; ++i)	{
+			TOUT<T> p(new T);
+			s.Convert(*p, db);
+			out.push_back(p);
+		}
+	}
+
+	db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+	++db.stats().fields_read;
+#endif
+
+	return false;
+}
+
+
 //--------------------------------------------------------------------------------
 template <template <typename> class TOUT, typename T>
 bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db,

+ 67 - 8
code/BlenderLoader.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BlenderIntermediate.h"
 #include "BlenderModifier.h"
 #include "BlenderBMesh.h"
+#include "BlenderCustomData.h"
 #include <assimp/StringUtils.h>
 #include <assimp/scene.h>
 #include <assimp/importerdesc.h>
@@ -1021,6 +1022,34 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
         }
     }
 
+    // TODO should we create the TextureUVMapping map in Convert<Material> to prevent redundant processing?
+
+    // create texture <-> uvname mapping for all materials
+    // key is texture number, value is data *
+    typedef std::map<uint32_t, const MLoopUV *> TextureUVMapping;
+    // key is material number, value is the TextureUVMapping for the material
+    typedef std::map<uint32_t, TextureUVMapping> MaterialTextureUVMappings;
+    MaterialTextureUVMappings matTexUvMappings;
+    const uint32_t maxMat = static_cast<const uint32_t>(mesh->mat.size());
+    for (uint32_t m = 0; m < maxMat; ++m) {
+        // get material by index
+        const std::shared_ptr<Material> pMat = mesh->mat[m];
+        TextureUVMapping texuv;
+        const uint32_t maxTex = sizeof(pMat->mtex) / sizeof(pMat->mtex[0]);
+        for (uint32_t t = 0; t < maxTex; ++t) {
+            if (pMat->mtex[t] && pMat->mtex[t]->uvname[0]) {
+                // get the CustomData layer for given uvname and correct type
+                const ElemBase *pLoop = getCustomDataLayerData(mesh->ldata, CD_MLOOPUV, pMat->mtex[t]->uvname);
+                if (pLoop) {
+                    texuv.insert(std::make_pair(t, dynamic_cast<const MLoopUV *>(pLoop)));
+                }
+            }
+        }
+        if (texuv.size()) {
+            matTexUvMappings.insert(std::make_pair(m, texuv));
+        }
+    }
+
     // collect texture coordinates, they're stored in a separate per-face buffer
     if (mesh->mtface || mesh->mloopuv) {
         if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
@@ -1028,8 +1057,17 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
         }
         for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
             ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
-
-            (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+            const auto itMatTexUvMapping = matTexUvMappings.find((*it)->mMaterialIndex);
+            if (itMatTexUvMapping == matTexUvMappings.end()) {
+                // default behaviour like before
+                (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+            }
+            else {
+                // create texture coords for every mapped tex
+                for (uint32_t i = 0; i < itMatTexUvMapping->second.size(); ++i) {
+                    (*it)->mTextureCoords[i] = new aiVector3D[(*it)->mNumVertices];
+                }
+            }
             (*it)->mNumFaces = (*it)->mNumVertices = 0;
         }
 
@@ -1051,13 +1089,34 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
             aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
             const aiFace& f = out->mFaces[out->mNumFaces++];
 
-            aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
-            for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
-                const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
-                vo->x = uv.uv[0];
-                vo->y = uv.uv[1];
+            const auto itMatTexUvMapping = matTexUvMappings.find(v.mat_nr);
+            if (itMatTexUvMapping == matTexUvMappings.end()) {
+                // old behavior
+                aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+                for (unsigned int j = 0; j < f.mNumIndices; ++j, ++vo, ++out->mNumVertices) {
+                    const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
+                    vo->x = uv.uv[0];
+                    vo->y = uv.uv[1];
+                }
+            }
+            else {
+                // create textureCoords for every mapped tex
+                for (uint32_t m = 0; m < itMatTexUvMapping->second.size(); ++m) {
+                    const MLoopUV *tm = itMatTexUvMapping->second[m];
+                    aiVector3D* vo = &out->mTextureCoords[m][out->mNumVertices];
+                    uint32_t j = 0;
+                    for (; j < f.mNumIndices; ++j, ++vo) {
+                        const MLoopUV& uv = tm[v.loopstart + j];
+                        vo->x = uv.uv[0];
+                        vo->y = uv.uv[1];
+                    }
+                    // only update written mNumVertices in last loop
+                    // TODO why must the numVertices be incremented here?
+                    if (m == itMatTexUvMapping->second.size() - 1) {
+                        out->mNumVertices += j;
+                    }
+                }
             }
-
         }
     }
 

+ 44 - 0
code/BlenderScene.cpp

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BlenderScene.h"
 #include "BlenderSceneGen.h"
 #include "BlenderDNA.h"
+#include "BlenderCustomData.h"
 
 using namespace Assimp;
 using namespace Assimp::Blender;
@@ -481,6 +482,12 @@ template <> void Structure :: Convert<Mesh> (
     ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol,"*mcol",db);
     ReadFieldPtr<ErrorPolicy_Fail>(dest.mat,"**mat",db);
 
+    ReadField<ErrorPolicy_Igno>(dest.vdata, "vdata", db);
+    ReadField<ErrorPolicy_Igno>(dest.edata, "edata", db);
+    ReadField<ErrorPolicy_Igno>(dest.fdata, "fdata", db);
+    ReadField<ErrorPolicy_Igno>(dest.pdata, "pdata", db);
+    ReadField<ErrorPolicy_Warn>(dest.ldata, "ldata", db);
+
     db.reader->IncPtr(size);
 }
 
@@ -786,6 +793,41 @@ template <> void Structure :: Convert<Image> (
     db.reader->IncPtr(size);
 }
 
+//--------------------------------------------------------------------------------
+template <> void Structure::Convert<CustomData>(
+    CustomData& dest,
+    const FileDatabase& db
+    ) const
+{
+    ReadFieldArray<ErrorPolicy_Warn>(dest.typemap, "typemap", db);
+    ReadField<ErrorPolicy_Warn>(dest.totlayer, "totlayer", db);
+    ReadField<ErrorPolicy_Warn>(dest.maxlayer, "maxlayer", db);
+    ReadField<ErrorPolicy_Warn>(dest.totsize, "totsize", db);
+    ReadFieldPtrVector<ErrorPolicy_Warn>(dest.layers, "*layers", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure::Convert<CustomDataLayer>(
+    CustomDataLayer& dest,
+    const FileDatabase& db
+    ) const
+{
+    ReadField<ErrorPolicy_Fail>(dest.type, "type", db);
+    ReadField<ErrorPolicy_Fail>(dest.offset, "offset", db);
+    ReadField<ErrorPolicy_Fail>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Fail>(dest.active, "active", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_rnd, "active_rnd", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_clone, "active_clone", db);
+    ReadField<ErrorPolicy_Fail>(dest.active_mask, "active_mask", db);
+    ReadField<ErrorPolicy_Fail>(dest.uid, "uid", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadCustomDataPtr<ErrorPolicy_Fail>(dest.data, dest.type, "*data", db);
+
+    db.reader->IncPtr(size);
+}
+
 //--------------------------------------------------------------------------------
 void DNA::RegisterConverters() {
 
@@ -822,6 +864,8 @@ void DNA::RegisterConverters() {
     converters["Camera"] = DNA::FactoryPair( &Structure::Allocate<Camera>, &Structure::Convert<Camera> );
     converters["MirrorModifierData"] = DNA::FactoryPair( &Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData> );
     converters["Image"] = DNA::FactoryPair( &Structure::Allocate<Image>, &Structure::Convert<Image> );
+    converters["CustomData"] = DNA::FactoryPair(&Structure::Allocate<CustomData>, &Structure::Convert<CustomData>);
+    converters["CustomDataLayer"] = DNA::FactoryPair(&Structure::Allocate<CustomDataLayer>, &Structure::Convert<CustomDataLayer>);
 }
 
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 79 - 1
code/BlenderScene.h

@@ -157,10 +157,16 @@ struct World : ElemBase {
 // -------------------------------------------------------------------------------
 struct MVert : ElemBase {
     float co[3] FAIL;
-    float no[3] FAIL;
+    float no[3] FAIL;       // readed as short and divided through / 32767.f
     char flag;
     int mat_nr WARN;
     int bweight;
+
+    MVert() : ElemBase()
+        , flag(0)
+        , mat_nr(0)
+        , bweight(0)
+    {}
 };
 
 // -------------------------------------------------------------------------------
@@ -369,6 +375,72 @@ struct Material : ElemBase {
     std::shared_ptr<MTex> mtex[18];
 };
 
+/*
+CustomDataLayer 104
+
+    int type 0 4
+    int offset 4 4
+    int flag 8 4
+    int active 12 4
+    int active_rnd 16 4
+    int active_clone 20 4
+    int active_mask 24 4
+    int uid 28 4
+    char name 32 64
+    void *data 96 8
+*/
+struct CustomDataLayer : ElemBase {
+    int type;
+    int offset;
+    int flag;
+    int active;
+    int active_rnd;
+    int active_clone;
+    int active_mask;
+    int uid;
+    char name[64];
+    std::shared_ptr<ElemBase> data;     // must be converted to real type according type member
+
+    CustomDataLayer()
+        : ElemBase()
+        , type(0)
+        , offset(0)
+        , flag(0)
+        , active(0)
+        , active_rnd(0)
+        , active_clone(0)
+        , active_mask(0)
+        , uid(0)
+        , data(nullptr)
+    {
+        memset(name, 0, sizeof name);
+    }
+};
+
+/*
+CustomData 208
+
+    CustomDataLayer *layers 0 8
+    int typemap 8 168
+    int pad_i1 176 4
+    int totlayer 180 4
+    int maxlayer 184 4
+    int totsize 188 4
+    BLI_mempool *pool 192 8
+    CustomDataExternal *external 200 8
+*/
+struct CustomData : ElemBase {
+    vector<std::shared_ptr<struct CustomDataLayer> > layers;
+    int typemap[42];    // CD_NUMTYPES
+    int totlayer;
+    int maxlayer;
+    int totsize;
+    /*
+    std::shared_ptr<BLI_mempool> pool;
+    std::shared_ptr<CustomDataExternal> external;
+    */
+};
+
 // -------------------------------------------------------------------------------
 struct Mesh : ElemBase {
     ID id FAIL;
@@ -398,6 +470,12 @@ struct Mesh : ElemBase {
     vector<MCol> mcol;
 
     vector< std::shared_ptr<Material> > mat FAIL;
+
+    struct CustomData vdata;
+    struct CustomData edata;
+    struct CustomData fdata;
+    struct CustomData pdata;
+    struct CustomData ldata;
 };
 
 // -------------------------------------------------------------------------------

+ 11 - 0
code/BlenderSceneGen.h

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

+ 8 - 0
code/CMakeLists.txt

@@ -469,6 +469,8 @@ ADD_ASSIMP_IMPORTER( BLEND
   BlenderBMesh.cpp
   BlenderTessellator.h
   BlenderTessellator.cpp
+  BlenderCustomData.h
+  BlenderCustomData.cpp
 )
 
 ADD_ASSIMP_IMPORTER( IFC
@@ -909,6 +911,12 @@ ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
 
 ADD_LIBRARY( assimp ${assimp_src} )
 
+TARGET_INCLUDE_DIRECTORIES ( assimp PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>
+)
+
 TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES} ${IRRXML_LIBRARY} )
 
 if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)

+ 4 - 3
code/ColladaHelper.h

@@ -272,12 +272,13 @@ struct Node
     /** Node instances at this node */
     std::vector<NodeInstance> mNodeInstances;
 
-    /** Rootnodes: Name of primary camera, if any */
+    /** Root-nodes: Name of primary camera, if any */
     std::string mPrimaryCamera;
 
     //! Constructor. Begin with a zero parent
-    Node() {
-        mParent = NULL;
+    Node()
+    : mParent( nullptr ){
+        // empty
     }
 
     //! Destructor: delete all children subsequently

+ 23 - 25
code/ColladaLoader.cpp

@@ -181,26 +181,27 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
     // ... then fill the materials with the now adjusted settings
     FillMaterials(parser, pScene);
 
-        // Apply unitsize scale calculation
-        pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0,  0,  0,
-                                                          0,  parser.mUnitSize,  0,  0,
-                                                          0,  0,  parser.mUnitSize,  0,
-                                                          0,  0,  0,  1);
-        if( !ignoreUpDirection ) {
-        // Convert to Y_UP, if different orientation
-        if( parser.mUpDirection == ColladaParser::UP_X)
-            pScene->mRootNode->mTransformation *= aiMatrix4x4(
-                 0, -1,  0,  0,
-                 1,  0,  0,  0,
-                 0,  0,  1,  0,
-                 0,  0,  0,  1);
-        else if( parser.mUpDirection == ColladaParser::UP_Z)
-            pScene->mRootNode->mTransformation *= aiMatrix4x4(
-                 1,  0,  0,  0,
-                 0,  0,  1,  0,
-                 0, -1,  0,  0,
-                 0,  0,  0,  1);
-        }
+    // Apply unitsize scale calculation
+    pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0,  0,  0,
+                                                        0,  parser.mUnitSize,  0,  0,
+                                                        0,  0,  parser.mUnitSize,  0,
+                                                        0,  0,  0,  1);
+    if( !ignoreUpDirection ) {
+    // Convert to Y_UP, if different orientation
+    if( parser.mUpDirection == ColladaParser::UP_X)
+        pScene->mRootNode->mTransformation *= aiMatrix4x4(
+                0, -1,  0,  0,
+                1,  0,  0,  0,
+                0,  0,  1,  0,
+                0,  0,  0,  1);
+    else if( parser.mUpDirection == ColladaParser::UP_Z)
+        pScene->mRootNode->mTransformation *= aiMatrix4x4(
+                1,  0,  0,  0,
+                0,  0,  1,  0,
+                0, -1,  0,  0,
+                0,  0,  0,  1);
+    }
+
     // store all meshes
     StoreSceneMeshes( pScene);
 
@@ -740,10 +741,6 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
     // create bones if given
     if( pSrcController && pSrcController->mType == Collada::Skin)
     {
-        // refuse if the vertex count does not match
-//      if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
-//          throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count");
-
         // resolve references - joint names
         const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource);
         const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource);
@@ -971,7 +968,8 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars
             for( size_t b = a+1; b < mAnims.size(); ++b)
             {
                 aiAnimation* other = mAnims[b];
-                if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond )
+                if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && 
+                        other->mTicksPerSecond == templateAnim->mTicksPerSecond )
                     collectedAnimIndices.push_back( b);
             }
 

+ 14 - 14
code/ColladaParser.cpp

@@ -68,7 +68,7 @@ using namespace Assimp::Formatter;
 // Constructor to be privately used by Importer
 ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
     : mFileName( pFile )
-    , mReader( NULL )
+    , mReader( nullptr )
     , mDataLibrary()
     , mAccessorLibrary()
     , mMeshLibrary()
@@ -79,20 +79,20 @@ ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
     , mLightLibrary()
     , mCameraLibrary()
     , mControllerLibrary()
-    , mRootNode( NULL )
+    , mRootNode( nullptr )
     , mAnims()
     , mUnitSize( 1.0f )
     , mUpDirection( UP_Y )
     , mFormat(FV_1_5_n )    // We assume the newest file format by default
 {
     // validate io-handler instance
-    if ( NULL == pIOHandler ) {
+    if (nullptr == pIOHandler ) {
         throw DeadlyImportError("IOSystem is NULL." );
     }
 
     // open the file
     std::unique_ptr<IOStream> file( pIOHandler->Open(pFile ) );
-    if (file.get() == NULL) {
+    if (file.get() == nullptr) {
         throw DeadlyImportError( "Failed to open file " + pFile + "." );
     }
 
@@ -363,17 +363,17 @@ void ColladaParser::ReadAnimationClipLibrary()
 
 void ColladaParser::PostProcessControllers()
 {
-  for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it)
-  {
-    std::string meshId = it->second.mMeshId;
-    ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
-    while(findItr != mControllerLibrary.end()) {
-      meshId = findItr->second.mMeshId;
-      findItr = mControllerLibrary.find(meshId);
-    }
+    std::string meshId;
+    for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) {
+        meshId = it->second.mMeshId;
+        ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
+        while(findItr != mControllerLibrary.end()) {
+            meshId = findItr->second.mMeshId;
+            findItr = mControllerLibrary.find(meshId);
+        }
     
-    it->second.mMeshId = meshId;
-  }
+        it->second.mMeshId = meshId;
+    }
 }
 
 // ------------------------------------------------------------------------------------------------

+ 22 - 9
code/ConvertToLHProcess.cpp

@@ -59,6 +59,25 @@ using namespace Assimp;
 
 #ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
 
+namespace {
+
+template <typename aiMeshType>
+void flipUVs(aiMeshType* pMesh) {
+    if (pMesh == nullptr) { return; }
+    // mirror texture y coordinate
+    for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) {
+        if (!pMesh->HasTextureCoords(tcIdx)) {
+            break;
+        }
+
+        for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) {
+            pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y;
+        }
+    }
+}
+
+} // namespace
+
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 MakeLeftHandedProcess::MakeLeftHandedProcess()
@@ -282,15 +301,9 @@ void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
 // Converts a single mesh
 void FlipUVsProcess::ProcessMesh( aiMesh* pMesh)
 {
-    // mirror texture y coordinate
-    for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)   {
-        if( !pMesh->HasTextureCoords( a ) ) {
-            break;
-        }
-
-        for( unsigned int b = 0; b < pMesh->mNumVertices; b++ ) {
-            pMesh->mTextureCoords[ a ][ b ].y = 1.0f - pMesh->mTextureCoords[ a ][ b ].y;
-        }
+    flipUVs(pMesh);
+    for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) {
+        flipUVs(pMesh->mAnimMeshes[idx]);
     }
 }
 

+ 4 - 4
code/Exporter.cpp

@@ -150,14 +150,14 @@ Exporter::ExportFormatEntry gExporters[] =
 #endif
 
 #ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
-    Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-    Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
     Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
         aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
     Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
         aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
+    Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
+        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
+    Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
+        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER

+ 252 - 266
code/FBXConverter.cpp

@@ -73,11 +73,7 @@ using namespace Util;
 
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
 
-// XXX vc9's debugger won't step into anonymous namespaces
-//namespace {
-
-
-Converter::Converter( aiScene* out, const Document& doc )
+FBXConverter::FBXConverter( aiScene* out, const Document& doc )
 : defaultMaterialIndex()
 , out( out )
 , doc( doc ) {
@@ -118,7 +114,7 @@ Converter::Converter( aiScene* out, const Document& doc )
 }
 
 
-Converter::~Converter() {
+FBXConverter::~FBXConverter() {
     std::for_each( meshes.begin(), meshes.end(), Util::delete_fun<aiMesh>() );
     std::for_each( materials.begin(), materials.end(), Util::delete_fun<aiMaterial>() );
     std::for_each( animations.begin(), animations.end(), Util::delete_fun<aiAnimation>() );
@@ -127,7 +123,7 @@ Converter::~Converter() {
     std::for_each( textures.begin(), textures.end(), Util::delete_fun<aiTexture>() );
 }
 
-void Converter::ConvertRootNode() {
+void FBXConverter::ConvertRootNode() {
     out->mRootNode = new aiNode();
     out->mRootNode->mName.Set( "RootNode" );
 
@@ -135,7 +131,7 @@ void Converter::ConvertRootNode() {
     ConvertNodes( 0L, *out->mRootNode );
 }
 
-void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform ) {
+void FBXConverter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform ) {
     const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced( id, "Model" );
 
     std::vector<aiNode*> nodes;
@@ -189,12 +185,12 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa
                 }
 
                 if ( !name_carrier ) {
-                    NodeNameCache::const_iterator it( std::find( mNodeNames.begin(), mNodeNames.end(), original_name ) );
+                    NodeNameCache::const_iterator it = mNodeNames.find(original_name);
                     if ( it != mNodeNames.end() ) {
                         original_name = original_name + std::string( "001" );
                     }
 
-                    mNodeNames.push_back( original_name );
+                    mNodeNames.insert( original_name );
                     nodes_chain.push_back( new aiNode( original_name ) );
                 } else {
                     original_name = nodes_chain.back()->mName.C_Str();
@@ -286,7 +282,7 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa
 }
 
 
-void Converter::ConvertLights( const Model& model, const std::string &orig_name ) {
+void FBXConverter::ConvertLights( const Model& model, const std::string &orig_name ) {
     const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
     for( const NodeAttribute* attr : node_attrs ) {
         const Light* const light = dynamic_cast<const Light*>( attr );
@@ -296,7 +292,7 @@ void Converter::ConvertLights( const Model& model, const std::string &orig_name
     }
 }
 
-void Converter::ConvertCameras( const Model& model, const std::string &orig_name ) {
+void FBXConverter::ConvertCameras( const Model& model, const std::string &orig_name ) {
     const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
     for( const NodeAttribute* attr : node_attrs ) {
         const Camera* const cam = dynamic_cast<const Camera*>( attr );
@@ -306,7 +302,7 @@ void Converter::ConvertCameras( const Model& model, const std::string &orig_name
     }
 }
 
-void Converter::ConvertLight( const Light& light, const std::string &orig_name ) {
+void FBXConverter::ConvertLight( const Light& light, const std::string &orig_name ) {
     lights.push_back( new aiLight() );
     aiLight* const out_light = lights.back();
 
@@ -383,7 +379,7 @@ void Converter::ConvertLight( const Light& light, const std::string &orig_name )
     }
 }
 
-void Converter::ConvertCamera( const Camera& cam, const std::string &orig_name )
+void FBXConverter::ConvertCamera( const Camera& cam, const std::string &orig_name )
 {
     cameras.push_back( new aiCamera() );
     aiCamera* const out_camera = cameras.back();
@@ -402,133 +398,120 @@ void Converter::ConvertCamera( const Camera& cam, const std::string &orig_name )
     out_camera->mClipPlaneFar = cam.FarPlane();
 }
 
-static bool HasName( NodeNameCache &cache, const std::string &name ) {
-    NodeNameCache::const_iterator it( std::find( cache.begin(), cache.end(), name ) );
-    return it != cache.end();
-
-}
-void Converter::GetUniqueName( const std::string &name, std::string uniqueName ) {
-    if ( !HasName( mNodeNames, name ) ) {
-        uniqueName = name;
-        return;
-    }
-
-    int i( 0 );
-    std::string newName;
-    while ( HasName( mNodeNames, newName ) ) {
+void FBXConverter::GetUniqueName( const std::string &name, std::string &uniqueName )
+{
+    int i = 0;
+    uniqueName = name;
+    while (mNodeNames.find(uniqueName) != mNodeNames.end())
+    {
         ++i;
-        newName.clear();
-        newName += name;
         std::stringstream ext;
-        ext << std::setfill( '0' ) << std::setw( 3 ) << i;
-        newName += ext.str();
+        ext << name << std::setfill('0') << std::setw(3) << i;
+        uniqueName = ext.str();
     }
-    uniqueName = newName;
-    mNodeNames.push_back( uniqueName );
+    mNodeNames.insert(uniqueName);
 }
 
 
-const char* Converter::NameTransformationComp( TransformationComp comp )
-{
-    switch ( comp )
-    {
-    case TransformationComp_Translation:
-        return "Translation";
-    case TransformationComp_RotationOffset:
-        return "RotationOffset";
-    case TransformationComp_RotationPivot:
-        return "RotationPivot";
-    case TransformationComp_PreRotation:
-        return "PreRotation";
-    case TransformationComp_Rotation:
-        return "Rotation";
-    case TransformationComp_PostRotation:
-        return "PostRotation";
-    case TransformationComp_RotationPivotInverse:
-        return "RotationPivotInverse";
-    case TransformationComp_ScalingOffset:
-        return "ScalingOffset";
-    case TransformationComp_ScalingPivot:
-        return "ScalingPivot";
-    case TransformationComp_Scaling:
-        return "Scaling";
-    case TransformationComp_ScalingPivotInverse:
-        return "ScalingPivotInverse";
-    case TransformationComp_GeometricScaling:
-        return "GeometricScaling";
-    case TransformationComp_GeometricRotation:
-        return "GeometricRotation";
-    case TransformationComp_GeometricTranslation:
-        return "GeometricTranslation";
-    case TransformationComp_GeometricScalingInverse:
-        return "GeometricScalingInverse";
-    case TransformationComp_GeometricRotationInverse:
-        return "GeometricRotationInverse";
-    case TransformationComp_GeometricTranslationInverse:
-        return "GeometricTranslationInverse";
-    case TransformationComp_MAXIMUM: // this is to silence compiler warnings
-    default:
-        break;
+const char* FBXConverter::NameTransformationComp( TransformationComp comp ) {
+    switch ( comp ) {
+        case TransformationComp_Translation:
+            return "Translation";
+        case TransformationComp_RotationOffset:
+            return "RotationOffset";
+        case TransformationComp_RotationPivot:
+            return "RotationPivot";
+        case TransformationComp_PreRotation:
+            return "PreRotation";
+        case TransformationComp_Rotation:
+            return "Rotation";
+        case TransformationComp_PostRotation:
+            return "PostRotation";
+        case TransformationComp_RotationPivotInverse:
+            return "RotationPivotInverse";
+        case TransformationComp_ScalingOffset:
+            return "ScalingOffset";
+        case TransformationComp_ScalingPivot:
+            return "ScalingPivot";
+        case TransformationComp_Scaling:
+            return "Scaling";
+        case TransformationComp_ScalingPivotInverse:
+            return "ScalingPivotInverse";
+        case TransformationComp_GeometricScaling:
+            return "GeometricScaling";
+        case TransformationComp_GeometricRotation:
+            return "GeometricRotation";
+        case TransformationComp_GeometricTranslation:
+            return "GeometricTranslation";
+        case TransformationComp_GeometricScalingInverse:
+            return "GeometricScalingInverse";
+        case TransformationComp_GeometricRotationInverse:
+            return "GeometricRotationInverse";
+        case TransformationComp_GeometricTranslationInverse:
+            return "GeometricTranslationInverse";
+        case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+        default:
+            break;
     }
 
     ai_assert( false );
-    return NULL;
+
+    return nullptr;
 }
 
-const char* Converter::NameTransformationCompProperty( TransformationComp comp )
-{
-    switch ( comp )
-    {
-    case TransformationComp_Translation:
-        return "Lcl Translation";
-    case TransformationComp_RotationOffset:
-        return "RotationOffset";
-    case TransformationComp_RotationPivot:
-        return "RotationPivot";
-    case TransformationComp_PreRotation:
-        return "PreRotation";
-    case TransformationComp_Rotation:
-        return "Lcl Rotation";
-    case TransformationComp_PostRotation:
-        return "PostRotation";
-    case TransformationComp_RotationPivotInverse:
-        return "RotationPivotInverse";
-    case TransformationComp_ScalingOffset:
-        return "ScalingOffset";
-    case TransformationComp_ScalingPivot:
-        return "ScalingPivot";
-    case TransformationComp_Scaling:
-        return "Lcl Scaling";
-    case TransformationComp_ScalingPivotInverse:
-        return "ScalingPivotInverse";
-    case TransformationComp_GeometricScaling:
-        return "GeometricScaling";
-    case TransformationComp_GeometricRotation:
-        return "GeometricRotation";
-    case TransformationComp_GeometricTranslation:
-        return "GeometricTranslation";
-    case TransformationComp_GeometricScalingInverse:
-        return "GeometricScalingInverse";
-    case TransformationComp_GeometricRotationInverse:
-        return "GeometricRotationInverse";
-    case TransformationComp_GeometricTranslationInverse:
-        return "GeometricTranslationInverse";
-    case TransformationComp_MAXIMUM: // this is to silence compiler warnings
-        break;
+const char* FBXConverter::NameTransformationCompProperty( TransformationComp comp ) {
+    switch ( comp ) {
+        case TransformationComp_Translation:
+            return "Lcl Translation";
+        case TransformationComp_RotationOffset:
+            return "RotationOffset";
+        case TransformationComp_RotationPivot:
+            return "RotationPivot";
+        case TransformationComp_PreRotation:
+            return "PreRotation";
+        case TransformationComp_Rotation:
+            return "Lcl Rotation";
+        case TransformationComp_PostRotation:
+            return "PostRotation";
+        case TransformationComp_RotationPivotInverse:
+            return "RotationPivotInverse";
+        case TransformationComp_ScalingOffset:
+            return "ScalingOffset";
+        case TransformationComp_ScalingPivot:
+            return "ScalingPivot";
+        case TransformationComp_Scaling:
+            return "Lcl Scaling";
+        case TransformationComp_ScalingPivotInverse:
+            return "ScalingPivotInverse";
+        case TransformationComp_GeometricScaling:
+            return "GeometricScaling";
+        case TransformationComp_GeometricRotation:
+            return "GeometricRotation";
+        case TransformationComp_GeometricTranslation:
+            return "GeometricTranslation";
+        case TransformationComp_GeometricScalingInverse:
+            return "GeometricScalingInverse";
+        case TransformationComp_GeometricRotationInverse:
+            return "GeometricRotationInverse";
+        case TransformationComp_GeometricTranslationInverse:
+            return "GeometricTranslationInverse";
+        case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+            break;
     }
 
     ai_assert( false );
-    return NULL;
+
+    return nullptr;
 }
 
-aiVector3D Converter::TransformationCompDefaultValue( TransformationComp comp )
+aiVector3D FBXConverter::TransformationCompDefaultValue( TransformationComp comp )
 {
     // XXX a neat way to solve the never-ending special cases for scaling
     // would be to do everything in log space!
     return comp == TransformationComp_Scaling ? aiVector3D( 1.f, 1.f, 1.f ) : aiVector3D();
 }
 
-void Converter::GetRotationMatrix( Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out )
+void FBXConverter::GetRotationMatrix( Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out )
 {
     if ( mode == Model::RotOrder_SphericXYZ ) {
         FBXImporter::LogError( "Unsupported RotationMode: SphericXYZ" );
@@ -599,11 +582,15 @@ void Converter::GetRotationMatrix( Model::RotOrder mode, const aiVector3D& rotat
 
     default:
         ai_assert( false );
+        break;
     }
 
-    ai_assert( ( order[ 0 ] >= 0 ) && ( order[ 0 ] <= 2 ) );
-    ai_assert( ( order[ 1 ] >= 0 ) && ( order[ 1 ] <= 2 ) );
-    ai_assert( ( order[ 2 ] >= 0 ) && ( order[ 2 ] <= 2 ) );
+    ai_assert( order[ 0 ] >= 0 );
+    ai_assert( order[ 0 ] <= 2 );
+    ai_assert( order[ 1 ] >= 0 );
+    ai_assert( order[ 1 ] <= 2 );
+    ai_assert( order[ 2 ] >= 0 );
+    ai_assert( order[ 2 ] <= 2 );
 
     if ( !is_id[ order[ 0 ] ] ) {
         out = temp[ order[ 0 ] ];
@@ -618,7 +605,7 @@ void Converter::GetRotationMatrix( Model::RotOrder mode, const aiVector3D& rotat
     }
 }
 
-bool Converter::NeedsComplexTransformationChain( const Model& model )
+bool FBXConverter::NeedsComplexTransformationChain( const Model& model )
 {
     const PropertyTable& props = model.Props();
     bool ok;
@@ -649,13 +636,13 @@ bool Converter::NeedsComplexTransformationChain( const Model& model )
     return false;
 }
 
-std::string Converter::NameTransformationChainNode( const std::string& name, TransformationComp comp )
+std::string FBXConverter::NameTransformationChainNode( const std::string& name, TransformationComp comp )
 {
     return name + std::string( MAGIC_NODE_TAG ) + "_" + NameTransformationComp( comp );
 }
 
-void Converter::GenerateTransformationNodeChain( const Model& model, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes )
-{
+void FBXConverter::GenerateTransformationNodeChain( const Model& model, std::vector<aiNode*>& output_nodes,
+        std::vector<aiNode*>& post_output_nodes ) {
     const PropertyTable& props = model.Props();
     const Model::RotOrder rot = model.RotationOrder();
 
@@ -826,7 +813,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector
     }
 }
 
-void Converter::SetupNodeMetadata( const Model& model, aiNode& nd )
+void FBXConverter::SetupNodeMetadata( const Model& model, aiNode& nd )
 {
     const PropertyTable& props = model.Props();
     DirectPropertyMap unparsedProperties = props.GetUnparsedProperties();
@@ -863,7 +850,7 @@ void Converter::SetupNodeMetadata( const Model& model, aiNode& nd )
     }
 }
 
-void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform )
+void FBXConverter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform )
 {
     const std::vector<const Geometry*>& geos = model.GetGeometry();
 
@@ -890,7 +877,7 @@ void Converter::ConvertModel( const Model& model, aiNode& nd, const aiMatrix4x4&
     }
 }
 
-std::vector<unsigned int> Converter::ConvertMesh( const MeshGeometry& mesh, const Model& model,
+std::vector<unsigned int> FBXConverter::ConvertMesh( const MeshGeometry& mesh, const Model& model,
     const aiMatrix4x4& node_global_transform, aiNode& nd)
 {
     std::vector<unsigned int> temp;
@@ -925,7 +912,7 @@ std::vector<unsigned int> Converter::ConvertMesh( const MeshGeometry& mesh, cons
     return temp;
 }
 
-aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh, aiNode& nd)
+aiMesh* FBXConverter::SetupEmptyMesh( const MeshGeometry& mesh, aiNode& nd)
 {
     aiMesh* const out_mesh = new aiMesh();
     meshes.push_back( out_mesh );
@@ -948,7 +935,7 @@ aiMesh* Converter::SetupEmptyMesh( const MeshGeometry& mesh, aiNode& nd)
     return out_mesh;
 }
 
-unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, const Model& model,
+unsigned int FBXConverter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, const Model& model,
     const aiMatrix4x4& node_global_transform, aiNode& nd)
 {
     const MatIndexArray& mindices = mesh.GetMaterialIndices();
@@ -1075,7 +1062,7 @@ unsigned int Converter::ConvertMeshSingleMaterial( const MeshGeometry& mesh, con
     return static_cast<unsigned int>( meshes.size() - 1 );
 }
 
-std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
+std::vector<unsigned int> FBXConverter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
     const aiMatrix4x4& node_global_transform, aiNode& nd)
 {
     const MatIndexArray& mindices = mesh.GetMaterialIndices();
@@ -1095,7 +1082,7 @@ std::vector<unsigned int> Converter::ConvertMeshMultiMaterial( const MeshGeometr
     return indices;
 }
 
-unsigned int Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
+unsigned int FBXConverter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, const Model& model,
     MatIndexArray::value_type index,
     const aiMatrix4x4& node_global_transform,
     aiNode& nd)
@@ -1271,7 +1258,7 @@ unsigned int Converter::ConvertMeshMultiMaterial( const MeshGeometry& mesh, cons
     return static_cast<unsigned int>( meshes.size() - 1 );
 }
 
-void Converter::ConvertWeights( aiMesh* out, const Model& model, const MeshGeometry& geo,
+void FBXConverter::ConvertWeights( aiMesh* out, const Model& model, const MeshGeometry& geo,
     const aiMatrix4x4& node_global_transform ,
     unsigned int materialIndex,
     std::vector<unsigned int>* outputVertStartIndices  )
@@ -1376,7 +1363,7 @@ void Converter::ConvertWeights( aiMesh* out, const Model& model, const MeshGeome
     std::swap_ranges( bones.begin(), bones.end(), out->mBones );
 }
 
-void Converter::ConvertCluster( std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
+void FBXConverter::ConvertCluster( std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
         std::vector<size_t>& out_indices,
         std::vector<size_t>& index_out_indices,
         std::vector<size_t>& count_out_indices,
@@ -1417,7 +1404,7 @@ void Converter::ConvertCluster( std::vector<aiBone*>& bones, const Model& /*mode
     }
 }
 
-void Converter::ConvertMaterialForMesh( aiMesh* out, const Model& model, const MeshGeometry& geo,
+void FBXConverter::ConvertMaterialForMesh( aiMesh* out, const Model& model, const MeshGeometry& geo,
     MatIndexArray::value_type materialIndex )
 {
     // locate source materials for this mesh
@@ -1439,7 +1426,7 @@ void Converter::ConvertMaterialForMesh( aiMesh* out, const Model& model, const M
     materials_converted[ mat ] = out->mMaterialIndex;
 }
 
-unsigned int Converter::GetDefaultMaterial()
+unsigned int FBXConverter::GetDefaultMaterial()
 {
     if ( defaultMaterialIndex ) {
         return defaultMaterialIndex - 1;
@@ -1461,7 +1448,7 @@ unsigned int Converter::GetDefaultMaterial()
 }
 
 
-unsigned int Converter::ConvertMaterial( const Material& material, const MeshGeometry* const mesh )
+unsigned int FBXConverter::ConvertMaterial( const Material& material, const MeshGeometry* const mesh )
 {
     const PropertyTable& props = material.Props();
 
@@ -1496,7 +1483,7 @@ unsigned int Converter::ConvertMaterial( const Material& material, const MeshGeo
     return static_cast<unsigned int>( materials.size() - 1 );
 }
 
-unsigned int Converter::ConvertVideo( const Video& video )
+unsigned int FBXConverter::ConvertVideo( const Video& video )
 {
     // generate empty output texture
     aiTexture* out_tex = new aiTexture();
@@ -1526,7 +1513,47 @@ unsigned int Converter::ConvertVideo( const Video& video )
     return static_cast<unsigned int>( textures.size() - 1 );
 }
 
-void Converter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap& textures,
+aiString FBXConverter::GetTexturePath(const Texture* tex)
+{
+    aiString path;
+    path.Set(tex->RelativeFilename());
+
+    const Video* media = tex->Media();
+    if (media != nullptr) {
+        bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
+        unsigned int index;
+
+        VideoMap::const_iterator it = textures_converted.find(media);
+        if (it != textures_converted.end()) {
+            index = (*it).second;
+            textureReady = true;
+        }
+        else {
+            if (media->ContentLength() > 0) {
+                index = ConvertVideo(*media);
+                textures_converted[media] = index;
+                textureReady = true;
+            }
+        }
+
+        // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready
+        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 through 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);
+            }
+        }
+    }
+
+    return path;
+}
+
+void FBXConverter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap& textures,
     const std::string& propName,
     aiTextureType target, const MeshGeometry* const mesh )
 {
@@ -1538,41 +1565,7 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap&
     const Texture* const tex = ( *it ).second;
     if ( tex != 0 )
     {
-        aiString path;
-        path.Set( tex->RelativeFilename() );
-
-        const Video* media = tex->Media();
-        if (media != 0) {
-			bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
-			unsigned int index;
-
-			VideoMap::const_iterator it = textures_converted.find(media);
-			if (it != textures_converted.end()) {
-				index = (*it).second;
-				textureReady = true;
-			}
-			else {
-				if (media->ContentLength() > 0) {
-					index = ConvertVideo(*media);
-					textures_converted[media] = index;
-					textureReady = true;
-				}
-			}
-
-			// setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready
-			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 through 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);
-                }
-			}
-		}  
-
+        aiString path = GetTexturePath(tex);
         out_mat->AddProperty( &path, _AI_MATKEY_TEXTURE_BASE, target, 0 );
 
         aiUVTransform uvTrafo;
@@ -1678,7 +1671,7 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap&
     }
 }
 
-void Converter::TrySetTextureProperties( aiMaterial* out_mat, const LayeredTextureMap& layeredTextures,
+void FBXConverter::TrySetTextureProperties( aiMaterial* out_mat, const LayeredTextureMap& layeredTextures,
         const std::string& propName,
         aiTextureType target, const MeshGeometry* const mesh ) {
     LayeredTextureMap::const_iterator it = layeredTextures.find( propName );
@@ -1696,9 +1689,7 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const LayeredTextu
     
         const Texture* const tex = ( *it ).second->getTexture(texIndex);
 
-        aiString path;
-        path.Set( tex->RelativeFilename() );
-
+        aiString path = GetTexturePath(tex);
         out_mat->AddProperty( &path, _AI_MATKEY_TEXTURE_BASE, target, texIndex );
 
         aiUVTransform uvTrafo;
@@ -1803,7 +1794,7 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const LayeredTextu
     }
 }
 
-void Converter::SetTextureProperties( aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh )
+void FBXConverter::SetTextureProperties( aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh )
 {
     TrySetTextureProperties( out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh );
     TrySetTextureProperties( out_mat, textures, "AmbientColor", aiTextureType_AMBIENT, mesh );
@@ -1818,7 +1809,7 @@ void Converter::SetTextureProperties( aiMaterial* out_mat, const TextureMap& tex
     TrySetTextureProperties( out_mat, textures, "ShininessExponent", aiTextureType_SHININESS, mesh );
 }
 
-void Converter::SetTextureProperties( aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh )
+void FBXConverter::SetTextureProperties( aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh )
 {
     TrySetTextureProperties( out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh );
     TrySetTextureProperties( out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh );
@@ -1833,7 +1824,7 @@ void Converter::SetTextureProperties( aiMaterial* out_mat, const LayeredTextureM
     TrySetTextureProperties( out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh );
 }
 
-aiColor3D Converter::GetColorPropertyFactored( const PropertyTable& props, const std::string& colorName,
+aiColor3D FBXConverter::GetColorPropertyFactored( const PropertyTable& props, const std::string& colorName,
     const std::string& factorName, bool& result, bool useTemplate )
 {
     result = true;
@@ -1858,13 +1849,13 @@ aiColor3D Converter::GetColorPropertyFactored( const PropertyTable& props, const
     return aiColor3D( BaseColor.x, BaseColor.y, BaseColor.z );
 }
 
-aiColor3D Converter::GetColorPropertyFromMaterial( const PropertyTable& props, const std::string& baseName,
+aiColor3D FBXConverter::GetColorPropertyFromMaterial( const PropertyTable& props, const std::string& baseName,
     bool& result )
 {
     return GetColorPropertyFactored( props, baseName + "Color", baseName + "Factor", result, true );
 }
 
-aiColor3D Converter::GetColorProperty( const PropertyTable& props, const std::string& colorName,
+aiColor3D FBXConverter::GetColorProperty( const PropertyTable& props, const std::string& colorName,
     bool& result, bool useTemplate )
 {
     result = true;
@@ -1877,7 +1868,7 @@ aiColor3D Converter::GetColorProperty( const PropertyTable& props, const std::st
     return aiColor3D( ColorVec.x, ColorVec.y, ColorVec.z );
 }
 
-void Converter::SetShadingPropertiesCommon( aiMaterial* out_mat, const PropertyTable& props )
+void FBXConverter::SetShadingPropertiesCommon( aiMaterial* out_mat, const PropertyTable& props )
 {
     // Set shading properties.
     // Modern FBX Files have two separate systems for defining these,
@@ -1976,60 +1967,60 @@ void Converter::SetShadingPropertiesCommon( aiMaterial* out_mat, const PropertyT
 }
 
 
-double Converter::FrameRateToDouble( FileGlobalSettings::FrameRate fp, double customFPSVal )
-{
+double FBXConverter::FrameRateToDouble( FileGlobalSettings::FrameRate fp, double customFPSVal ) {
     switch ( fp ) {
-    case FileGlobalSettings::FrameRate_DEFAULT:
-        return 1.0;
+        case FileGlobalSettings::FrameRate_DEFAULT:
+            return 1.0;
 
-    case FileGlobalSettings::FrameRate_120:
-        return 120.0;
+        case FileGlobalSettings::FrameRate_120:
+            return 120.0;
 
-    case FileGlobalSettings::FrameRate_100:
-        return 100.0;
+        case FileGlobalSettings::FrameRate_100:
+            return 100.0;
 
-    case FileGlobalSettings::FrameRate_60:
-        return 60.0;
+        case FileGlobalSettings::FrameRate_60:
+            return 60.0;
 
-    case FileGlobalSettings::FrameRate_50:
-        return 50.0;
+        case FileGlobalSettings::FrameRate_50:
+            return 50.0;
 
-    case FileGlobalSettings::FrameRate_48:
-        return 48.0;
+        case FileGlobalSettings::FrameRate_48:
+            return 48.0;
 
-    case FileGlobalSettings::FrameRate_30:
-    case FileGlobalSettings::FrameRate_30_DROP:
-        return 30.0;
+        case FileGlobalSettings::FrameRate_30:
+        case FileGlobalSettings::FrameRate_30_DROP:
+            return 30.0;
 
-    case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME:
-    case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME:
-        return 29.9700262;
+        case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME:
+        case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME:
+            return 29.9700262;
 
-    case FileGlobalSettings::FrameRate_PAL:
-        return 25.0;
+        case FileGlobalSettings::FrameRate_PAL:
+            return 25.0;
 
-    case FileGlobalSettings::FrameRate_CINEMA:
-        return 24.0;
+        case FileGlobalSettings::FrameRate_CINEMA:
+            return 24.0;
 
-    case FileGlobalSettings::FrameRate_1000:
-        return 1000.0;
+        case FileGlobalSettings::FrameRate_1000:
+            return 1000.0;
 
-    case FileGlobalSettings::FrameRate_CINEMA_ND:
-        return 23.976;
+        case FileGlobalSettings::FrameRate_CINEMA_ND:
+            return 23.976;
 
-    case FileGlobalSettings::FrameRate_CUSTOM:
-        return customFPSVal;
+        case FileGlobalSettings::FrameRate_CUSTOM:
+            return customFPSVal;
 
-    case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings
-        break;
+        case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings
+            break;
     }
 
     ai_assert( false );
+
     return -1.0f;
 }
 
 
-void Converter::ConvertAnimations()
+void FBXConverter::ConvertAnimations()
 {
     // first of all determine framerate
     const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode();
@@ -2042,7 +2033,7 @@ void Converter::ConvertAnimations()
     }
 }
 
-std::string Converter::FixNodeName( const std::string& name ) {
+std::string FBXConverter::FixNodeName( const std::string& name ) {
     // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if
     // this causes ambiguities, well possible between empty identifiers,
     // such as "Model::" and ""). Make sure the behaviour is consistent
@@ -2055,7 +2046,7 @@ std::string Converter::FixNodeName( const std::string& name ) {
     return name;
 }
 
-void Converter::ConvertAnimationStack( const AnimationStack& st )
+void FBXConverter::ConvertAnimationStack( const AnimationStack& st )
 {
     const AnimationLayerList& layers = st.Layers();
     if ( layers.empty() ) {
@@ -2197,7 +2188,7 @@ static void validateAnimCurveNodes( const std::vector<const AnimationCurveNode*>
 #endif // ASSIMP_BUILD_DEBUG
 
 // ------------------------------------------------------------------------------------------------
-void Converter::GenerateNodeAnimations( std::vector<aiNodeAnim*>& node_anims,
+void FBXConverter::GenerateNodeAnimations( std::vector<aiNodeAnim*>& node_anims,
     const std::string& fixed_name,
     const std::vector<const AnimationCurveNode*>& curves,
     const LayerMap& layer_map,
@@ -2431,10 +2422,9 @@ void Converter::GenerateNodeAnimations( std::vector<aiNodeAnim*>& node_anims,
     node_anim_chain_bits[ fixed_name ] = flags;
 }
 
-bool Converter::IsRedundantAnimationData( const Model& target,
-    TransformationComp comp,
-    const std::vector<const AnimationCurveNode*>& curves )
-{
+bool FBXConverter::IsRedundantAnimationData( const Model& target,
+        TransformationComp comp,
+        const std::vector<const AnimationCurveNode*>& curves ) {
     ai_assert( curves.size() );
 
     // look for animation nodes with
@@ -2477,7 +2467,7 @@ bool Converter::IsRedundantAnimationData( const Model& target,
 }
 
 
-aiNodeAnim* Converter::GenerateRotationNodeAnim( const std::string& name,
+aiNodeAnim* FBXConverter::GenerateRotationNodeAnim( const std::string& name,
     const Model& target,
     const std::vector<const AnimationCurveNode*>& curves,
     const LayerMap& layer_map,
@@ -2507,7 +2497,7 @@ aiNodeAnim* Converter::GenerateRotationNodeAnim( const std::string& name,
     return na.release();
 }
 
-aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name,
+aiNodeAnim* FBXConverter::GenerateScalingNodeAnim( const std::string& name,
     const Model& /*target*/,
     const std::vector<const AnimationCurveNode*>& curves,
     const LayerMap& layer_map,
@@ -2537,16 +2527,14 @@ aiNodeAnim* Converter::GenerateScalingNodeAnim( const std::string& name,
     return na.release();
 }
 
-
-aiNodeAnim* Converter::GenerateTranslationNodeAnim( const std::string& name,
-    const Model& /*target*/,
-    const std::vector<const AnimationCurveNode*>& curves,
-    const LayerMap& layer_map,
-    int64_t start, int64_t stop,
-    double& max_time,
-    double& min_time,
-    bool inverse )
-{
+aiNodeAnim* FBXConverter::GenerateTranslationNodeAnim( const std::string& name,
+        const Model& /*target*/,
+        const std::vector<const AnimationCurveNode*>& curves,
+        const LayerMap& layer_map,
+        int64_t start, int64_t stop,
+        double& max_time,
+        double& min_time,
+        bool inverse ) {
     std::unique_ptr<aiNodeAnim> na( new aiNodeAnim() );
     na->mNodeName.Set( name );
 
@@ -2575,7 +2563,7 @@ aiNodeAnim* Converter::GenerateTranslationNodeAnim( const std::string& name,
     return na.release();
 }
 
-aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
+aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim( const std::string& name,
     const Model& target,
     NodeMap::const_iterator chain[ TransformationComp_MAXIMUM ],
     NodeMap::const_iterator iter_end,
@@ -2711,7 +2699,7 @@ aiNodeAnim* Converter::GenerateSimpleNodeAnim( const std::string& name,
     return na.release();
 }
 
-Converter::KeyFrameListList Converter::GetKeyframeList( const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop )
+FBXConverter::KeyFrameListList FBXConverter::GetKeyframeList( const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop )
 {
     KeyFrameListList inputs;
     inputs.reserve( nodes.size() * 3 );
@@ -2767,12 +2755,11 @@ Converter::KeyFrameListList Converter::GetKeyframeList( const std::vector<const
 }
 
 
-KeyTimeList Converter::GetKeyTimeList( const KeyFrameListList& inputs )
-{
-    ai_assert( inputs.size() );
+KeyTimeList FBXConverter::GetKeyTimeList( const KeyFrameListList& inputs ) {
+    ai_assert( !inputs.empty() );
 
-    // reserve some space upfront - it is likely that the keyframe lists
-    // have matching time values, so max(of all keyframe lists) should
+    // reserve some space upfront - it is likely that the key-frame lists
+    // have matching time values, so max(of all key-frame lists) should
     // be a good estimate.
     KeyTimeList keys;
 
@@ -2816,17 +2803,15 @@ KeyTimeList Converter::GetKeyTimeList( const KeyFrameListList& inputs )
     return keys;
 }
 
-void Converter::InterpolateKeys( aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
-    const aiVector3D& def_value,
-    double& max_time,
-    double& min_time )
-
-{
-    ai_assert( keys.size() );
-    ai_assert( valOut );
+void FBXConverter::InterpolateKeys( aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
+        const aiVector3D& def_value,
+        double& max_time,
+        double& min_time ) {
+    ai_assert( !keys.empty() );
+    ai_assert( nullptr != valOut );
 
     std::vector<unsigned int> next_pos;
-    const size_t count = inputs.size();
+    const size_t count( inputs.size() );
 
     next_pos.resize( inputs.size(), 0 );
 
@@ -2837,6 +2822,9 @@ void Converter::InterpolateKeys( aiVectorKey* valOut, const KeyTimeList& keys, c
             const KeyFrameList& kfl = inputs[ i ];
 
             const size_t ksize = std::get<0>(kfl)->size();
+            if (ksize == 0) {
+                continue;
+            }
             if ( ksize > next_pos[ i ] && std::get<0>(kfl)->at( next_pos[ i ] ) == time ) {
                 ++next_pos[ i ];
             }
@@ -2871,14 +2859,14 @@ void Converter::InterpolateKeys( aiVectorKey* valOut, const KeyTimeList& keys, c
     }
 }
 
-void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
+void FBXConverter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
     const aiVector3D& def_value,
     double& maxTime,
     double& minTime,
     Model::RotOrder order )
 {
-    ai_assert( keys.size() );
-    ai_assert( valOut );
+    ai_assert( !keys.empty() );
+    ai_assert( nullptr != valOut );
 
     std::unique_ptr<aiVectorKey[]> temp( new aiVectorKey[ keys.size() ] );
     InterpolateKeys( temp.get(), keys, inputs, def_value, maxTime, minTime );
@@ -2909,7 +2897,7 @@ void Converter::InterpolateKeys( aiQuatKey* valOut, const KeyTimeList& keys, con
     }
 }
 
-void Converter::ConvertTransformOrder_TRStoSRT( aiQuatKey* out_quat, aiVectorKey* out_scale,
+void FBXConverter::ConvertTransformOrder_TRStoSRT( aiQuatKey* out_quat, aiVectorKey* out_scale,
     aiVectorKey* out_translation,
     const KeyFrameListList& scaling,
     const KeyFrameListList& translation,
@@ -2967,7 +2955,7 @@ void Converter::ConvertTransformOrder_TRStoSRT( aiQuatKey* out_quat, aiVectorKey
     }
 }
 
-aiQuaternion Converter::EulerToQuaternion( const aiVector3D& rot, Model::RotOrder order )
+aiQuaternion FBXConverter::EulerToQuaternion( const aiVector3D& rot, Model::RotOrder order )
 {
     aiMatrix4x4 m;
     GetRotationMatrix( order, rot, m );
@@ -2975,7 +2963,7 @@ aiQuaternion Converter::EulerToQuaternion( const aiVector3D& rot, Model::RotOrde
     return aiQuaternion( aiMatrix3x3( m ) );
 }
 
-void Converter::ConvertScaleKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/,
+void FBXConverter::ConvertScaleKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/,
     int64_t start, int64_t stop,
     double& maxTime,
     double& minTime )
@@ -2995,7 +2983,7 @@ void Converter::ConvertScaleKeys( aiNodeAnim* na, const std::vector<const Animat
         InterpolateKeys( na->mScalingKeys, keys, inputs, aiVector3D( 1.0f, 1.0f, 1.0f ), maxTime, minTime );
 }
 
-void Converter::ConvertTranslationKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
+void FBXConverter::ConvertTranslationKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
     const LayerMap& /*layers*/,
     int64_t start, int64_t stop,
     double& maxTime,
@@ -3013,7 +3001,7 @@ void Converter::ConvertTranslationKeys( aiNodeAnim* na, const std::vector<const
         InterpolateKeys( na->mPositionKeys, keys, inputs, aiVector3D( 0.0f, 0.0f, 0.0f ), maxTime, minTime );
 }
 
-void Converter::ConvertRotationKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
+void FBXConverter::ConvertRotationKeys( aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
     const LayerMap& /*layers*/,
     int64_t start, int64_t stop,
     double& maxTime,
@@ -3033,7 +3021,7 @@ void Converter::ConvertRotationKeys( aiNodeAnim* na, const std::vector<const Ani
     }
 }
 
-void Converter::ConvertGlobalSettings() {
+void FBXConverter::ConvertGlobalSettings() {
     if (nullptr == out) {
         return;
     }
@@ -3044,7 +3032,7 @@ void Converter::ConvertGlobalSettings() {
     out->mMetaData->Set(index, "UnitScaleFactor", unitScalFactor);
 }
 
-void Converter::TransferDataToScene()
+void FBXConverter::TransferDataToScene()
 {
     ai_assert( !out->mMeshes );
     ai_assert( !out->mNumMeshes );
@@ -3096,12 +3084,10 @@ void Converter::TransferDataToScene()
     }
 }
 
-//} // !anon
-
 // ------------------------------------------------------------------------------------------------
 void ConvertToAssimpScene(aiScene* out, const Document& doc)
 {
-    Converter converter(out,doc);
+    FBXConverter converter(out,doc);
 }
 
 } // !FBX

+ 9 - 5
code/FBXConverter.h

@@ -68,7 +68,7 @@ namespace FBX {
 
 class Document;
 
-using NodeNameCache = std::vector<std::string>;
+using NodeNameCache = std::set<std::string>;
 
 /** 
  *  Convert a FBX #Document to #aiScene
@@ -78,7 +78,7 @@ using NodeNameCache = std::vector<std::string>;
 void ConvertToAssimpScene(aiScene* out, const Document& doc);
 
 /** Dummy class to encapsulate the conversion process */
-class Converter {
+class FBXConverter {
 public:
     /**
     *  The different parts that make up the final local transformation of a fbx-node
@@ -106,8 +106,8 @@ public:
     };
 
 public:
-    Converter(aiScene* out, const Document& doc);
-    ~Converter();
+    FBXConverter(aiScene* out, const Document& doc);
+    ~FBXConverter();
 
 private:
     // ------------------------------------------------------------------------------------------------
@@ -131,7 +131,7 @@ private:
     void ConvertCamera( const Camera& cam, const std::string &orig_name );
 
     // ------------------------------------------------------------------------------------------------
-    void GetUniqueName( const std::string &name, std::string uniqueName );
+    void GetUniqueName( const std::string &name, std::string& uniqueName );
 
     // ------------------------------------------------------------------------------------------------
     // this returns unified names usable within assimp identifiers (i.e. no space characters -
@@ -228,6 +228,10 @@ private:
     // Video -> aiTexture
     unsigned int ConvertVideo(const Video& video);
 
+    // ------------------------------------------------------------------------------------------------
+    // convert embedded texture if necessary and return actual texture path
+    aiString GetTexturePath(const Texture* tex);
+
     // ------------------------------------------------------------------------------------------------
     void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
         const std::string& propName,

+ 1 - 1
code/FBXDocumentUtil.cpp

@@ -115,7 +115,7 @@ std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
         }
     }
 
-    if(!Properties70) {
+    if(!Properties70 || !Properties70->Compound()) {
         if(!no_warn) {
             DOMWarning("property table (Properties70) not found",&element);
         }

+ 7 - 3
code/FBXExporter.cpp

@@ -985,6 +985,10 @@ int64_t to_ktime(double ticks, const aiAnimation* anim) {
     return (static_cast<int64_t>(ticks) / static_cast<int64_t>(anim->mTicksPerSecond)) * FBX::SECOND;
 }
 
+int64_t to_ktime(double time) {
+    return (static_cast<int64_t>(time * FBX::SECOND));
+}
+
 void FBXExporter::WriteObjects ()
 {
     if (!binary) {
@@ -2089,7 +2093,7 @@ void FBXExporter::WriteObjects ()
             // position/translation
             for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) {
                 const aiVectorKey& k = na->mPositionKeys[ki];
-                times.push_back(to_ktime(k.mTime, anim));
+                times.push_back(to_ktime(k.mTime));
                 xval.push_back(k.mValue.x);
                 yval.push_back(k.mValue.y);
                 zval.push_back(k.mValue.z);
@@ -2103,7 +2107,7 @@ void FBXExporter::WriteObjects ()
             times.clear(); xval.clear(); yval.clear(); zval.clear();
             for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) {
                 const aiQuatKey& k = na->mRotationKeys[ki];
-                times.push_back(to_ktime(k.mTime, anim));
+                times.push_back(to_ktime(k.mTime));
                 // TODO: aiQuaternion method to convert to Euler...
                 aiMatrix4x4 m(k.mValue.GetMatrix());
                 aiVector3D qs, qr, qt;
@@ -2121,7 +2125,7 @@ void FBXExporter::WriteObjects ()
             times.clear(); xval.clear(); yval.clear(); zval.clear();
             for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) {
                 const aiVectorKey& k = na->mScalingKeys[ki];
-                times.push_back(to_ktime(k.mTime, anim));
+                times.push_back(to_ktime(k.mTime));
                 xval.push_back(k.mValue.x);
                 yval.push_back(k.mValue.y);
                 zval.push_back(k.mValue.z);

+ 3 - 0
code/FBXMeshGeometry.cpp

@@ -437,6 +437,9 @@ 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" && isDirect) {
+        if (!HasElement(source, indexDataElementName)) {
+            return;
+        }
         std::vector<T> tempData;
 		ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
 

+ 6 - 6
code/FileSystemFilter.h

@@ -67,7 +67,7 @@ public:
     FileSystemFilter(const std::string& file, IOSystem* old)
     : mWrapped  (old)
     , mSrc_file(file)
-    , sep(mWrapped->getOsSeparator()) {
+    , mSep(mWrapped->getOsSeparator()) {
         ai_assert(nullptr != mWrapped);
 
         // Determine base directory
@@ -116,7 +116,7 @@ public:
     // -------------------------------------------------------------------
     /** Returns the directory separator. */
     char getOsSeparator() const {
-        return sep;
+        return mSep;
     }
 
     // -------------------------------------------------------------------
@@ -256,7 +256,7 @@ private:
 
             while(true) {
                 tmp = mBase;
-                tmp += sep;
+                tmp += mSep;
 
                 std::string::size_type dirsep = in.rfind('/', last_dirsep);
                 if (std::string::npos == dirsep) {
@@ -298,7 +298,7 @@ private:
             in.erase(in.begin(),it+1);
         }
 
-        const char sep = getOsSeparator();
+        const char separator = getOsSeparator();
         for (it = in.begin(); it != in.end(); ++it) {
             // Exclude :// and \\, which remain untouched.
             // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
@@ -313,7 +313,7 @@ private:
 
             // Cleanup path delimiters
             if (*it == '/' || (*it) == '\\') {
-                *it = sep;
+                *it = separator;
 
                 // And we're removing double delimiters, frequent issue with
                 // incorrectly composited paths ...
@@ -337,7 +337,7 @@ private:
 private:
     IOSystem *mWrapped;
     std::string mSrc_file, mBase;
-    char sep;
+    char mSep;
 };
 
 } //!ns Assimp

+ 4 - 2
code/GenFaceNormalsProcess.cpp

@@ -73,6 +73,7 @@ GenFaceNormalsProcess::~GenFaceNormalsProcess()
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
 bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const {
+    force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
     return  (pFlags & aiProcess_GenNormals) != 0;
 }
 
@@ -105,7 +106,8 @@ void GenFaceNormalsProcess::Execute( aiScene* pScene) {
 bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
 {
     if (NULL != pMesh->mNormals) {
-        return false;
+        if (force_) delete[] pMesh->mNormals;
+        else return false;
     }
 
     // If the mesh consists of lines and/or points but not of
@@ -134,7 +136,7 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
         const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
         const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
         const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
-        const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize();
+        const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
 
         for (unsigned int i = 0;i < face.mNumIndices;++i) {
             pMesh->mNormals[face.mIndices[i]] = vNor;

+ 2 - 1
code/GenFaceNormalsProcess.h

@@ -78,7 +78,8 @@ public:
 
 
 private:
-    bool GenMeshFaceNormals (aiMesh* pcMesh);
+    bool GenMeshFaceNormals(aiMesh* pcMesh);
+    mutable bool force_ = false;
 };
 
 } // end of namespace Assimp

+ 5 - 2
code/GenVertexNormalsProcess.cpp

@@ -72,6 +72,7 @@ GenVertexNormalsProcess::~GenVertexNormalsProcess() {
 // Returns whether the processing step is present in the given flag field.
 bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
 {
+    force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
     return (pFlags & aiProcess_GenSmoothNormals) != 0;
 }
 
@@ -113,8 +114,10 @@ void GenVertexNormalsProcess::Execute( aiScene* pScene)
 // Executes the post processing step on the given imported data.
 bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex)
 {
-    if (NULL != pMesh->mNormals)
-        return false;
+    if (NULL != pMesh->mNormals) {
+        if (force_) delete[] pMesh->mNormals;
+        else return false;
+    }
 
     // If the mesh consists of lines and/or points but not of
     // triangles or higher-order polygons the normal vectors

+ 1 - 0
code/GenVertexNormalsProcess.h

@@ -107,6 +107,7 @@ private:
 
     /** Configuration option: maximum smoothing angle, in radians*/
     ai_real configMaxAngle;
+    mutable bool force_ = false;
 };
 
 } // end of namespace Assimp

+ 10 - 8
code/Importer.cpp

@@ -178,6 +178,7 @@ Importer::~Importer()
 {
     // Delete all import plugins
 	DeleteImporterInstanceList(pimpl->mImporter);
+    aiReleaseDefaultMaterial();
 
     // Delete all post-processing plug-ins
     for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)
@@ -383,6 +384,8 @@ bool _ValidateFlags(unsigned int pFlags)
 void Importer::FreeScene( )
 {
     ASSIMP_BEGIN_EXCEPTION_REGION();
+
+    aiReleaseDefaultMaterial();
     delete pimpl->mScene;
     pimpl->mScene = NULL;
 
@@ -490,9 +493,9 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
     SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength));
 
     // read the file and recover the previous IOSystem
-    static const size_t BufferSize(Importer::MaxLenHint + 28);
-    char fbuff[ BufferSize ];
-    ai_snprintf(fbuff, BufferSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint);
+    static const size_t BufSize(Importer::MaxLenHint + 28);
+    char fbuff[BufSize];
+    ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint);
 
     ReadFile(fbuff,pFlags);
     SetIOHandler(io);
@@ -930,20 +933,19 @@ BaseImporter* Importer::GetImporter (const char* szExtension) const
 
 // ------------------------------------------------------------------------------------------------
 // Find a loader plugin for a given file extension
-size_t Importer::GetImporterIndex (const char* szExtension) const
-{
-    ai_assert(szExtension);
+size_t Importer::GetImporterIndex (const char* szExtension) const {
+    ai_assert(nullptr != szExtension);
 
     ASSIMP_BEGIN_EXCEPTION_REGION();
 
     // skip over wildcard and dot characters at string head --
-    for(;*szExtension == '*' || *szExtension == '.'; ++szExtension);
+    for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension );
 
     std::string ext(szExtension);
     if (ext.empty()) {
         return static_cast<size_t>(-1);
     }
-    std::transform(ext.begin(),ext.end(), ext.begin(), tolower);
+    std::transform( ext.begin(), ext.end(), ext.begin(), ToLower<char> );
 
     std::set<std::string> str;
     for (std::vector<BaseImporter*>::const_iterator i =  pimpl->mImporter.begin();i != pimpl->mImporter.end();++i)  {

+ 1 - 1
code/Importer/IFC/IFCGeometry.cpp

@@ -317,7 +317,7 @@ void ProcessRevolvedAreaSolid(const Schema_2x3::IfcRevolvedAreaSolid& solid, Tem
 }
 
 // ------------------------------------------------------------------------------------------------
-void ProcessSweptDiskSolid(const Schema_2x3::IfcSweptDiskSolid solid, TempMesh& result, ConversionData& conv)
+void ProcessSweptDiskSolid(const Schema_2x3::IfcSweptDiskSolid &solid, TempMesh& result, ConversionData& conv)
 {
     const Curve* const curve = Curve::Convert(*solid.Directrix, conv);
     if(!curve) {

+ 18 - 0
code/JoinVerticesProcess.cpp

@@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Vertex.h>
 #include <assimp/TinyFormatter.h>
 #include <stdio.h>
+#include <unordered_set>
 
 using namespace Assimp;
 // ------------------------------------------------------------------------------------------------
@@ -239,6 +240,19 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
         return 0;
     }
 
+    // We should care only about used vertices, not all of them
+    // (this can happen due to original file vertices buffer being used by
+    // multiple meshes)
+    std::unordered_set<unsigned int> usedVertexIndices;
+    usedVertexIndices.reserve(pMesh->mNumVertices);
+    for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+    {
+        aiFace& face = pMesh->mFaces[a];
+        for( unsigned int b = 0; b < face.mNumIndices; b++) {
+            usedVertexIndices.insert(face.mIndices[b]);
+        }
+    }
+
     // We'll never have more vertices afterwards.
     std::vector<Vertex> uniqueVertices;
     uniqueVertices.reserve( pMesh->mNumVertices);
@@ -292,6 +306,10 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
 
     // Now check each vertex if it brings something new to the table
     for( unsigned int a = 0; a < pMesh->mNumVertices; a++)  {
+        if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
+            continue;
+        }
+
         // collect the vertex data
         Vertex v(pMesh,a);
 

+ 4 - 2
code/LWOMaterial.cpp

@@ -253,7 +253,8 @@ bool LWOImporter::HandleTextures(aiMaterial* pcMat, const TextureList& in, aiTex
         pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_TEXOP(type,cur));
 
         // setup the mapping mode
-        pcMat->AddProperty<int>((int*)&mapping,1,AI_MATKEY_MAPPING(type,cur));
+        int mapping_ = static_cast<int>(mapping);
+        pcMat->AddProperty<int>(&mapping_, 1, AI_MATKEY_MAPPING(type, cur));
 
         // add the u-wrapping
         temp = (unsigned int)GetMapMode(texture.wrapModeWidth);
@@ -365,7 +366,8 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,aiMaterial* pcMat)
     }
     if (surf.mMaximumSmoothAngle <= 0.0)
         m = aiShadingMode_Flat;
-    pcMat->AddProperty((int*)&m,1,AI_MATKEY_SHADING_MODEL);
+    int m_ = static_cast<int>(m);
+    pcMat->AddProperty(&m_, 1, AI_MATKEY_SHADING_MODEL);
 
     // (the diffuse value is just a scaling factor)
     // If a diffuse texture is set, we set this value to 1.0

+ 9 - 37
code/MMDPmxParser.cpp

@@ -41,6 +41,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 #include <utility>
 #include "MMDPmxParser.h"
+#include <assimp/StringUtils.h>
 #include "../contrib/utf8cpp/source/utf8.h"
 #include <assimp/Exceptional.h>
 
@@ -118,7 +119,7 @@ namespace pmx
 		stream->read((char*) &count, sizeof(uint8_t));
 		if (count < 8)
 		{
-			throw;
+			throw DeadlyImportError("MMD: invalid size");
 		}
 		stream->read((char*) &encoding, sizeof(uint8_t));
 		stream->read((char*) &uv, sizeof(uint8_t));
@@ -395,7 +396,7 @@ namespace pmx
 			}
 			break;
 		default:
-			throw;
+            throw DeadlyImportError("MMD: unknown morth type");
 		}
 	}
 
@@ -476,8 +477,8 @@ namespace pmx
 	{
 		// 未実装
 		std::cerr << "Not Implemented Exception" << std::endl;
-		throw;
-	}
+        throw DeadlyImportError("MMD: Not Implemented Exception");
+    }
 
 	void PmxModel::Init()
 	{
@@ -516,15 +517,15 @@ namespace pmx
 		if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20)
 		{
 			std::cerr << "invalid magic number." << std::endl;
-			throw;
-		}
+            throw DeadlyImportError("MMD: invalid magic number.");
+        }
 		// バージョン
 		stream->read((char*) &version, sizeof(float));
 		if (version != 2.0f && version != 2.1f)
 		{
 			std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
-			throw;
-		}
+            throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version));
+        }
 		// ファイル設定
 		this->setting.Read(stream);
 
@@ -605,34 +606,5 @@ namespace pmx
 		{
 			this->joints[i].Read(stream, &setting);
 		}
-
-		//if (this->version == 2.1f)
-		//{
-		//	stream->read((char*) &this->soft_body_count, sizeof(int));
-		//	this->soft_bodies = mmd::make_unique<PmxSoftBody []>(this->soft_body_count);
-		//	for (int i = 0; i < this->soft_body_count; i++)
-		//	{
-		//		this->soft_bodies[i].Read(stream, &setting);
-		//	}
-		//}
 	}
-
-	//std::unique_ptr<PmxModel> ReadFromFile(const char *filename)
-	//{
-	//	auto stream = std::ifstream(filename, std::ios_base::binary);
-	//	auto pmx = PmxModel::ReadFromStream(&stream);
-	//	if (!stream.eof())
-	//	{
-	//		std::cerr << "don't reach the end of file." << std::endl;
-	//	}
-	//	stream.close();
-	//	return pmx;
-	//}
-
-	//std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream)
-	//{
-	//	auto pmx = mmd::make_unique<PmxModel>();
-	//	pmx->Read(stream);
-	//	return pmx;
-	//}
 }

+ 59 - 26
code/MaterialSystem.cpp

@@ -63,9 +63,9 @@ aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
     unsigned int index,
     const aiMaterialProperty** pPropOut)
 {
-    ai_assert (pMat != NULL);
-    ai_assert (pKey != NULL);
-    ai_assert (pPropOut != NULL);
+    ai_assert( pMat != NULL );
+    ai_assert( pKey != NULL );
+    ai_assert( pPropOut != NULL );
 
     /*  Just search for a property with exactly this name ..
      *  could be improved by hashing, but it's possibly
@@ -76,7 +76,7 @@ aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
 
         if (prop /* just for safety ... */
             && 0 == strcmp( prop->mKey.data, pKey )
-            && (UINT_MAX == type  || prop->mSemantic == type) /* UINT_MAX is a wildcard, but this is undocumented :-) */
+            && (UINT_MAX == type  || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */
             && (UINT_MAX == index || prop->mIndex == index))
         {
             *pPropOut = pMat->mProperties[i];
@@ -96,8 +96,8 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
     ai_real* pOut,
     unsigned int* pMax)
 {
-    ai_assert (pOut != NULL);
-    ai_assert (pMat != NULL);
+    ai_assert( pOut != NULL );
+    ai_assert( pMat != NULL );
 
     const aiMaterialProperty* prop;
     aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop);
@@ -182,8 +182,8 @@ aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat,
     int* pOut,
     unsigned int* pMax)
 {
-    ai_assert (pOut != NULL);
-    ai_assert (pMat != NULL);
+    ai_assert( pOut != NULL );
+    ai_assert( pMat != NULL );
 
     const aiMaterialProperty* prop;
     aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**) &prop);
@@ -274,7 +274,7 @@ aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat,
     aiUVTransform* pOut)
 {
     unsigned int iMax = 4;
-    return  aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax);
+    return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -314,7 +314,7 @@ aiReturn aiGetMaterialString(const aiMaterial* pMat,
 
 // ------------------------------------------------------------------------------------------------
 // Get the number of textures on a particular texture stack
-ASSIMP_API unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat,
+unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat,
     C_ENUM aiTextureType type)
 {
     ai_assert (pMat != NULL);
@@ -347,15 +347,18 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat,
     unsigned int* flags         /*= NULL*/
     )
 {
-    ai_assert(NULL != mat && NULL != path);
+    ai_assert( NULL != mat );
+    ai_assert( NULL != path );
 
     // Get the path to the texture
     if (AI_SUCCESS != aiGetMaterialString(mat,AI_MATKEY_TEXTURE(type,index),path))  {
         return AI_FAILURE;
     }
+
     // Determine mapping type
-    aiTextureMapping mapping = aiTextureMapping_UV;
-    aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index),(int*)&mapping);
+    int mapping_ = static_cast<int>(aiTextureMapping_UV);
+    aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index), &mapping_);
+    aiTextureMapping mapping = static_cast<aiTextureMapping>(mapping_);
     if (_mapping)
         *_mapping = mapping;
 
@@ -380,15 +383,37 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat,
     if (flags){
         aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags);
     }
+
     return AI_SUCCESS;
 }
 
+static aiMaterial *DefaultMaterial = nullptr;
+
+// ------------------------------------------------------------------------------------------------
+// Will return the default material.
+aiMaterial *aiCreateAndRegisterDefaultMaterial() {
+    if (nullptr == DefaultMaterial) {
+        DefaultMaterial = new aiMaterial;
+        aiString s;
+        s.Set(AI_DEFAULT_MATERIAL_NAME);
+        DefaultMaterial->AddProperty(&s, AI_MATKEY_NAME);
+    }
+
+    return DefaultMaterial;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Will return the default material.
+void aiReleaseDefaultMaterial() {
+    DefaultMaterial = nullptr;
+}
+
 static const unsigned int DefaultNumAllocated = 5;
 
 // ------------------------------------------------------------------------------------------------
 // Construction. Actually the one and only way to get an aiMaterial instance
 aiMaterial::aiMaterial() 
-: mProperties( NULL )
+: mProperties( nullptr )
 , mNumProperties( 0 )
 , mNumAllocated( DefaultNumAllocated ) {
     // Allocate 5 entries by default
@@ -403,12 +428,20 @@ aiMaterial::~aiMaterial()
     delete[] mProperties;
 }
 
+// ------------------------------------------------------------------------------------------------
+aiString aiMaterial::GetName() {
+    aiString name;
+    Get(AI_MATKEY_NAME, name);
+
+    return name;
+}
+
 // ------------------------------------------------------------------------------------------------
 void aiMaterial::Clear()
 {
-    for (unsigned int i = 0; i < mNumProperties;++i)    {
+    for ( unsigned int i = 0; i < mNumProperties; ++i )    {
         // delete this entry
-        delete mProperties[i];
+        delete mProperties[ i ];
         AI_DEBUG_INVALIDATE_PTR(mProperties[i]);
     }
     mNumProperties = 0;
@@ -417,11 +450,9 @@ void aiMaterial::Clear()
 }
 
 // ------------------------------------------------------------------------------------------------
-aiReturn aiMaterial::RemoveProperty (const char* pKey,unsigned int type,
-    unsigned int index
-    )
+aiReturn aiMaterial::RemoveProperty ( const char* pKey,unsigned int type, unsigned int index )
 {
-    ai_assert(NULL != pKey);
+    ai_assert( nullptr != pKey );
 
     for (unsigned int i = 0; i < mNumProperties;++i) {
         aiMaterialProperty* prop = mProperties[i];
@@ -453,17 +484,18 @@ aiReturn aiMaterial::AddBinaryProperty (const void* pInput,
     aiPropertyTypeInfo pType
     )
 {
-    ai_assert (pInput != NULL);
-    ai_assert (pKey != NULL);
-    ai_assert (0 != pSizeInBytes);
+    ai_assert( pInput != NULL );
+    ai_assert( pKey != NULL );
+    ai_assert( 0 != pSizeInBytes );
 
     if ( 0 == pSizeInBytes ) {
 
     }
+
     // first search the list whether there is already an entry with this key
-    unsigned int iOutIndex = UINT_MAX;
-    for (unsigned int i = 0; i < mNumProperties;++i)    {
-        aiMaterialProperty* prop = mProperties[i];
+    unsigned int iOutIndex( UINT_MAX );
+    for ( unsigned int i = 0; i < mNumProperties; ++i ) {
+        aiMaterialProperty *prop( mProperties[ i ] );
 
         if (prop /* just for safety */ && !strcmp( prop->mKey.data, pKey ) &&
             prop->mSemantic == type && prop->mIndex == index){
@@ -515,6 +547,7 @@ aiReturn aiMaterial::AddBinaryProperty (const void* pInput,
     }
     // push back ...
     mProperties[mNumProperties++] = pcNew;
+
     return AI_SUCCESS;
 }
 

+ 68 - 32
code/ObjFileImporter.cpp

@@ -76,41 +76,36 @@ using namespace std;
 
 // ------------------------------------------------------------------------------------------------
 //  Default constructor
-ObjFileImporter::ObjFileImporter() :
-    m_Buffer(),
-    m_pRootObject( NULL ),
-    m_strAbsPath( "" )
-{
+ObjFileImporter::ObjFileImporter()
+: m_Buffer()
+, m_pRootObject( nullptr )
+, m_strAbsPath( "" ) {
     DefaultIOSystem io;
     m_strAbsPath = io.getOsSeparator();
 }
 
 // ------------------------------------------------------------------------------------------------
 //  Destructor.
-ObjFileImporter::~ObjFileImporter()
-{
+ObjFileImporter::~ObjFileImporter() {
     delete m_pRootObject;
-    m_pRootObject = NULL;
+    m_pRootObject = nullptr;
 }
 
 // ------------------------------------------------------------------------------------------------
 //  Returns true, if file is an obj file.
-bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem*  pIOHandler , bool checkSig ) const
-{
-    if(!checkSig) //Check File Extension
-    {
+bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem*  pIOHandler , bool checkSig ) const {
+    if(!checkSig)  {
+        //Check File Extension
         return SimpleExtensionCheck(pFile,"obj");
-    }
-    else //Check file Header
-    {
+    } else {
+        // Check file Header
         static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
-        return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9 );
+        return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9, 200, false, true );
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-const aiImporterDesc* ObjFileImporter::GetInfo () const
-{
+const aiImporterDesc* ObjFileImporter::GetInfo () const {
     return &desc;
 }
 
@@ -215,22 +210,63 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene
         ai_assert(false);
     }
 
-    // Create nodes for the whole scene
-    std::vector<aiMesh*> MeshArray;
-    for (size_t index = 0; index < pModel->m_Objects.size(); ++index ) {
-        createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray);
-    }
+    if (pModel->m_Objects.size() > 0) {
+        // Create nodes for the whole scene
+        std::vector<aiMesh*> MeshArray;
+        for (size_t index = 0; index < pModel->m_Objects.size(); ++index) {
+            createNodes(pModel, pModel->m_Objects[index], pScene->mRootNode, pScene, MeshArray);
+        }
 
-    // Create mesh pointer buffer for this scene
-    if (pScene->mNumMeshes > 0) {
-        pScene->mMeshes = new aiMesh*[ MeshArray.size() ];
-        for (size_t index =0; index < MeshArray.size(); ++index ) {
-            pScene->mMeshes[ index ] = MeshArray[ index ];
+        // Create mesh pointer buffer for this scene
+        if (pScene->mNumMeshes > 0) {
+            pScene->mMeshes = new aiMesh*[MeshArray.size()];
+            for (size_t index = 0; index < MeshArray.size(); ++index) {
+                pScene->mMeshes[index] = MeshArray[index];
+            }
+        }
+
+        // Create all materials
+        createMaterials(pModel, pScene);
+    }else {
+		if (pModel->m_Vertices.empty()){
+			return;
+		}
+
+		std::unique_ptr<aiMesh> mesh( new aiMesh );
+        mesh->mPrimitiveTypes = aiPrimitiveType_POINT;
+        unsigned int n = pModel->m_Vertices.size();
+        mesh->mNumVertices = n;
+
+        mesh->mVertices = new aiVector3D[n];
+        memcpy(mesh->mVertices, pModel->m_Vertices.data(), n*sizeof(aiVector3D) );
+
+        if ( !pModel->m_Normals.empty() ) {
+            mesh->mNormals = new aiVector3D[n];
+            if (pModel->m_Normals.size() < n) {
+                throw DeadlyImportError("OBJ: vertex normal index out of range");
+            }
+            memcpy(mesh->mNormals, pModel->m_Normals.data(), n*sizeof(aiVector3D));
         }
-    }
 
-    // Create all materials
-    createMaterials( pModel, pScene );
+        if ( !pModel->m_VertexColors.empty() ){
+            mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
+            for (unsigned int i = 0; i < n; ++i) {
+                if (i < pModel->m_VertexColors.size() ) {
+                    const aiVector3D& color = pModel->m_VertexColors[i];
+                    mesh->mColors[0][i] = aiColor4D(color.x, color.y, color.z, 1.0);
+                }else {
+                    throw DeadlyImportError("OBJ: vertex color index out of range");
+                }
+            }
+        }
+
+        pScene->mRootNode->mNumMeshes = 1;
+        pScene->mRootNode->mMeshes = new unsigned int[1];
+        pScene->mRootNode->mMeshes[0] = 0;
+        pScene->mMeshes = new aiMesh*[1];
+        pScene->mNumMeshes = 1;
+        pScene->mMeshes[0] = mesh.release();
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -457,7 +493,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
             // Copy all vertex colors
             if ( !pModel->m_VertexColors.empty())
             {
-                const aiVector3D color = pModel->m_VertexColors[ vertex ];
+                const aiVector3D& color = pModel->m_VertexColors[ vertex ];
                 pMesh->mColors[0][ newIndex ] = aiColor4D(color.x, color.y, color.z, 1.0);
             }
 

+ 5 - 7
code/ObjFileMtlImporter.cpp

@@ -84,8 +84,6 @@ static const std::string BumpOption         = "-bm";
 static const std::string ChannelOption      = "-imfchan";
 static const std::string TypeOption         = "-type";
 
-
-
 // -------------------------------------------------------------------
 //  Constructor
 ObjFileMtlImporter::ObjFileMtlImporter( std::vector<char> &buffer,
@@ -334,6 +332,11 @@ void ObjFileMtlImporter::getTexture() {
         // Specular texture
         out = & m_pModel->m_pCurrentMaterial->textureSpecular;
         clampIndex = ObjFile::Material::TextureSpecularType;
+    } else if ( !ASSIMP_strincmp( pPtr, DisplacementTexture1.c_str(), static_cast<unsigned int>(DisplacementTexture1.size()) ) ||
+                !ASSIMP_strincmp( pPtr, DisplacementTexture2.c_str(), static_cast<unsigned int>(DisplacementTexture2.size()) ) ) {
+        // Displacement texture
+        out = &m_pModel->m_pCurrentMaterial->textureDisp;
+        clampIndex = ObjFile::Material::TextureDispType;
     } else if ( !ASSIMP_strincmp( pPtr, OpacityTexture.c_str(), static_cast<unsigned int>(OpacityTexture.size()) ) ) {
         // Opacity texture
         out = & m_pModel->m_pCurrentMaterial->textureOpacity;
@@ -356,11 +359,6 @@ void ObjFileMtlImporter::getTexture() {
         // Reflection texture(s)
         //Do nothing here
         return;
-    } else if ( !ASSIMP_strincmp( pPtr, DisplacementTexture1.c_str(), static_cast<unsigned int>(DisplacementTexture1.size()) ) ||
-                !ASSIMP_strincmp( pPtr, DisplacementTexture2.c_str(), static_cast<unsigned int>(DisplacementTexture2.size()) ) ) {
-        // Displacement texture
-        out = &m_pModel->m_pCurrentMaterial->textureDisp;
-        clampIndex = ObjFile::Material::TextureDispType;
     } else if ( !ASSIMP_strincmp( pPtr, SpecularityTexture.c_str(), static_cast<unsigned int>(SpecularityTexture.size()) ) ) {
         // Specularity scaling (glossiness)
         out = & m_pModel->m_pCurrentMaterial->textureSpecularity;

+ 200 - 222
code/PlyLoader.cpp

@@ -55,7 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/scene.h>
 #include <assimp/importerdesc.h>
 
-using namespace Assimp;
+using namespace ::Assimp;
 
 static const aiImporterDesc desc = {
     "Stanford Polygon Library (PLY) Importer",
@@ -73,22 +73,20 @@ static const aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 // Internal stuff
-namespace
-{
-  // ------------------------------------------------------------------------------------------------
-  // Checks that property index is within range
-  template <class T>
-  const T &GetProperty(const std::vector<T> &props, int idx)
-  {
-    if (static_cast<size_t>(idx) >= props.size()) {
-      throw DeadlyImportError("Invalid .ply file: Property index is out of range.");
-    }
+namespace {
+    // ------------------------------------------------------------------------------------------------
+    // Checks that property index is within range
+    template <class T>
+    inline
+    const T &GetProperty(const std::vector<T> &props, int idx) {
+        if (static_cast<size_t>(idx) >= props.size()) {
+            throw DeadlyImportError("Invalid .ply file: Property index is out of range.");
+        }
 
-    return props[idx];
-  }
+        return props[idx];
+    }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 PLYImporter::PLYImporter()
@@ -129,7 +127,7 @@ const aiImporterDesc* PLYImporter::GetInfo() const {
 
 // ------------------------------------------------------------------------------------------------
 static bool isBigEndian(const char* szMe) {
-    ai_assert(NULL != szMe);
+    ai_assert(nullptr != szMe);
 
     // binary_little_endian
     // binary_big_endian
@@ -150,7 +148,7 @@ static bool isBigEndian(const char* szMe) {
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
-    static const std::string mode = "rb";
+    const std::string mode = "rb";
     std::unique_ptr<IOStream> fileStream(pIOHandler->Open(pFile, mode));
     if (!fileStream.get()) {
         throw DeadlyImportError("Failed to open file " + pFile + ".");
@@ -184,7 +182,7 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
     char* szMe = (char*)&this->mBuffer[0];
     SkipSpacesAndLineEnd(szMe, (const char**)&szMe);
 
-    // determine the format of the file data and construct the aimesh
+    // determine the format of the file data and construct the aiMesh
     PLY::DOM sPlyDom;   
     this->pcDOM = &sPlyDom;
 
@@ -192,7 +190,7 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
         if (TokenMatch(szMe, "ascii", 5)) {
             SkipLine(szMe, (const char**)&szMe);
             if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) {
-                if (mGeneratedMesh != NULL) {
+                if (mGeneratedMesh != nullptr) {
                     delete(mGeneratedMesh);
                     mGeneratedMesh = nullptr;
                 }
@@ -206,7 +204,7 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
 
             // skip the line, parse the rest of the header and build the DOM
             if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) {
-                if (mGeneratedMesh != NULL) {
+                if (mGeneratedMesh != nullptr) {
                     delete(mGeneratedMesh);
                     mGeneratedMesh = nullptr;
                 }
@@ -215,7 +213,7 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
                 throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)");
             }
         } else {
-            if (mGeneratedMesh != NULL) {
+            if (mGeneratedMesh != nullptr) {
                 delete(mGeneratedMesh);
                 mGeneratedMesh = nullptr;
             }
@@ -225,7 +223,7 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
         }
     } else {
         AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
-        if (mGeneratedMesh != NULL) {
+        if (mGeneratedMesh != nullptr) {
             delete(mGeneratedMesh);
             mGeneratedMesh = nullptr;
         }
@@ -237,13 +235,13 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
     //free the file buffer
     streamedBuffer.close();
 
-    if (mGeneratedMesh == NULL) {
+    if (mGeneratedMesh == nullptr) {
         throw DeadlyImportError("Invalid .ply file: Unable to extract mesh data ");
     }
 
     // if no face list is existing we assume that the vertex
     // list is containing a list of points
-    bool pointsOnly = mGeneratedMesh->mFaces == NULL ? true : false;
+    bool pointsOnly = mGeneratedMesh->mFaces == nullptr ? true : false;
     if (pointsOnly) {
       mGeneratedMesh->mPrimitiveTypes = aiPrimitiveType::aiPrimitiveType_POINT;
     }
@@ -277,8 +275,8 @@ void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
 }
 
 void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementInstance* instElement, unsigned int pos) {
-    ai_assert(NULL != pcElement);
-    ai_assert(NULL != instElement);
+    ai_assert(nullptr != pcElement);
+    ai_assert(nullptr != instElement);
 
     ai_uint aiPositions[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
     PLY::EDataType aiTypes[3] = { EDT_Char, EDT_Char, EDT_Char };
@@ -416,7 +414,7 @@ void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementIn
             haveColor = true;
         }
 
-        // assume 1.0 for the alpha channel ifit is not set
+        // assume 1.0 for the alpha channel if it is not set
         if (0xFFFFFFFF == aiColors[3]) {
             cOut.a = 1.0;
         } else {
@@ -481,225 +479,205 @@ void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementIn
 // ------------------------------------------------------------------------------------------------
 // Convert a color component to [0...1]
 ai_real PLYImporter::NormalizeColorValue(PLY::PropertyInstance::ValueUnion val, PLY::EDataType eType) {
-  switch (eType)
-  {
-  case EDT_Float:
-    return val.fFloat;
-  case EDT_Double:
-    return (ai_real)val.fDouble;
-
-  case EDT_UChar:
-    return (ai_real)val.iUInt / (ai_real)0xFF;
-  case EDT_Char:
-    return (ai_real)(val.iInt + (0xFF / 2)) / (ai_real)0xFF;
-  case EDT_UShort:
-    return (ai_real)val.iUInt / (ai_real)0xFFFF;
-  case EDT_Short:
-    return (ai_real)(val.iInt + (0xFFFF / 2)) / (ai_real)0xFFFF;
-  case EDT_UInt:
-    return (ai_real)val.iUInt / (ai_real)0xFFFF;
-  case EDT_Int:
-    return ((ai_real)val.iInt / (ai_real)0xFF) + 0.5f;
-  default:;
-  };
-  return 0.0f;
+    switch (eType) {
+        case EDT_Float:
+            return val.fFloat;
+        case EDT_Double:
+            return (ai_real)val.fDouble;
+        case EDT_UChar:
+            return (ai_real)val.iUInt / (ai_real)0xFF;
+        case EDT_Char:
+            return (ai_real)(val.iInt + (0xFF / 2)) / (ai_real)0xFF;
+        case EDT_UShort:
+            return (ai_real)val.iUInt / (ai_real)0xFFFF;
+        case EDT_Short:
+            return (ai_real)(val.iInt + (0xFFFF / 2)) / (ai_real)0xFFFF;
+        case EDT_UInt:
+            return (ai_real)val.iUInt / (ai_real)0xFFFF;
+        case EDT_Int:
+            return ((ai_real)val.iInt / (ai_real)0xFF) + 0.5f;
+        default:
+            break;
+    }
+
+    return 0.0f;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Try to extract proper faces from the PLY DOM
-void PLYImporter::LoadFace(const PLY::Element* pcElement, const PLY::ElementInstance* instElement, unsigned int pos)
-{
-  ai_assert(NULL != pcElement);
-  ai_assert(NULL != instElement);
-
-  if (mGeneratedMesh == NULL)
-    throw DeadlyImportError("Invalid .ply file: Vertices should be declared before faces");
-
-  bool bOne = false;
-
-  // index of the vertex index list
-  unsigned int iProperty = 0xFFFFFFFF;
-  PLY::EDataType eType = EDT_Char;
-  bool bIsTriStrip = false;
+void PLYImporter::LoadFace(const PLY::Element* pcElement, const PLY::ElementInstance* instElement,
+        unsigned int pos) {
+    ai_assert(nullptr != pcElement);
+    ai_assert(nullptr != instElement);
 
-  // index of the material index property
-  //unsigned int iMaterialIndex = 0xFFFFFFFF;
-  //PLY::EDataType eType2 = EDT_Char;
-
-  // texture coordinates
-  unsigned int iTextureCoord = 0xFFFFFFFF;
-  PLY::EDataType eType3 = EDT_Char;
+    if (mGeneratedMesh == nullptr) {
+        throw DeadlyImportError("Invalid .ply file: Vertices should be declared before faces");
+    }
 
-  // face = unique number of vertex indices
-  if (PLY::EEST_Face == pcElement->eSemantic)
-  {
-    unsigned int _a = 0;
-    for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
-      a != pcElement->alProperties.end(); ++a, ++_a)
-    {
-      if (PLY::EST_VertexIndex == (*a).Semantic)
-      {
-        // must be a dynamic list!
-        if (!(*a).bIsList)
-          continue;
+    bool bOne = false;
+
+    // index of the vertex index list
+    unsigned int iProperty = 0xFFFFFFFF;
+    PLY::EDataType eType = EDT_Char;
+    bool bIsTriStrip = false;
+
+    // index of the material index property
+    //unsigned int iMaterialIndex = 0xFFFFFFFF;
+    //PLY::EDataType eType2 = EDT_Char;
+
+    // texture coordinates
+    unsigned int iTextureCoord = 0xFFFFFFFF;
+    PLY::EDataType eType3 = EDT_Char;
+
+    // face = unique number of vertex indices
+    if (PLY::EEST_Face == pcElement->eSemantic) {
+        unsigned int _a = 0;
+        for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
+                a != pcElement->alProperties.end(); ++a, ++_a) {
+            if (PLY::EST_VertexIndex == (*a).Semantic) {
+                // must be a dynamic list!
+                if (!(*a).bIsList) {
+                    continue;
+                }
 
-        iProperty = _a;
-        bOne = true;
-        eType = (*a).eType;
-      }
-      /*else if (PLY::EST_MaterialIndex == (*a).Semantic)
-      {
-      if ((*a).bIsList)
-      continue;
-      iMaterialIndex = _a;
-      bOne = true;
-      eType2 = (*a).eType;
-      }*/
-      else if (PLY::EST_TextureCoordinates == (*a).Semantic)
-      {
-        // must be a dynamic list!
-        if (!(*a).bIsList)
-          continue;
-        iTextureCoord = _a;
-        bOne = true;
-        eType3 = (*a).eType;
-      }
-    }
-  }
-  // triangle strip
-  // TODO: triangle strip and material index support???
-  else if (PLY::EEST_TriStrip == pcElement->eSemantic)
-  {
-    unsigned int _a = 0;
-    for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
-      a != pcElement->alProperties.end(); ++a, ++_a)
-    {
-      // must be a dynamic list!
-      if (!(*a).bIsList)
-        continue;
-      iProperty = _a;
-      bOne = true;
-      bIsTriStrip = true;
-      eType = (*a).eType;
-      break;
+                iProperty = _a;
+                bOne = true;
+                eType = (*a).eType;
+            } else if (PLY::EST_TextureCoordinates == (*a).Semantic) {
+                // must be a dynamic list!
+                if (!(*a).bIsList) {
+                    continue;
+                }
+                iTextureCoord = _a;
+                bOne = true;
+                eType3 = (*a).eType;
+            }
+        }
     }
-  }
-
-  // check whether we have at least one per-face information set
-  if (bOne)
-  {
-    if (mGeneratedMesh->mFaces == NULL)
-    {
-      mGeneratedMesh->mNumFaces = pcElement->NumOccur;
-      mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces];
+    // triangle strip
+    // TODO: triangle strip and material index support???
+    else if (PLY::EEST_TriStrip == pcElement->eSemantic) {
+        unsigned int _a = 0;
+        for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
+                a != pcElement->alProperties.end(); ++a, ++_a) {
+            // must be a dynamic list!
+            if (!(*a).bIsList) {
+                continue;
+            }
+            iProperty = _a;
+            bOne = true;
+            bIsTriStrip = true;
+            eType = (*a).eType;
+            break;
+        }
     }
 
-    if (!bIsTriStrip)
-    {
-      // parse the list of vertex indices
-      if (0xFFFFFFFF != iProperty)
-      {
-        const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iProperty).avList.size();
-        mGeneratedMesh->mFaces[pos].mNumIndices = iNum;
-        mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[iNum];
-
-        std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p =
-          GetProperty(instElement->alProperties, iProperty).avList.begin();
-
-        for (unsigned int a = 0; a < iNum; ++a, ++p)
-        {
-          mGeneratedMesh->mFaces[pos].mIndices[a] = PLY::PropertyInstance::ConvertTo<unsigned int>(*p, eType);
+    // check whether we have at least one per-face information set
+    if (bOne) {
+        if (mGeneratedMesh->mFaces == nullptr) {
+            mGeneratedMesh->mNumFaces = pcElement->NumOccur;
+            mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces];
         }
-      }
 
-      // parse the material index
-      // cannot be handled without processing the whole file first
-      /*if (0xFFFFFFFF != iMaterialIndex)
-      {
-      mGeneratedMesh->mFaces[pos]. = PLY::PropertyInstance::ConvertTo<unsigned int>(
-      GetProperty(instElement->alProperties, iMaterialIndex).avList.front(), eType2);
-      }*/
+        if (!bIsTriStrip) {
+            // parse the list of vertex indices
+            if (0xFFFFFFFF != iProperty) {
+                const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iProperty).avList.size();
+                mGeneratedMesh->mFaces[pos].mNumIndices = iNum;
+                mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[iNum];
 
-      if (0xFFFFFFFF != iTextureCoord)
-      {
-        const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iTextureCoord).avList.size();
+                std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p =
+                GetProperty(instElement->alProperties, iProperty).avList.begin();
 
-        //should be 6 coords
-        std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p =
-          GetProperty(instElement->alProperties, iTextureCoord).avList.begin();
+                for (unsigned int a = 0; a < iNum; ++a, ++p) {
+                    mGeneratedMesh->mFaces[pos].mIndices[a] = PLY::PropertyInstance::ConvertTo<unsigned int>(*p, eType);
+                }
+            }
 
-        if ((iNum / 3) == 2) // X Y coord
+        // parse the material index
+        // cannot be handled without processing the whole file first
+        /*if (0xFFFFFFFF != iMaterialIndex)
         {
-          for (unsigned int a = 0; a < iNum; ++a, ++p)
-          {
-            unsigned int vindex = mGeneratedMesh->mFaces[pos].mIndices[a / 2];
-            if (vindex < mGeneratedMesh->mNumVertices)
-            {
-              if (mGeneratedMesh->mTextureCoords[0] == NULL)
-              {
-                mGeneratedMesh->mNumUVComponents[0] = 2;
-                mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices];
-              }
-
-              if (a % 2 == 0)
-                mGeneratedMesh->mTextureCoords[0][vindex].x = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3);
-              else
-                mGeneratedMesh->mTextureCoords[0][vindex].y = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3);
-
-              mGeneratedMesh->mTextureCoords[0][vindex].z = 0;
+            mGeneratedMesh->mFaces[pos]. = PLY::PropertyInstance::ConvertTo<unsigned int>(
+            GetProperty(instElement->alProperties, iMaterialIndex).avList.front(), eType2);
+        }*/
+
+            if (0xFFFFFFFF != iTextureCoord) {
+                const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iTextureCoord).avList.size();
+
+                //should be 6 coords
+                std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p =
+                GetProperty(instElement->alProperties, iTextureCoord).avList.begin();
+
+                if ((iNum / 3) == 2) // X Y coord
+                {
+                    for (unsigned int a = 0; a < iNum; ++a, ++p) {
+                        unsigned int vindex = mGeneratedMesh->mFaces[pos].mIndices[a / 2];
+                        if (vindex < mGeneratedMesh->mNumVertices) {
+                            if (mGeneratedMesh->mTextureCoords[0] == nullptr ) {
+                                mGeneratedMesh->mNumUVComponents[0] = 2;
+                                mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices];
+                            }
+
+                            if (a % 2 == 0) {
+                                mGeneratedMesh->mTextureCoords[0][vindex].x = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3);
+                            } else {
+                                mGeneratedMesh->mTextureCoords[0][vindex].y = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3);
+                            }
+
+                            mGeneratedMesh->mTextureCoords[0][vindex].z = 0;
+                        }
+                    }
+                }
             }
-          }
-        }
-      }
-    }
-    else // triangle strips
-    {
-      // normally we have only one triangle strip instance where
-      // a value of -1 indicates a restart of the strip
-      bool flip = false;
-      const std::vector<PLY::PropertyInstance::ValueUnion>& quak = GetProperty(instElement->alProperties, iProperty).avList;
-      //pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u)); //Limits memory consumption
-
-      int aiTable[2] = { -1, -1 };
-      for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator a = quak.begin(); a != quak.end(); ++a)  {
-        const int p = PLY::PropertyInstance::ConvertTo<int>(*a, eType);
+        } else { // triangle strips
+            // normally we have only one triangle strip instance where
+            // a value of -1 indicates a restart of the strip
+            bool flip = false;
+            const std::vector<PLY::PropertyInstance::ValueUnion>& quak = GetProperty(instElement->alProperties, iProperty).avList;
+            //pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u)); //Limits memory consumption
+
+            int aiTable[2] = { -1, -1 };
+            for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator a = quak.begin(); a != quak.end(); ++a)  {
+                const int p = PLY::PropertyInstance::ConvertTo<int>(*a, eType);
+
+                if (-1 == p) {
+                    // restart the strip ...
+                    aiTable[0] = aiTable[1] = -1;
+                    flip = false;
+                    continue;
+                }
+                if (-1 == aiTable[0]) {
+                    aiTable[0] = p;
+                    continue;
+                }
+                if (-1 == aiTable[1]) {
+                    aiTable[1] = p;
+                    continue;
+                }
 
-        if (-1 == p)    {
-          // restart the strip ...
-          aiTable[0] = aiTable[1] = -1;
-          flip = false;
-          continue;
-        }
-        if (-1 == aiTable[0]) {
-          aiTable[0] = p;
-          continue;
-        }
-        if (-1 == aiTable[1]) {
-          aiTable[1] = p;
-          continue;
-        }
+                if (mGeneratedMesh->mFaces == nullptr) {
+                    mGeneratedMesh->mNumFaces = pcElement->NumOccur;
+                    mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces];
+                }
 
-        if (mGeneratedMesh->mFaces == NULL)
-        {
-          mGeneratedMesh->mNumFaces = pcElement->NumOccur;
-          mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces];
-        }
+                mGeneratedMesh->mFaces[pos].mNumIndices = 3;
+                mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[3];
+                mGeneratedMesh->mFaces[pos].mIndices[0] = aiTable[0];
+                mGeneratedMesh->mFaces[pos].mIndices[1] = aiTable[1];
+                mGeneratedMesh->mFaces[pos].mIndices[2] = p;
 
-        mGeneratedMesh->mFaces[pos].mNumIndices = 3;
-        mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[3];
-        mGeneratedMesh->mFaces[pos].mIndices[0] = aiTable[0];
-        mGeneratedMesh->mFaces[pos].mIndices[1] = aiTable[1];
-        mGeneratedMesh->mFaces[pos].mIndices[2] = p;
+                // every second pass swap the indices.
+                flip = !flip;
+                if ( flip ) {
+                    std::swap(mGeneratedMesh->mFaces[pos].mIndices[0], mGeneratedMesh->mFaces[pos].mIndices[1]);
+                }
 
-        if ((flip = !flip)) {
-          std::swap(mGeneratedMesh->mFaces[pos].mIndices[0], mGeneratedMesh->mFaces[pos].mIndices[1]);
+                aiTable[0] = aiTable[1];
+                aiTable[1] = p;
+            }
         }
-
-        aiTable[0] = aiTable[1];
-        aiTable[1] = p;
-      }
     }
-  }
 }
 
 // ------------------------------------------------------------------------------------------------

+ 6 - 3
code/PostStepRegistry.cpp

@@ -164,9 +164,6 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
     out.push_back( new OptimizeGraphProcess());
 #endif
-#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS)
-    out.push_back( new FindDegeneratesProcess());
-#endif
 #ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
     out.push_back( new ComputeUVMappingProcess());
 #endif
@@ -179,6 +176,12 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS)
     out.push_back( new TriangulateProcess());
 #endif
+#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS)
+    //find degenerates should run after triangulation (to sort out small
+    //generated triangles) but before sort by p types (in case there are lines
+    //and points generated and inserted into a mesh)
+    out.push_back( new FindDegeneratesProcess());
+#endif
 #if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS)
     out.push_back( new SortByPTypeProcess());
 #endif

+ 39 - 16
code/STLLoader.cpp

@@ -90,6 +90,9 @@ static bool IsBinarySTL(const char* buffer, unsigned int fileSize) {
     return expectedBinaryFileSize == fileSize;
 }
 
+static const size_t BufferSize = 500;
+static const char UnicodeBoundary = 127;
+
 // An ascii STL buffer will begin with "solid NAME", where NAME is optional.
 // Note: The "solid NAME" check is necessary, but not sufficient, to determine
 // if the buffer is ASCII; a binary header could also begin with "solid NAME".
@@ -108,10 +111,10 @@ static bool IsAsciiSTL(const char* buffer, unsigned int fileSize) {
     bool isASCII( strncmp( buffer, "solid", 5 ) == 0 );
     if( isASCII ) {
         // A lot of importers are write solid even if the file is binary. So we have to check for ASCII-characters.
-        if( fileSize >= 500 ) {
+        if( fileSize >= BufferSize) {
             isASCII = true;
-            for( unsigned int i = 0; i < 500; i++ ) {
-                if( buffer[ i ] > 127 ) {
+            for( unsigned int i = 0; i < BufferSize; i++ ) {
+                if( buffer[ i ] > UnicodeBoundary) {
                     isASCII = false;
                     break;
                 }
@@ -211,10 +214,11 @@ void STLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
 
     // create a single default material, using a white diffuse color for consistency with
     // other geometric types (e.g., PLY).
-    aiMaterial* pcMat = new aiMaterial();
+    aiMaterial* pcMat = aiCreateAndRegisterDefaultMaterial();
+    /*aiMaterial* pcMat = new aiMaterial();
     aiString s;
     s.Set(AI_DEFAULT_MATERIAL_NAME);
-    pcMat->AddProperty(&s, AI_MATKEY_NAME);
+    pcMat->AddProperty(&s, AI_MATKEY_NAME);*/
 
     aiColor4D clrDiffuse(ai_real(1.0),ai_real(1.0),ai_real(1.0),ai_real(1.0));
     if (bMatClr) {
@@ -362,14 +366,23 @@ void STLImporter::LoadASCIIFile( aiNode *root ) {
             pMesh->mNumFaces = 0;
             throw DeadlyImportError("Normal buffer size does not match position buffer size");
         }
-        pMesh->mNumFaces = static_cast<unsigned int>(positionBuffer.size() / 3);
-        pMesh->mNumVertices = static_cast<unsigned int>(positionBuffer.size());
-        pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
-        memcpy(pMesh->mVertices, positionBuffer.data(), pMesh->mNumVertices * sizeof(aiVector3D));
-        positionBuffer.clear();
-        pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
-        memcpy(pMesh->mNormals, normalBuffer.data(), pMesh->mNumVertices * sizeof(aiVector3D));
-        normalBuffer.clear();
+
+        // only process positionbuffer when filled, else exception when accessing with index operator
+        // see line 353: only warning is triggered
+        // see line 373(now): access to empty positionbuffer with index operator forced exception
+        if (!positionBuffer.empty()) {
+            pMesh->mNumFaces = static_cast<unsigned int>(positionBuffer.size() / 3);
+            pMesh->mNumVertices = static_cast<unsigned int>(positionBuffer.size());
+            pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+            memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
+            positionBuffer.clear();
+        }
+        // also only process normalBuffer when filled, else exception when accessing with index operator
+        if (!normalBuffer.empty()) {
+            pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+            memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D));
+            normalBuffer.clear();
+        }
 
         // now copy faces
         addFacesToMesh(pMesh);
@@ -526,11 +539,21 @@ bool STLImporter::LoadBinaryFile()
     // now copy faces
     addFacesToMesh(pMesh);
 
+    aiNode* root = pScene->mRootNode;
+
+    // allocate one node
+    aiNode* node = new aiNode();
+    node->mParent = root;
+
+    root->mNumChildren = 1u;
+    root->mChildren = new aiNode*[root->mNumChildren];
+    root->mChildren[0] = node;
+
     // add all created meshes to the single node
-    pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
-    pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+    node->mNumMeshes = pScene->mNumMeshes;
+    node->mMeshes = new unsigned int[pScene->mNumMeshes];
     for (unsigned int i = 0; i < pScene->mNumMeshes; i++)
-        pScene->mRootNode->mMeshes[i] = i;
+        node->mMeshes[i] = i;
 
     if (bIsMaterialise && !pMesh->mColors[0])
     {

+ 12 - 12
code/SceneCombiner.cpp

@@ -707,30 +707,30 @@ void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator
     // we work with hashes to make the comparisons MUCH faster,
     // at least if we have many bones.
     std::list<BoneWithHash> asBones;
-    BuildUniqueBoneList(asBones, it,end);
+    BuildUniqueBoneList( asBones, it, end );
 
     // now create the output bones
     out->mNumBones = 0;
     out->mBones = new aiBone*[asBones.size()];
 
-    for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),end = asBones.end(); it != end;++it)  {
+    for (std::list<BoneWithHash>::const_iterator boneIt = asBones.begin(),boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt )  {
         // Allocate a bone and setup it's name
         aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
-        pc->mName = aiString( *((*it).second ));
+        pc->mName = aiString( *( boneIt->second ));
 
-        std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
+        std::vector< BoneSrcIndex >::const_iterator wend = boneIt->pSrcBones.end();
 
         // Loop through all bones to be joined for this bone
-        for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)  {
+        for (std::vector< BoneSrcIndex >::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit)  {
             pc->mNumWeights += (*wmit).first->mNumWeights;
 
             // NOTE: different offset matrices for bones with equal names
             // are - at the moment - not handled correctly.
-            if (wmit != (*it).pSrcBones.begin() && pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)   {
+            if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) {
                 ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment");
                 continue;
             }
-            pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
+            pc->mOffsetMatrix = wmit->first->mOffsetMatrix;
         }
 
         // Allocate the vertex weight array
@@ -738,7 +738,7 @@ void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator
 
         // And copy the final weights - adjust the vertex IDs by the
         // face index offset of the corresponding mesh.
-        for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)  {
+        for (std::vector< BoneSrcIndex >::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != wend; ++wmit)  {
             aiBone* pip = (*wmit).first;
             for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) {
                 const aiVertexWeight& vfi = pip->mWeights[mp];
@@ -849,14 +849,14 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/,
         // copy vertex colors
         n = 0;
         while ((**begin).HasVertexColors(n))    {
-            aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
-            for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)   {
+            aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
+            for ( std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it )   {
                 if ((*it)->mColors[n])  {
-                    ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
+                    ::memcpy( pVec2, (*it)->mColors[ n ], (*it)->mNumVertices * sizeof( aiColor4D ) ) ;
                 } else {
                     ASSIMP_LOG_WARN( "JoinMeshes: VCs expected but input mesh contains no VCs" );
                 }
-                pv2 += (*it)->mNumVertices;
+                pVec2 += (*it)->mNumVertices;
             }
             ++n;
         }

+ 10 - 6
code/ScenePrivate.h

@@ -55,12 +55,8 @@ namespace Assimp {
 class Importer;
 
 struct ScenePrivateData {
-    ScenePrivateData()
-    : mOrigImporter( nullptr )
-    , mPPStepsApplied( 0 )
-    , mIsCopy( false ) {
-        // empty
-    }
+    //  The struct constructor.
+    ScenePrivateData();
 
     // Importer that originally loaded the scene though the C-API
     // If set, this object is owned by this private data instance.
@@ -77,6 +73,14 @@ struct ScenePrivateData {
     bool mIsCopy;
 };
 
+inline
+ScenePrivateData::ScenePrivateData()
+: mOrigImporter( nullptr )
+, mPPStepsApplied( 0 )
+, mIsCopy( false ) {
+    // empty
+}
+
 // Access private data stored in the scene
 inline
 ScenePrivateData* ScenePriv(aiScene* in) {

+ 1 - 1
code/SpatialSort.cpp

@@ -223,7 +223,7 @@ namespace {
         if( (-42 == (~42 + 1)) && (binValue & 0x80000000))
             return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue;
         // One's complement?
-        else if( (-42 == ~42) && (binValue & 0x80000000))
+        else if ( (-42 == ~42) && (binValue & 0x80000000))
             return BinFloat(-0) - binValue;
         // Sign-magnitude?
         else if( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary

+ 21 - 18
code/TriangulateProcess.cpp

@@ -105,8 +105,10 @@ void TriangulateProcess::Execute( aiScene* pScene)
     bool bHas = false;
     for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
     {
-        if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) {
-            bHas = true;
+        if (pScene->mMeshes[ a ]) {
+            if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) {
+                bHas = true;
+            }
         }
     }
     if ( bHas ) {
@@ -285,7 +287,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
             // We project it onto a plane to get a 2d triangle.
 
             // Collect all vertices of of the polygon.
-            for (tmp = 0; tmp < max; ++tmp) {
+           for (tmp = 0; tmp < max; ++tmp) {
                 temp_verts3d[tmp] = verts[idx[tmp]];
             }
 
@@ -485,21 +487,22 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
         for(aiFace* f = last_face; f != curOut; ) {
             unsigned int* i = f->mIndices;
 
-            //  drop dumb 0-area triangles
-            if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
-                ASSIMP_LOG_DEBUG("Dropping triangle with area 0");
-                --curOut;
-
-                delete[] f->mIndices;
-                f->mIndices = NULL;
-
-                for(aiFace* ff = f; ff != curOut; ++ff) {
-                    ff->mNumIndices = (ff+1)->mNumIndices;
-                    ff->mIndices = (ff+1)->mIndices;
-                    (ff+1)->mIndices = NULL;
-                }
-                continue;
-            }
+            //  drop dumb 0-area triangles - deactivated for now:
+            //FindDegenerates post processing step can do the same thing
+            //if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
+            //    ASSIMP_LOG_DEBUG("Dropping triangle with area 0");
+            //    --curOut;
+
+            //    delete[] f->mIndices;
+            //    f->mIndices = nullptr;
+
+            //    for(aiFace* ff = f; ff != curOut; ++ff) {
+            //        ff->mNumIndices = (ff+1)->mNumIndices;
+            //        ff->mIndices = (ff+1)->mIndices;
+            //        (ff+1)->mIndices = nullptr;
+            //    }
+            //    continue;
+            //}
 
             i[0] = idx[i[0]];
             i[1] = idx[i[1]];

+ 5 - 3
code/Version.cpp

@@ -150,9 +150,11 @@ ASSIMP_API aiScene::~aiScene() {
             delete mMeshes[a];
     delete [] mMeshes;
 
-    if (mNumMaterials && mMaterials)
-        for( unsigned int a = 0; a < mNumMaterials; a++)
-            delete mMaterials[a];
+    if (mNumMaterials && mMaterials) {
+        for (unsigned int a = 0; a < mNumMaterials; ++a ) {
+            delete mMaterials[ a ];
+        }
+    }
     delete [] mMaterials;
 
     if (mNumAnimations && mAnimations)

+ 6 - 1
code/XFileImporter.cpp

@@ -332,6 +332,11 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec
                 // collect vertex data for indices of this face
                 for( unsigned int d = 0; d < df.mNumIndices; ++d ) {
                     df.mIndices[d] = newIndex;
+                    const unsigned int newIdx( pf.mIndices[ d ] );
+                    if ( newIdx > sourceMesh->mPositions.size() ) {
+                        continue;
+                    }
+
                     orgPoints[newIndex] = pf.mIndices[d];
 
                     // Position
@@ -459,7 +464,7 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData
             nbone->mNodeName.Set( bone->mBoneName);
             nanim->mChannels[b] = nbone;
 
-            // keyframes are given as combined transformation matrix keys
+            // key-frames are given as combined transformation matrix keys
             if( !bone->mTrafoKeys.empty() )
             {
                 nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size();

+ 8 - 3
code/XFileParser.cpp

@@ -471,7 +471,10 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh)
         unsigned int numIndices = ReadInt();
         Face& face = pMesh->mPosFaces[a];
         for (unsigned int b = 0; b < numIndices; ++b) {
-            face.mIndices.push_back( ReadInt() );
+            const int idx( ReadInt() );
+            if ( static_cast<unsigned int>( idx ) <= numVertices ) {
+                face.mIndices.push_back( idx );
+            }
         }
         TestForSeparator();
     }
@@ -1293,7 +1296,7 @@ unsigned int XFileParser::ReadBinDWord() {
 // ------------------------------------------------------------------------------------------------
 unsigned int XFileParser::ReadInt()
 {
-    if( mIsBinaryFormat)
+   if( mIsBinaryFormat)
     {
         if( mBinaryNumCount == 0 && mEnd - mP >= 2)
         {
@@ -1305,7 +1308,8 @@ unsigned int XFileParser::ReadInt()
         }
 
         --mBinaryNumCount;
-        if ( mEnd - mP >= 4) {
+        const size_t len( mEnd - mP );
+        if ( len >= 4) {
             return ReadBinDWord();
         } else {
             mP = mEnd;
@@ -1340,6 +1344,7 @@ unsigned int XFileParser::ReadInt()
         }
 
         CheckForSeparator();
+
         return isNegative ? ((unsigned int) -int( number)) : number;
     }
 }

+ 7 - 0
code/glTF2Asset.h

@@ -766,10 +766,17 @@ namespace glTF2
             Ref<Accessor> indices;
 
             Ref<Material> material;
+
+            struct Target {
+                AccessorList position, normal, tangent;
+            };
+            std::vector<Target> targets;
         };
 
         std::vector<Primitive> primitives;
 
+        std::vector<float> weights;
+
         Mesh() {}
 
 		/// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root)

+ 45 - 0
code/glTF2Asset.inl

@@ -931,6 +931,21 @@ namespace {
         else return false;
         return true;
     }
+
+    inline bool GetAttribTargetVector(Mesh::Primitive& p, const int targetIndex, const char* attr, Mesh::AccessorList*& v, int& pos)
+    {
+        if ((pos = Compare(attr, "POSITION"))) {
+            v = &(p.targets[targetIndex].position);
+        }
+        else if ((pos = Compare(attr, "NORMAL"))) {
+            v = &(p.targets[targetIndex].normal);
+        }
+        else if ((pos = Compare(attr, "TANGENT"))) {
+            v = &(p.targets[targetIndex].tangent);
+        }
+        else return false;
+        return true;
+    }
 }
 
 inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
@@ -965,6 +980,26 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
                 }
             }
 
+            if (Value* targetsArray = FindArray(primitive, "targets")) {
+                prim.targets.resize(targetsArray->Size());
+                for (unsigned int i = 0; i < targetsArray->Size(); ++i) {
+                    Value& target = (*targetsArray)[i];
+                    if (!target.IsObject()) continue;
+                    for (Value::MemberIterator it = target.MemberBegin(); it != target.MemberEnd(); ++it) {
+                        if (!it->value.IsUint()) continue;
+                        const char* attr = it->name.GetString();
+                        // Valid attribute semantics include POSITION, NORMAL, TANGENT
+                        int undPos = 0;
+                        Mesh::AccessorList* vec = 0;
+                        if (GetAttribTargetVector(prim, i, attr, vec, undPos)) {
+                            size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
+                            if ((*vec).size() <= idx) (*vec).resize(idx + 1);
+                            (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
+                        }
+                    }
+                }
+            }
+
             if (Value* indices = FindUInt(primitive, "indices")) {
 				prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
             }
@@ -974,6 +1009,16 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
             }
         }
     }
+
+    if (Value* weights = FindArray(pJSON_Object, "weights")) {
+        this->weights.resize(weights->Size());
+        for (unsigned int i = 0; i < weights->Size(); ++i) {
+          Value& weightValue = (*weights)[i];
+          if (weightValue.IsNumber()) {
+            this->weights[i] = weightValue.GetFloat();
+          }
+        }
+    }
 }
 
 inline void Camera::Read(Value& obj, Asset& /*r*/)

+ 4 - 21
code/glTF2Exporter.cpp

@@ -94,19 +94,7 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai
     , mIOSystem(pIOSystem)
     , mProperties(pProperties)
 {
-    aiScene* sceneCopy_tmp;
-    SceneCombiner::CopyScene(&sceneCopy_tmp, pScene);
-    std::unique_ptr<aiScene> sceneCopy(sceneCopy_tmp);
-
-    SplitLargeMeshesProcess_Triangle tri_splitter;
-    tri_splitter.SetLimit(0xffff);
-    tri_splitter.Execute(sceneCopy.get());
-
-    SplitLargeMeshesProcess_Vertex vert_splitter;
-    vert_splitter.SetLimit(0xffff);
-    vert_splitter.Execute(sceneCopy.get());
-
-    mScene = sceneCopy.get();
+    mScene = pScene;
 
     mAsset.reset( new Asset( pIOSystem ) );
 
@@ -681,12 +669,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buf
 
 void glTF2Exporter::ExportMeshes()
 {
-    // Not for
-    //     using IndicesType = decltype(aiFace::mNumIndices);
-    // But yes for
-    //     using IndicesType = unsigned short;
-    // because "ComponentType_UNSIGNED_SHORT" used for indices. And it's a maximal type according to glTF specification.
-    typedef unsigned short IndicesType;
+    typedef decltype(aiFace::mNumIndices) IndicesType;
 
     std::string fname = std::string(mFilename);
     std::string bufferIdPrefix = fname.substr(0, fname.rfind(".gltf"));
@@ -778,11 +761,11 @@ void glTF2Exporter::ExportMeshes()
             indices.resize(aim->mNumFaces * nIndicesPerFace);
             for (size_t i = 0; i < aim->mNumFaces; ++i) {
                 for (size_t j = 0; j < nIndicesPerFace; ++j) {
-                    indices[i*nIndicesPerFace + j] = uint16_t(aim->mFaces[i].mIndices[j]);
+                    indices[i*nIndicesPerFace + j] = IndicesType(aim->mFaces[i].mIndices[j]);
                 }
             }
 
-			p.indices = ExportData(*mAsset, meshId, b, unsigned(indices.size()), &indices[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_UNSIGNED_SHORT, true);
+			p.indices = ExportData(*mAsset, meshId, b, unsigned(indices.size()), &indices[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_UNSIGNED_INT, true);
 		}
 
         switch (aim->mPrimitiveTypes) {

+ 152 - 29
code/glTF2Importer.cpp

@@ -51,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ai_assert.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/importerdesc.h>
+#include <assimp/CreateAnimMesh.h>
 
 #include <memory>
 
@@ -65,6 +66,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace glTF2;
 
+namespace {
+    // generate bitangents from normals and tangents according to spec
+    struct Tangent {
+        aiVector3D xyz;
+        ai_real w;
+    };
+} // namespace
 
 //
 // glTF2Importer
@@ -109,13 +117,9 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool
 
     if (pIOHandler) {
         glTF2::Asset asset(pIOHandler);
-        try {
-            asset.Load(pFile, extension == "glb");
-            std::string version = asset.asset.version;
-            return !version.empty() && version[0] == '2';
-        } catch (...) {
-            return false;
-        }
+        asset.Load(pFile, extension == "glb");
+        std::string version = asset.asset.version;
+        return !version.empty() && version[0] == '2';
     }
 
     return false;
@@ -416,10 +420,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
                 // only extract tangents if normals are present
                 if (attr.tangent.size() > 0 && attr.tangent[0]) {
                     // generate bitangents from normals and tangents according to spec
-                    struct Tangent {
-                        aiVector3D xyz;
-                        ai_real w;
-                    } *tangents = nullptr;
+                    Tangent *tangents = nullptr;
 
                     attr.tangent[0]->ExtractData(tangents);
 
@@ -451,11 +452,57 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
                 }
             }
 
+            std::vector<Mesh::Primitive::Target>& targets = prim.targets;
+            if (targets.size() > 0) {
+                aim->mNumAnimMeshes = targets.size();
+                aim->mAnimMeshes = new aiAnimMesh*[aim->mNumAnimMeshes];
+                for (size_t i = 0; i < targets.size(); i++) {
+                    aim->mAnimMeshes[i] = aiCreateAnimMesh(aim);
+                    aiAnimMesh& aiAnimMesh = *(aim->mAnimMeshes[i]);
+                    Mesh::Primitive::Target& target = targets[i];
+
+                    if (target.position.size() > 0) {
+                        aiVector3D *positionDiff = nullptr;
+                        target.position[0]->ExtractData(positionDiff);
+                        for(unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
+                            aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
+                        }
+                        delete [] positionDiff;
+                    }
+                    if (target.normal.size() > 0) {
+                        aiVector3D *normalDiff = nullptr;
+                        target.normal[0]->ExtractData(normalDiff);
+                        for(unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
+                            aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
+                        }
+                        delete [] normalDiff;
+                    }
+                    if (target.tangent.size() > 0) {
+                        Tangent *tangent = nullptr;
+                        attr.tangent[0]->ExtractData(tangent);
 
-            if (prim.indices) {
-                aiFace* faces = 0;
-                unsigned int nFaces = 0;
+                        aiVector3D *tangentDiff = nullptr;
+                        target.tangent[0]->ExtractData(tangentDiff);
+
+                        for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
+                            tangent[vertexId].xyz += tangentDiff[vertexId];
+                            aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz;
+                            aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w;
+                        }
+                        delete [] tangent;
+                        delete [] tangentDiff;
+                    }
+                    if (mesh.weights.size() > i) {
+                        aiAnimMesh.mWeight = mesh.weights[i];
+                    }
+                }
+            }
 
+
+            aiFace* faces = 0;
+            unsigned int nFaces = 0;
+
+            if (prim.indices) {
                 unsigned int count = prim.indices->count;
 
                 Accessor::Indexer data = prim.indices->GetIndexer();
@@ -505,9 +552,18 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
                     case PrimitiveMode_TRIANGLE_STRIP: {
                         nFaces = count - 2;
                         faces = new aiFace[nFaces];
-                        SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
-                        for (unsigned int i = 3; i < count; ++i) {
-                            SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], data.GetUInt(i));
+                        for (unsigned int i = 0; i < nFaces; ++i) {
+                            //The ordering is to ensure that the triangles are all drawn with the same orientation
+                            if ((i + 1) % 2 == 0)
+                            {
+                                //For even n, vertices n + 1, n, and n + 2 define triangle n
+                                SetFace(faces[i], data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
+                            }
+                            else
+                            {
+                                //For odd n, vertices n, n+1, and n+2 define triangle n
+                                SetFace(faces[i], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+                            }
                         }
                         break;
                     }
@@ -515,19 +571,92 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
                         nFaces = count - 2;
                         faces = new aiFace[nFaces];
                         SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
-                        for (unsigned int i = 3; i < count; ++i) {
-                            SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i));
+                        for (unsigned int i = 1; i < nFaces; ++i) {
+                            SetFace(faces[i], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i + 2));
                         }
                         break;
                 }
+            }
+            else { // no indices provided so directly generate from counts
+
+                // use the already determined count as it includes checks 
+                unsigned int count = aim->mNumVertices;
 
-                if (faces) {
-                    aim->mFaces = faces;
-                    aim->mNumFaces = nFaces;
-                    ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices));
+                switch (prim.mode) {
+                case PrimitiveMode_POINTS: {
+                    nFaces = count;
+                    faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; ++i) {
+                        SetFace(faces[i], i);
+                    }
+                    break;
+                }
+
+                case PrimitiveMode_LINES: {
+                    nFaces = count / 2;
+                    faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 2) {
+                        SetFace(faces[i / 2], i, i + 1);
+                    }
+                    break;
+                }
+
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP: {
+                    nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
+                    faces = new aiFace[nFaces];
+                    SetFace(faces[0], 0, 1);
+                    for (unsigned int i = 2; i < count; ++i) {
+                        SetFace(faces[i - 1], faces[i - 2].mIndices[1], i);
+                    }
+                    if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
+                        SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]);
+                    }
+                    break;
+                }
+
+                case PrimitiveMode_TRIANGLES: {
+                    nFaces = count / 3;
+                    faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 3) {
+                        SetFace(faces[i / 3], i, i + 1, i + 2);
+                    }
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_STRIP: {
+                    nFaces = count - 2;
+                    faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < nFaces; ++i) {
+                        //The ordering is to ensure that the triangles are all drawn with the same orientation
+                        if ((i+1) % 2 == 0)
+                        {
+                            //For even n, vertices n + 1, n, and n + 2 define triangle n
+                            SetFace(faces[i], i+1, i, i+2);
+                        }
+                        else
+                        {
+                            //For odd n, vertices n, n+1, and n+2 define triangle n
+                            SetFace(faces[i], i, i+1, i+2);
+                        }
+                    }
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_FAN:
+                    nFaces = count - 2;
+                    faces = new aiFace[nFaces];
+                    SetFace(faces[0], 0, 1, 2);
+                    for (unsigned int i = 1; i < nFaces; ++i) {
+                        SetFace(faces[i], faces[0].mIndices[0], faces[i - 1].mIndices[2], i + 2);
+                    }
+                    break;
                 }
             }
 
+            if (faces) {
+                aim->mFaces = faces;
+                aim->mNumFaces = nFaces;
+                ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices));
+            }
 
             if (prim.material) {
                 aim->mMaterialIndex = prim.material.GetIndex();
@@ -739,12 +868,6 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
 
     ImportNodes(asset);
 
-    // TODO: it does not split the loaded vertices, should it?
-    //pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
-    MakeVerboseFormatProcess process;
-    process.Execute(pScene);
-
-
     if (pScene->mNumMeshes == 0) {
         pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
     }

+ 71 - 12
code/glTFImporter.cpp

@@ -351,10 +351,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
             }
 
 
-            if (prim.indices) {
-				aiFace* faces = 0;
-                unsigned int nFaces = 0;
+            aiFace* faces = 0;
+            unsigned int nFaces = 0;
 
+            if (prim.indices) {
                 unsigned int count = prim.indices->count;
 
                 Accessor::Indexer data = prim.indices->GetIndexer();
@@ -419,14 +419,78 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
                         }
                         break;
                 }
+            }
+            else { // no indices provided so directly generate from counts
+
+                // use the already determined count as it includes checks 
+                unsigned int count = aim->mNumVertices;
+
+                switch (prim.mode) {
+                case PrimitiveMode_POINTS: {
+                    nFaces = count;
+                    faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; ++i) {
+                        SetFace(faces[i], i);
+                    }
+                    break;
+                }
+
+                case PrimitiveMode_LINES: {
+                    nFaces = count / 2;
+                    faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 2) {
+                        SetFace(faces[i / 2], i, i + 1);
+                    }
+                    break;
+                }
+
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP: {
+                    nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
+                    faces = new aiFace[nFaces];
+                    SetFace(faces[0], 0, 1);
+                    for (unsigned int i = 2; i < count; ++i) {
+                        SetFace(faces[i - 1], faces[i - 2].mIndices[1], i);
+                    }
+                    if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
+                        SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]);
+                    }
+                    break;
+                }
 
-                if (faces) {
-                    aim->mFaces = faces;
-                    aim->mNumFaces = nFaces;
-                    ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices));
+                case PrimitiveMode_TRIANGLES: {
+                    nFaces = count / 3;
+                    faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 3) {
+                        SetFace(faces[i / 3], i, i + 1, i + 2);
+                    }
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_STRIP: {
+                    nFaces = count - 2;
+                    faces = new aiFace[nFaces];
+                    SetFace(faces[0], 0, 1, 2);
+                    for (unsigned int i = 3; i < count; ++i) {
+                        SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], i);
+                    }
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_FAN:
+                    nFaces = count - 2;
+                    faces = new aiFace[nFaces];
+                    SetFace(faces[0], 0, 1, 2);
+                    for (unsigned int i = 3; i < count; ++i) {
+                        SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], i);
+                    }
+                    break;
                 }
             }
 
+            if (faces) {
+                aim->mFaces = faces;
+                aim->mNumFaces = nFaces;
+                ai_assert(CheckValidFacesIndices(faces, nFaces, aim->mNumVertices));
+            }
 
             if (prim.material) {
                 aim->mMaterialIndex = prim.material.GetIndex();
@@ -676,11 +740,6 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS
 
     ImportNodes(asset);
 
-    // TODO: it does not split the loaded vertices, should it?
-    //pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
-	MakeVerboseFormatProcess process;
-    process.Execute(pScene);
-
 
     if (pScene->mNumMeshes == 0) {
         pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;

+ 6 - 0
contrib/openddlparser/code/OpenDDLParser.cpp

@@ -100,9 +100,15 @@ static bool isUnsignedIntegerType( Value::ValueType integerType ) {
 }
 
 static DDLNode *createDDLNode( Text *id, OpenDDLParser *parser ) {
+    // Basic checks
     if( ddl_nullptr == id || ddl_nullptr == parser ) {
         return ddl_nullptr;
     }
+    
+    // If the buffer is empty ( an empty node ) return nullptr
+    if ( ddl_nullptr == id->m_buffer ) {
+        return ddl_nullptr;
+    }
 
     const std::string type( id->m_buffer );
     DDLNode *parent( parser->top() );

+ 2 - 1
include/assimp/BaseImporter.h

@@ -244,7 +244,8 @@ public: // static utilities
         const char** tokens,
         unsigned int numTokens,
         unsigned int searchBytes = 200,
-        bool tokensSol = false);
+        bool tokensSol = false,
+        bool noAlphaBeforeTokens = false);
 
     // -------------------------------------------------------------------
     /** @brief Check whether a file has a specific file extension

+ 1 - 1
include/assimp/Macros.h

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

+ 21 - 20
include/assimp/anim.h

@@ -79,26 +79,27 @@ struct aiVectorKey
     /// @brief  Construction from a given time and key value.
 
     aiVectorKey(double time, const aiVector3D& value)
-        :   mTime   (time)
-        ,   mValue  (value)
-    {}
+    : mTime( time )
+    , mValue( value ) {
+        // empty
+    }
 
     typedef aiVector3D elem_type;
 
     // Comparison operators. For use with std::find();
-    bool operator == (const aiVectorKey& o) const {
-        return o.mValue == this->mValue;
+    bool operator == (const aiVectorKey& rhs) const {
+        return rhs.mValue == this->mValue;
     }
-    bool operator != (const aiVectorKey& o) const {
-        return o.mValue != this->mValue;
+    bool operator != (const aiVectorKey& rhs ) const {
+        return rhs.mValue != this->mValue;
     }
 
     // Relational operators. For use with std::sort();
-    bool operator < (const aiVectorKey& o) const {
-        return mTime < o.mTime;
+    bool operator < (const aiVectorKey& rhs ) const {
+        return mTime < rhs.mTime;
     }
-    bool operator > (const aiVectorKey& o) const {
-        return mTime > o.mTime;
+    bool operator > (const aiVectorKey& rhs ) const {
+        return mTime > rhs.mTime;
     }
 #endif // __cplusplus
 };
@@ -130,25 +131,25 @@ struct aiQuatKey
     typedef aiQuaternion elem_type;
 
     // Comparison operators. For use with std::find();
-    bool operator == (const aiQuatKey& o) const {
-        return o.mValue == this->mValue;
+    bool operator == (const aiQuatKey& rhs ) const {
+        return rhs.mValue == this->mValue;
     }
-    bool operator != (const aiQuatKey& o) const {
-        return o.mValue != this->mValue;
+    bool operator != (const aiQuatKey& rhs ) const {
+        return rhs.mValue != this->mValue;
     }
 
     // Relational operators. For use with std::sort();
-    bool operator < (const aiQuatKey& o) const {
-        return mTime < o.mTime;
+    bool operator < (const aiQuatKey& rhs ) const {
+        return mTime < rhs.mTime;
     }
-    bool operator > (const aiQuatKey& o) const {
-        return mTime > o.mTime;
+    bool operator > (const aiQuatKey& rhs ) const {
+        return mTime > rhs.mTime;
     }
 #endif
 };
 
 // ---------------------------------------------------------------------------
-/** Binds a anim mesh to a specific point in time. */
+/** Binds a anim-mesh to a specific point in time. */
 struct aiMeshKey
 {
     /** The time of this key */

+ 37 - 6
include/assimp/material.h

@@ -608,16 +608,17 @@ struct aiMaterialProperty
 #ifdef __cplusplus
 
     aiMaterialProperty()
-        : mSemantic( 0 )
-        , mIndex( 0 )
-        , mDataLength( 0 )
-        , mType( aiPTI_Float )
-        , mData( NULL )
-    {
+    : mSemantic( 0 )
+    , mIndex( 0 )
+    , mDataLength( 0 )
+    , mType( aiPTI_Float )
+    , mData(nullptr) {
+        // empty
     }
 
     ~aiMaterialProperty()   {
         delete[] mData;
+        mData = nullptr;
     }
 
 #endif
@@ -651,6 +652,14 @@ public:
     aiMaterial();
     ~aiMaterial();
 
+    // -------------------------------------------------------------------
+    /**
+      * @brief  Returns the name of the material.
+      * @return The name of the material.
+      */
+    // -------------------------------------------------------------------
+    aiString GetName();
+
     // -------------------------------------------------------------------
     /** @brief Retrieve an array of Type values with a specific key
      *  from the material
@@ -1556,10 +1565,32 @@ C_ENUM aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat,
     unsigned int* flags                 /*= NULL*/);
 #endif // !#ifdef __cplusplus
 
+// ---------------------------------------------------------------------------
+/** @brief  Helper function to get all values pertaining to a particular
+*  texture slot from a material structure.
+*
+*  @return Pointer showing to the default material.
+*/
+// ---------------------------------------------------------------------------
+#ifdef __cplusplus
+ASSIMP_API aiMaterial *aiCreateAndRegisterDefaultMaterial(void);
+#else
+C_STRUCT aiMaterial *aiCreateAndRegisterDefaultMaterial(void);
+#endif // !#ifdef __cplusplus
+
+// ---------------------------------------------------------------------------
+/**
+  * @brief  Helper function to release the default material instance, the
+  *         instance will not be destroyed.
+  */
+// ---------------------------------------------------------------------------
+ASSIMP_API void aiReleaseDefaultMaterial();
+
 #ifdef __cplusplus
 }
 
 #include "material.inl"
 
 #endif //!__cplusplus
+
 #endif //!!AI_MATERIAL_H_INC

+ 2 - 2
include/assimp/material.inl

@@ -120,7 +120,7 @@ inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
     const aiMaterialProperty* prop;
     const aiReturn ret = ::aiGetMaterialProperty(this,pKey,type,idx,
         (const aiMaterialProperty**)&prop);
-    if ( AI_SUCCESS == ret )    {
+    if ( AI_SUCCESS == ret ) {
 
         if (prop->mDataLength < sizeof(Type)) {
             return AI_FAILURE;
@@ -130,7 +130,7 @@ inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
             return AI_FAILURE;
         }
 
-        ::memcpy(&pOut,prop->mData,sizeof(Type));
+        ::memcpy( &pOut, prop->mData, sizeof( Type ) );
     }
     return ret;
 }

+ 4 - 1
include/assimp/postprocess.h

@@ -555,10 +555,13 @@ enum aiPostProcessSteps
      *  of the imported model. And if so, it uses that.
      */
     aiProcess_EmbedTextures  = 0x10000000,
-
+        
     // aiProcess_GenEntityMeshes = 0x100000,
     // aiProcess_OptimizeAnimations = 0x200000
     // aiProcess_FixTexturePaths = 0x200000
+
+
+    aiProcess_ForceGenNormals = 0x20000000,
 };
 
 

+ 14 - 6
include/assimp/qnan.h

@@ -98,8 +98,10 @@ AI_FORCE_INLINE bool is_qnan(float in)
     //   compare <register-with-different-width> against <in>
 
     // FIXME: Use <float> stuff instead? I think fpclassify needs C99
-    return (reinterpret_cast<_IEEESingle*>(&in)->IEEE.Exp == (1u << 8)-1 &&
-        reinterpret_cast<_IEEESingle*>(&in)->IEEE.Frac);
+    _IEEESingle temp;
+    memcpy(&temp, &in, sizeof(float));
+    return (temp.IEEE.Exp == (1u << 8)-1 &&
+        temp.IEEE.Frac);
 }
 
 // ---------------------------------------------------------------------------
@@ -114,8 +116,10 @@ AI_FORCE_INLINE bool is_qnan(double in)
     //   compare <register-with-different-width> against <in>
 
     // FIXME: Use <float> stuff instead? I think fpclassify needs C99
-    return (reinterpret_cast<_IEEEDouble*>(&in)->IEEE.Exp == (1u << 11)-1 &&
-        reinterpret_cast<_IEEEDouble*>(&in)->IEEE.Frac);
+    _IEEEDouble temp;
+    memcpy(&temp, &in, sizeof(in));
+    return (temp.IEEE.Exp == (1u << 11)-1 &&
+        temp.IEEE.Frac);
 }
 
 // ---------------------------------------------------------------------------
@@ -125,7 +129,9 @@ AI_FORCE_INLINE bool is_qnan(double in)
  *  @param in Input value */
 AI_FORCE_INLINE bool is_special_float(float in)
 {
-    return (reinterpret_cast<_IEEESingle*>(&in)->IEEE.Exp == (1u << 8)-1);
+    _IEEESingle temp;
+    memcpy(&temp, &in, sizeof(float));
+    return (temp.IEEE.Exp == (1u << 8)-1);
 }
 
 // ---------------------------------------------------------------------------
@@ -135,7 +141,9 @@ AI_FORCE_INLINE bool is_special_float(float in)
  *  @param in Input value */
 AI_FORCE_INLINE bool is_special_float(double in)
 {
-    return (reinterpret_cast<_IEEEDouble*>(&in)->IEEE.Exp == (1u << 11)-1);
+   _IEEESingle temp;
+    memcpy(&temp, &in, sizeof(float));
+    return (temp.IEEE.Exp == (1u << 11)-1);
 }
 
 // ---------------------------------------------------------------------------

+ 26 - 36
packaging/windows-innosetup/script.iss

@@ -2,7 +2,7 @@
 
 [Setup]
 AppName=Open Asset Import Library - SDK
-AppVerName=Open Asset Import Library - SDK (v3.3.1)
+AppVerName=Open Asset Import Library - SDK (v4.1.0)
 DefaultDirName={pf}\Assimp
 DefaultGroupName=Assimp
 UninstallDisplayIcon={app}\bin\x86\assimp.exe
@@ -12,9 +12,9 @@ SetupIconFile=..\..\tools\shared\assimp_tools_icon.ico
 WizardImageFile=compiler:WizModernImage-IS.BMP
 WizardSmallImageFile=compiler:WizModernSmallImage-IS.BMP
 LicenseFile=License.rtf
-OutputBaseFileName=assimp-sdk-3.3.1-setup
-VersionInfoVersion=3.3.1.0
-VersionInfoTextVersion=3.3.1
+OutputBaseFileName=assimp-sdk-4.1.0-setup
+VersionInfoVersion=4.1.0.0
+VersionInfoTextVersion=4.1.0
 VersionInfoCompany=Assimp Development Team
 ArchitecturesInstallIn64BitMode=x64
 
@@ -30,20 +30,19 @@ Name: "help";        Description: "Help Files"; Types: full compact
 Name: "samples";     Description: "Samples"; Types: full
 Name: "test";        Description: "Test Models (BSD-licensed)"; Types: full
 Name: "test_nonbsd"; Description: "Test Models (other (free) licenses)"; Types: full
-Name: "pyassimp";    Description: "Python Bindings"; Types: full
-Name: "dassimp";     Description: "D Bindings"; Types: full
-Name: "assimp_net";  Description: "C#/.NET Bindings"; Types: full
+;Name: "pyassimp";    Description: "Python Bindings"; Types: full
+;Name: "dassimp";     Description: "D Bindings"; Types: full
+;Name: "assimp_net";  Description: "C#/.NET Bindings"; Types: full
 
 [Run]
-Filename: "{app}\stub\vc_redist.x86.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2015 redistributable package (32 Bit)"; Check: not IsWin64
-Filename: "{app}\stub\vc_redist.x64.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2015 redistributable package (64 Bit)"; Check: IsWin64
+;Filename: "{app}\stub\vc_redist.x86.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2017 redistributable package (32 Bit)"; Check: not IsWin64
+Filename: "{app}\stub\vc_redist.x64.exe"; Parameters: "/qb"; StatusMsg: "Installing VS2017 redistributable package (64 Bit)"; Check: IsWin64
 
 [Files]
-
 Source: "readme_installer.txt"; DestDir: "{app}"; Flags: isreadme
 
 ; Installer stub
-Source: "vc_redist.x86.exe"; DestDir: "{app}\stub\"; Check: not IsWin64
+;Source: "vc_redist.x86.exe"; DestDir: "{app}\stub\"; Check: not IsWin64
 Source: "vc_redist.x64.exe"; DestDir: "{app}\stub\"; Check: IsWin64
 
 ; Common stuff
@@ -55,27 +54,27 @@ Source: "WEB"; DestDir: "{app}"
 Source: "..\..\scripts\*"; DestDir: "{app}\scripts"; Flags: recursesubdirs
 
 ; x86 binaries
-Source: "..\..\bin\release\x86\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x86"
-Source: "..\..\bin\release\x86\assimp_viewer.exe";      DestDir: "{app}\bin\x86"; Components: tools
-Source: "D3DCompiler_42.dll";                         DestDir: "{app}\bin\x86"; Components: tools
-Source: "D3DX9_42.dll";                               DestDir: "{app}\bin\x86"; Components: tools
-Source: "..\..\bin\release\x86\assimp.exe";           DestDir: "{app}\bin\x86"; Components: tools
+;Source: "..\..\bin\release\x86\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x86"
+;Source: "..\..\bin\release\x86\assimp_viewer.exe";    DestDir: "{app}\bin\x86"; Components: tools
+;Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll";     DestDir: "{app}\bin\x86"; Components: tools
+;Source: "C:\Windows\SysWOW64\D3DX9_42.dll";           DestDir: "{app}\bin\x86"; Components: tools
+;Source: "..\..\bin\release\x86\assimp.exe";           DestDir: "{app}\bin\x86"; Components: tools
 
 ; x64 binaries
-Source: "..\..\bin\release\x64\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x64"
-Source: "..\..\bin\release\x64\assimp_viewer.exe";      DestDir: "{app}\bin\x64"; Components: tools
-Source: "D3DCompiler_42_x64.dll";                     DestDir: "{app}\bin\x64"; DestName: "D3DCompiler_42.dll"; Components: tools
-Source: "D3DX9_42_x64.dll";                           DestDir: "{app}\bin\x64"; DestName: "D3DX9_42.dll"; Components: tools
-Source: "..\..\bin\release\x64\assimp.exe";           DestDir: "{app}\bin\x64"; Components: tools
+Source: "..\..\bin\release\assimp-vc140-mt.dll";  DestDir: "{app}\bin\x64"
+Source: "..\..\bin\release\assimp_viewer.exe";    DestDir: "{app}\bin\x64"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DCompiler_42.dll"; DestDir: "{app}\bin\x64"; DestName: "D3DCompiler_42.dll"; Components: tools
+Source: "C:\Windows\SysWOW64\D3DX9_42.dll";       DestDir: "{app}\bin\x64"; DestName: "D3DX9_42.dll"; Components: tools
+Source: "..\..\bin\release\assimp.exe";           DestDir: "{app}\bin\x64"; Components: tools
 
 ; Documentation
-Source: "..\..\doc\AssimpDoc_Html\AssimpDoc.chm"; DestDir: "{app}\doc"; Components: help
-Source: "..\..\doc\AssimpCmdDoc_Html\AssimpCmdDoc.chm"; DestDir: "{app}\doc"; Components: help
-Source: "..\..\doc\datastructure.xml"; DestDir: "{app}\doc"; Components: help
+;Source: "..\..\doc\AssimpDoc_Html\AssimpDoc.chm"; DestDir: "{app}\doc"; Components: help
+;Source: "..\..\doc\AssimpCmdDoc_Html\AssimpCmdDoc.chm"; DestDir: "{app}\doc"; Components: help
+;Source: "..\..\doc\datastructure.xml"; DestDir: "{app}\doc"; Components: help
 
 ; Import libraries
-Source: "..\..\lib\release\x86\assimp.lib"; DestDir: "{app}\lib\x86"
-Source: "..\..\lib\release\x64\assimp.lib"; DestDir: "{app}\lib\x64"
+;Source: "..\..\lib\release\x86\assimp.lib"; DestDir: "{app}\lib\x86"
+Source: "..\..\lib\release\assimp-vc140-mt.lib"; DestDir: "{app}\lib\x64"
 
 ; Samples
 Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Components: samples
@@ -84,7 +83,7 @@ Source: "..\..\samples\*"; DestDir: "{app}\samples"; Flags: recursesubdirs; Comp
 Source: "..\..\include\*"; DestDir: "{app}\include"; Flags: recursesubdirs
 
 ; dAssimp
-Source: "..\..\port\dAssimp\*"; DestDir: "{app}\port\D"; Flags: recursesubdirs; Components: dassimp
+;Source: "..\..\port\dAssimp\*"; DestDir: "{app}\port\D"; Flags: recursesubdirs; Components: dassimp
 
 ; Assimp.NET
 ;Source: "..\..\port\Assimp.NET\*"; DestDir: "{app}\port\C#"; Flags: recursesubdirs; Components: assimp_net
@@ -97,15 +96,6 @@ Source: "..\..\port\dAssimp\*"; DestDir: "{app}\port\D"; Flags: recursesubdirs;
 ;Source: "..\..\test\regression\*"; DestDir: "{app}\test\regression"; Flags: recursesubdirs; Components: test
 ;Source: "..\..\test\models-nonbsd\*"; DestDir: "{app}\test\models-nonbsd"; Flags: recursesubdirs; Components: test_nonbsd
 
-; Source Code & Workspaces
-;Source: "..\..\code\*"; Excludes: "*.o"; DestDir: "{app}\code"; Flags: recursesubdirs; Components: wsource
-;Source: "..\..\workspaces\vc8\*.sln"; DestDir: "{app}\workspaces\vc8"; Components: wsource and vc8
-;Source: "..\..\workspaces\vc8\*.vcproj"; DestDir: "{app}\workspaces\vc8"; Components: wsource and vc8
-;Source: "..\..\workspaces\vc9\*.sln"; DestDir: "{app}\workspaces\vc9"; Components: wsource and vc9
-;Source: "..\..\workspaces\vc9\*.vcproj"; DestDir: "{app}\workspaces\vc9"; Components: wsource and vc9
-
-; Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
-
 [Icons]
 Name: "{group}\Assimp Manual"; Filename: "{app}\doc\AssimpDoc.chm" ; Components: help
 Name: "{group}\Assimp Command Line Manual"; Filename: "{app}\doc\AssimpCmdDoc.chm"; Components: help

+ 1 - 1
port/AssimpNET/Readme.md

@@ -1 +1 @@
-See https://code.google.com/p/assimp-net/ and https://github.com/assimp/assimp-net for a Github mirror.
+Please check the following github-repo for the source: https://github.com/kebby/assimp-net

+ 0 - 94
port/PyAssimp/README.md

@@ -1,94 +0,0 @@
-PyAssimp Readme
-===============
-
-A simple Python wrapper for Assimp using `ctypes` to access the library.
-Requires Python >= 2.6.
-
-Python 3 support is mostly here, but not well tested.
-
-Note that pyassimp is not complete. Many ASSIMP features are missing.
-
-USAGE
------
-
-### Complete example: 3D viewer
-
-`pyassimp` comes with a simple 3D viewer that shows how to load and display a 3D
-model using a shader-based OpenGL pipeline.
-
-![Screenshot](3d_viewer_screenshot.png)
-
-To use it, from within `/port/PyAssimp`:
-
-```
-$ cd scripts
-$ python ./3D-viewer <path to your model>
-```
-
-You can use this code as starting point in your applications.
-
-### Writing your own code
-
-To get started with `pyassimp`, examine the simpler `sample.py` script in `scripts/`,
-which illustrates the basic usage. All Assimp data structures are wrapped using
-`ctypes`. All the data+length fields in Assimp's data structures (such as
-`aiMesh::mNumVertices`, `aiMesh::mVertices`) are replaced by simple python
-lists, so you can call `len()` on them to get their respective size and access
-members using `[]`.
-
-For example, to load a file named `hello.3ds` and print the first
-vertex of the first mesh, you would do (proper error handling
-substituted by assertions ...):
-
-```python
-
-from pyassimp import *
-scene = load('hello.3ds')
-
-assert len(scene.meshes)
-mesh = scene.meshes[0]
-
-assert len(mesh.vertices)
-print(mesh.vertices[0])
-
-# don't forget this one, or you will leak!
-release(scene)
-
-```
-
-Another example to list the 'top nodes' in a
-scene:
-
-```python
-
-from pyassimp import *
-scene = load('hello.3ds')
-
-for c in scene.rootnode.children:
-    print(str(c))
-
-release(scene)
-
-```
-
-INSTALL
--------
-
-Install `pyassimp` by running:
-
-```
-$ python setup.py install
-```
-
-PyAssimp requires a assimp dynamic library (`DLL` on windows,
-`.so` on linux, `.dynlib` on macOS) in order to work. The default search directories 
-are:
-
-- the current directory
-- on linux additionally: `/usr/lib`, `/usr/local/lib`,
-  `/usr/lib/x86_64-linux-gnu`
-
-To build that library, refer to the Assimp master `INSTALL`
-instructions. To look in more places, edit `./pyassimp/helper.py`.
-There's an `additional_dirs` list waiting for your entries.
-

+ 96 - 0
port/PyAssimp/README.rst

@@ -0,0 +1,96 @@
+PyAssimp: Python bindings for libassimp
+=======================================
+
+A simple Python wrapper for Assimp using ``ctypes`` to access the
+library. Requires Python >= 2.6.
+
+Python 3 support is mostly here, but not well tested.
+
+Note that pyassimp is not complete. Many ASSIMP features are missing.
+
+USAGE
+-----
+
+Complete example: 3D viewer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``pyassimp`` comes with a simple 3D viewer that shows how to load and
+display a 3D model using a shader-based OpenGL pipeline.
+
+.. figure:: 3d_viewer_screenshot.png
+   :alt: Screenshot
+
+   Screenshot
+
+To use it, from within ``/port/PyAssimp``:
+
+::
+
+    $ cd scripts
+    $ python ./3D-viewer <path to your model>
+
+You can use this code as starting point in your applications.
+
+Writing your own code
+~~~~~~~~~~~~~~~~~~~~~
+
+To get started with ``pyassimp``, examine the simpler ``sample.py``
+script in ``scripts/``, which illustrates the basic usage. All Assimp
+data structures are wrapped using ``ctypes``. All the data+length fields
+in Assimp's data structures (such as ``aiMesh::mNumVertices``,
+``aiMesh::mVertices``) are replaced by simple python lists, so you can
+call ``len()`` on them to get their respective size and access members
+using ``[]``.
+
+For example, to load a file named ``hello.3ds`` and print the first
+vertex of the first mesh, you would do (proper error handling
+substituted by assertions ...):
+
+.. code:: python
+
+
+    from pyassimp import *
+    scene = load('hello.3ds')
+
+    assert len(scene.meshes)
+    mesh = scene.meshes[0]
+
+    assert len(mesh.vertices)
+    print(mesh.vertices[0])
+
+    # don't forget this one, or you will leak!
+    release(scene)
+
+Another example to list the 'top nodes' in a scene:
+
+.. code:: python
+
+
+    from pyassimp import *
+    scene = load('hello.3ds')
+
+    for c in scene.rootnode.children:
+        print(str(c))
+
+    release(scene)
+
+INSTALL
+-------
+
+Install ``pyassimp`` by running:
+
+::
+
+    $ python setup.py install
+
+PyAssimp requires a assimp dynamic library (``DLL`` on windows, ``.so``
+on linux, ``.dynlib`` on macOS) in order to work. The default search
+directories are:
+
+-  the current directory
+-  on linux additionally: ``/usr/lib``, ``/usr/local/lib``,
+   ``/usr/lib/x86_64-linux-gnu``
+
+To build that library, refer to the Assimp master ``INSTALL``
+instructions. To look in more places, edit ``./pyassimp/helper.py``.
+There's an ``additional_dirs`` list waiting for your entries.

+ 11 - 5
port/PyAssimp/pyassimp/helper.py

@@ -27,7 +27,7 @@ additional_dirs, ext_whitelist = [],[]
 if os.name=='posix':
     additional_dirs.append('./')
     additional_dirs.append('/usr/lib/')
-    additional_dirs.append('/usr/lib/x86_64-linux-gnu')
+    additional_dirs.append('/usr/lib/x86_64-linux-gnu/')
     additional_dirs.append('/usr/local/lib/')
 
     if 'LD_LIBRARY_PATH' in os.environ:
@@ -222,11 +222,17 @@ def search_library():
             for filename in os.listdir(curfolder):
                 # our minimum requirement for candidates is that
                 # they should contain 'assimp' somewhere in
-                # their name
-                if filename.lower().find('assimp')==-1 or\
-                    os.path.splitext(filename)[-1].lower() not in ext_whitelist:
+                # their name                                  
+                if filename.lower().find('assimp')==-1 : 
                     continue
-
+                is_out=1
+                for et in ext_whitelist:
+                  if et in filename.lower():
+                    is_out=0
+                    break
+                if is_out:
+                  continue
+                
                 library_path = os.path.join(curfolder, filename)
                 logger.debug('Try ' + library_path)
                 try:

+ 12 - 2
port/PyAssimp/setup.py

@@ -1,15 +1,25 @@
+ #!/usr/bin/env python
  # -*- coding: utf-8 -*-
 import os
 from distutils.core import setup
 
+def readme():
+    with open('README.rst') as f:
+        return f.read()
+
 setup(name='pyassimp',
-      version='4.1.0',
+      version='4.1.3',
       license='ISC',
       description='Python bindings for the Open Asset Import Library (ASSIMP)',
+      long_description=readme(),
       url='https://github.com/assimp/assimp',
+      author='ASSIMP developers',
+      author_email='[email protected]',
+      maintainer='Séverin Lemaignan',
+      maintainer_email='[email protected]',
       packages=['pyassimp'],
       data_files=[
-                  ('share/pyassimp', ['README.md']),
+                  ('share/pyassimp', ['README.rst']),
                   ('share/examples/pyassimp', ['scripts/' + f for f in os.listdir('scripts/')])
                  ],
       requires=['numpy']

+ 6 - 8
port/iOS/IPHONEOS_ARM64_TOOLCHAIN.cmake

@@ -3,17 +3,15 @@ INCLUDE(CMakeForceCompiler)
 SET (CMAKE_CROSSCOMPILING   TRUE)
 SET (CMAKE_SYSTEM_NAME      "Darwin")
 SET (CMAKE_SYSTEM_PROCESSOR "arm64")
+SET (IOS TRUE)
 
-SET (SDKVER     "7.1")
-SET (DEVROOT    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain")
-SET (SDKROOT    "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${SDKVER}.sdk")
-SET (CC "${DEVROOT}/usr/bin/clang")
-SET (CXX "${DEVROOT}/usr/bin/clang++")
+SET (IOS_SDK_DEVICE iPhoneOS)
 
-CMAKE_FORCE_C_COMPILER          (${CC} LLVM)
-CMAKE_FORCE_CXX_COMPILER        (${CXX} LLVM)
+SET (SDKVER     "${IOS_SDK_VERSION}")
+SET (DEVROOT    "${XCODE_ROOT_DIR}/Platforms/${IOS_SDK_DEVICE}.platform/Developer")
 
-SET (CMAKE_FIND_ROOT_PATH               "${SDKROOT}" "${DEVROOT}")
+
+SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "${DEVROOT}")
 SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM  NEVER)
 SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY  ONLY)
 SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE  ONLY)

+ 6 - 8
port/iOS/IPHONEOS_ARMV6_TOOLCHAIN.cmake

@@ -3,17 +3,15 @@ INCLUDE(CMakeForceCompiler)
 SET (CMAKE_CROSSCOMPILING   TRUE)
 SET (CMAKE_SYSTEM_NAME      "Darwin")
 SET (CMAKE_SYSTEM_PROCESSOR "armv6")
+SET (IOS TRUE)
 
-SET (SDKVER     "7.1")
-SET (DEVROOT    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain")
-SET (SDKROOT    "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${SDKVER}.sdk")
-SET (CC         "${DEVROOT}/usr/bin/clang")
-SET (CXX        "${DEVROOT}/usr/bin/clang++")
+SET (IOS_SDK_DEVICE iPhoneOS)
 
-CMAKE_FORCE_C_COMPILER          (${CC} LLVM)
-CMAKE_FORCE_CXX_COMPILER        (${CXX} LLVM)
+SET (SDKVER     "${IOS_SDK_VERSION}")
+SET (DEVROOT    "${XCODE_ROOT_DIR}/Platforms/${IOS_SDK_DEVICE}.platform/Developer")
 
-SET (CMAKE_FIND_ROOT_PATH               "${SDKROOT}" "${DEVROOT}")
+
+SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "${DEVROOT}")
 SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM  NEVER)
 SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY  ONLY)
 SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE  ONLY)

+ 6 - 8
port/iOS/IPHONEOS_ARMV7S_TOOLCHAIN.cmake

@@ -3,17 +3,15 @@ INCLUDE(CMakeForceCompiler)
 SET (CMAKE_CROSSCOMPILING   TRUE)
 SET (CMAKE_SYSTEM_NAME      "Darwin")
 SET (CMAKE_SYSTEM_PROCESSOR "armv7s")
+SET (IOS TRUE)
 
-SET (SDKVER     "7.1")
-SET (DEVROOT    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain")
-SET (SDKROOT    "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${SDKVER}.sdk")
-SET (CC         "${DEVROOT}/usr/bin/clang")
-SET (CXX        "${DEVROOT}/usr/bin/clang++")
+SET (IOS_SDK_DEVICE iPhoneOS)
 
-CMAKE_FORCE_C_COMPILER          (${CC} LLVM)
-CMAKE_FORCE_CXX_COMPILER        (${CXX} LLVM)
+SET (SDKVER     "${IOS_SDK_VERSION}")
+SET (DEVROOT    "${XCODE_ROOT_DIR}/Platforms/${IOS_SDK_DEVICE}.platform/Developer")
 
-SET (CMAKE_FIND_ROOT_PATH               "${SDKROOT}" "${DEVROOT}")
+
+SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "${DEVROOT}")
 SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM  NEVER)
 SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY  ONLY)
 SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE  ONLY)

+ 6 - 8
port/iOS/IPHONEOS_ARMV7_TOOLCHAIN.cmake

@@ -3,17 +3,15 @@ INCLUDE(CMakeForceCompiler)
 SET (CMAKE_CROSSCOMPILING   TRUE)
 SET (CMAKE_SYSTEM_NAME      "Darwin")
 SET (CMAKE_SYSTEM_PROCESSOR "armv7")
+SET (IOS TRUE)
 
-SET (SDKVER     "7.1")
-SET (DEVROOT    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain")
-SET (SDKROOT    "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS${SDKVER}.sdk")
-SET (CC         "${DEVROOT}/usr/bin/clang")
-SET (CXX        "${DEVROOT}/usr/bin/clang++")
+SET (IOS_SDK_DEVICE iPhoneOS)
 
-CMAKE_FORCE_C_COMPILER          (${CC} LLVM)
-CMAKE_FORCE_CXX_COMPILER        (${CXX} LLVM)
+SET (SDKVER     "${IOS_SDK_VERSION}")
+SET (DEVROOT    "${XCODE_ROOT_DIR}/Platforms/${IOS_SDK_DEVICE}.platform/Developer")
 
-SET (CMAKE_FIND_ROOT_PATH               "${SDKROOT}" "${DEVROOT}")
+
+SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "${DEVROOT}")
 SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM  NEVER)
 SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY  ONLY)
 SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE  ONLY)

+ 5 - 8
port/iOS/IPHONEOS_I386_TOOLCHAIN.cmake

@@ -3,18 +3,15 @@ INCLUDE(CMakeForceCompiler)
 SET (CMAKE_CROSSCOMPILING   TRUE)
 SET (CMAKE_SYSTEM_NAME      "Darwin")
 SET (CMAKE_SYSTEM_PROCESSOR "i386")
+SET (IOS TRUE)
 
-SET (SDKVER     "7.1")
+SET (IOS_SDK_DEVICE iPhoneSimulator)
 
-SET (DEVROOT    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain")
-SET (SDKROOT    "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${SDKVER}.sdk")
-SET (CC         "${DEVROOT}/usr/bin/clang")
-SET (CXX        "${DEVROOT}/usr/bin/clang++")
+SET (SDKVER     "${IOS_SDK_VERSION}")
+SET (DEVROOT    "${XCODE_ROOT_DIR}/Platforms/${IOS_SDK_DEVICE}.platform/Developer")
 
-CMAKE_FORCE_C_COMPILER          (${CC} LLVM)
-CMAKE_FORCE_CXX_COMPILER        (${CXX} LLVM)
 
-SET (CMAKE_FIND_ROOT_PATH               "${SDKROOT}" "${DEVROOT}")
+SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "${DEVROOT}")
 SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM  NEVER)
 SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY  ONLY)
 SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE  ONLY)

+ 5 - 8
port/iOS/IPHONEOS_X86_64_TOOLCHAIN.cmake

@@ -3,18 +3,15 @@ INCLUDE(CMakeForceCompiler)
 SET (CMAKE_CROSSCOMPILING   TRUE)
 SET (CMAKE_SYSTEM_NAME      "Darwin")
 SET (CMAKE_SYSTEM_PROCESSOR "x86_64")
+SET (IOS TRUE)
 
-SET (SDKVER     "7.1")
+SET (IOS_SDK_DEVICE iPhoneSimulator)
 
-SET (DEVROOT    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain")
-SET (SDKROOT    "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${SDKVER}.sdk")
-SET (CC         "${DEVROOT}/usr/bin/clang")
-SET (CXX        "${DEVROOT}/usr/bin/clang++")
+SET (SDKVER     "${IOS_SDK_VERSION}")
+SET (DEVROOT    "${XCODE_ROOT_DIR}/Platforms/${IOS_SDK_DEVICE}.platform/Developer")
 
-CMAKE_FORCE_C_COMPILER          (${CC} LLVM)
-CMAKE_FORCE_CXX_COMPILER        (${CXX} LLVM)
 
-SET (CMAKE_FIND_ROOT_PATH               "${SDKROOT}" "${DEVROOT}")
+SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "${DEVROOT}")
 SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM  NEVER)
 SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY  ONLY)
 SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE  ONLY)

+ 98 - 29
port/iOS/build.sh

@@ -6,24 +6,41 @@
 
 BUILD_DIR="./lib/iOS"
 
-IOS_SDK_VERSION=
-IOS_SDK_TARGET=6.0
-#(iPhoneOS iPhoneSimulator) -- determined from arch
-IOS_SDK_DEVICE=
+###################################
+# 		 SDK Version
+###################################
+IOS_SDK_VERSION=$(xcodebuild -version -sdk iphoneos | grep SDKVersion | cut -f2 -d ':' | tr -d '[[:space:]]')
+###################################
 
-XCODE_ROOT_DIR=/Applications/Xcode.app/Contents
-TOOLCHAIN=$XCODE_ROOT_DIR//Developer/Toolchains/XcodeDefault.xctoolchain
+###################################
+# 		 BUILD Configuration
+###################################
 
-BUILD_ARCHS_DEVICE="armv7 armv7s arm64"
-BUILD_ARCHS_SIMULATOR="i386 x86_64"
-BUILD_ARCHS_ALL=(armv7 armv7s arm64 i386 x86_64)
+BUILD_SHARED_LIBS=OFF
+BUILD_TYPE=Release
+
+################################################
+# 		 Minimum iOS deployment target version
+################################################
+MIN_IOS_VERSION="6.0"
+
+IOS_SDK_TARGET=$MIN_IOS_VERSION
+XCODE_ROOT_DIR=$(xcode-select  --print-path)
+TOOLCHAIN=$XCODE_ROOT_DIR/Toolchains/XcodeDefault.xctoolchain
+
+CMAKE_C_COMPILER=$(xcrun -find cc)
+CMAKE_CXX_COMPILER=$(xcrun -find c++)
+
+BUILD_ARCHS_DEVICE="arm64 armv7s armv7"
+BUILD_ARCHS_SIMULATOR="x86_64 i386"
+BUILD_ARCHS_ALL=($BUILD_ARCHS_DEVICE $BUILD_ARCHS_SIMULATOR)
 
 CPP_DEV_TARGET_LIST=(miphoneos-version-min mios-simulator-version-min)
 CPP_DEV_TARGET=
 CPP_STD_LIB_LIST=(libc++ libstdc++)
 CPP_STD_LIB=
 CPP_STD_LIST=(c++11 c++14)
-CPP_STD=
+CPP_STD=c++11
 
 function join { local IFS="$1"; shift; echo "$*"; }
 
@@ -41,27 +58,42 @@ build_arch()
         echo '[!] Target SDK set to DEVICE.'
     fi
 
-    unset DEVROOT SDKROOT CFLAGS LDFLAGS CPPFLAGS CXXFLAGS
-
-    export DEVROOT=$XCODE_ROOT_DIR/Developer/Platforms/$IOS_SDK_DEVICE.platform/Developer
+    unset DEVROOT SDKROOT CFLAGS LDFLAGS CPPFLAGS CXXFLAGS CMAKE_CLI_INPUT
+           
+	#export CC="$(xcrun -sdk iphoneos -find clang)"
+    #export CPP="$CC -E"
+    export DEVROOT=$XCODE_ROOT_DIR/Platforms/$IOS_SDK_DEVICE.platform/Developer
     export SDKROOT=$DEVROOT/SDKs/$IOS_SDK_DEVICE$IOS_SDK_VERSION.sdk
-    export CFLAGS="-arch $1 -pipe -no-cpp-precomp -stdlib=$CPP_STD_LIB -isysroot $SDKROOT -$CPP_DEV_TARGET=$IOS_SDK_TARGET -I$SDKROOT/usr/include/"
-    export LDFLAGS="-L$SDKROOT/usr/lib/"
-    export CPPFLAGS=$CFLAGS
+    export CFLAGS="-arch $1 -pipe -no-cpp-precomp -stdlib=$CPP_STD_LIB -isysroot $SDKROOT -I$SDKROOT/usr/include/ -miphoneos-version-min=$IOS_SDK_TARGET"
+     if [[ "$BUILD_TYPE" =~ "Debug" ]]; then
+      export CFLAGS="$CFLAGS -Og"
+     else
+	     export CFLAGS="$CFLAGS -O3"
+     fi
+    export LDFLAGS="-arch $1 -isysroot $SDKROOT -L$SDKROOT/usr/lib/"
+    export CPPFLAGS="$CFLAGS"
     export CXXFLAGS="$CFLAGS -std=$CPP_STD"
 
     rm CMakeCache.txt
-
-    cmake  -G 'Unix Makefiles' -DCMAKE_TOOLCHAIN_FILE=./port/iOS/IPHONEOS_$(echo $1 | tr '[:lower:]' '[:upper:]')_TOOLCHAIN.cmake -DENABLE_BOOST_WORKAROUND=ON -DBUILD_SHARED_LIBS=OFF
+    
+    CMAKE_CLI_INPUT="-DCMAKE_C_COMPILER=$CMAKE_C_COMPILER -DCMAKE_CXX_COMPILER=$CMAKE_CXX_COMPILER -DCMAKE_TOOLCHAIN_FILE=./port/iOS/IPHONEOS_$(echo $1 | tr '[:lower:]' '[:upper:]')_TOOLCHAIN.cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DENABLE_BOOST_WORKAROUND=ON -DBUILD_SHARED_LIBS=$BUILD_SHARED_LIBS"
+    
+    echo "[!] Running CMake with -G 'Unix Makefiles' $CMAKE_CLI_INPUT"
+    
+    cmake -G 'Unix Makefiles' ${CMAKE_CLI_INPUT}
 
     echo "[!] Building $1 library"
 
-    $XCODE_ROOT_DIR/Developer/usr/bin/make clean
-    $XCODE_ROOT_DIR/Developer/usr/bin/make assimp -j 8 -l
-
-    echo "[!] Moving built libraries into: $BUILD_DIR/$1/"
-
-    mv ./lib/*.a $BUILD_DIR/$1/
+    xcrun -run make clean
+    xcrun -run make assimp -j 8 -l    
+    
+    if [[ "$BUILD_SHARED_LIBS" =~ "ON" ]]; then
+    	echo "[!] Moving built dynamic libraries into: $BUILD_DIR/$1/"
+    	mv ./lib/*.dylib  $BUILD_DIR/$1/
+    fi
+    
+    echo "[!] Moving built static libraries into: $BUILD_DIR/$1/"
+    mv ./lib/*.a $BUILD_DIR/$1/	   
 }
 
 echo "[!] $0 - assimp iOS build script"
@@ -85,12 +117,22 @@ for i in "$@"; do
         DEPLOY_ARCHS=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
         echo "[!] Selecting architectures: $DEPLOY_ARCHS"
     ;;
+    --debug)
+    	BUILD_TYPE=Debug        
+        echo "[!] Selecting build type: Debug"
+    ;;
+    --shared-lib)
+    	BUILD_SHARED_LIBS=ON        
+        echo "[!] Will generate dynamic libraries"
+    ;;
     -n|--no-fat)
         DEPLOY_FAT=0
         echo "[!] Fat binary will not be created."
     ;;
     -h|--help)
         echo " - don't build fat library (--no-fat)."
+        echo " - Include debug information and symbols, no compiler optimizations (--debug)."
+        echo " - generate dynamic libraries rather than static ones (--shared-lib)."
         echo " - supported architectures (--archs):  $(echo $(join , ${BUILD_ARCHS_ALL[*]}) | sed 's/,/, /g')"
         echo " - supported C++ STD libs (--stdlib): $(echo $(join , ${CPP_STD_LIB_LIST[*]}) | sed 's/,/, /g')"
         echo " - supported C++ standards (--std): $(echo $(join , ${CPP_STD_LIST[*]}) | sed 's/,/, /g')"
@@ -105,29 +147,56 @@ cd ../../
 rm -rf $BUILD_DIR
 
 for ARCH_TARGET in $DEPLOY_ARCHS; do
+	echo "Creating folder: $BUILD_DIR/$ARCH_TARGET"
     mkdir -p $BUILD_DIR/$ARCH_TARGET
+    echo "Building for arc: $ARCH_TARGET" 
     build_arch $ARCH_TARGET
     #rm ./lib/libassimp.a
 done
 
 
-make_fat_binary()
+make_fat_static_or_shared_binary()
+{
+	LIB_NAME=$1
+	LIPO_ARGS=''
+    for ARCH_TARGET in $DEPLOY_ARCHS; do
+        if [[ "$BUILD_SHARED_LIBS" =~ "ON" ]]; then
+            LIPO_ARGS="$LIPO_ARGS-arch $ARCH_TARGET $BUILD_DIR/$ARCH_TARGET/$LIB_NAME.dylib "
+        else
+            LIPO_ARGS="$LIPO_ARGS-arch $ARCH_TARGET $BUILD_DIR/$ARCH_TARGET/$LIB_NAME.a "
+        fi
+    done
+    if [[ "$BUILD_SHARED_LIBS" =~ "ON" ]]; then
+    	LIPO_ARGS="$LIPO_ARGS -create -output $BUILD_DIR/$LIB_NAME-fat.dylib"
+    else
+    	LIPO_ARGS="$LIPO_ARGS -create -output $BUILD_DIR/$LIB_NAME-fat.a"
+    fi
+    lipo $LIPO_ARGS
+}
+
+make_fat_static_binary()
 {
 	LIB_NAME=$1
 	LIPO_ARGS=''
     for ARCH_TARGET in $DEPLOY_ARCHS; do
         LIPO_ARGS="$LIPO_ARGS-arch $ARCH_TARGET $BUILD_DIR/$ARCH_TARGET/$LIB_NAME.a "
     done
-    LIPO_ARGS="$LIPO_ARGS-create -output $BUILD_DIR/$LIB_NAME-fat.a"
+    LIPO_ARGS="$LIPO_ARGS -create -output $BUILD_DIR/$LIB_NAME-fat.a"
     lipo $LIPO_ARGS
 }
 
 if [[ "$DEPLOY_FAT" -eq 1 ]]; then
     echo '[+] Creating fat binaries ...'
     
-    make_fat_binary 'libassimp'
-    make_fat_binary 'libIrrXML'
-    make_fat_binary 'libzlibstatic'
+    if [[ "$BUILD_TYPE" =~ "Debug" ]]; then
+    	make_fat_static_or_shared_binary 'libassimpd'
+	    make_fat_static_binary 'libIrrXMLd'
+	    make_fat_static_binary 'libzlibstaticd'
+	else
+		make_fat_static_or_shared_binary 'libassimp'
+	    make_fat_static_binary 'libIrrXML'
+	    make_fat_static_binary 'libzlibstatic'
+	fi
     
     echo "[!] Done! The fat binaries can be found at $BUILD_DIR"
 fi

+ 1 - 0
test/CMakeLists.txt

@@ -106,6 +106,7 @@ SET( IMPORTERS
   unit/utBlendImportAreaLight.cpp
   unit/utBlenderImportExport.cpp
   unit/utBlendImportMaterials.cpp
+  unit/utBlenderWork.cpp
   unit/utBVHImportExport.cpp
   unit/utColladaExportCamera.cpp
   unit/utColladaExportLight.cpp

二進制
test/models/BLEND/plane_2_textures_2_texcoords_279.blend


二進制
test/models/BLEND/test_279.blend


二進制
test/models/IRR/instancing.irr


+ 7 - 0
test/models/OpenGEX/empty_camera.ogex

@@ -0,0 +1,7 @@
+CameraObject {
+    Param (attrib = "fov") { float { 0.97 } }
+    Param (attrib = "near") { float { 1.5 } }
+    Param (attrib = "far") { float { 150.0 } }
+}
+
+CameraObject {}

+ 21 - 0
test/models/glTF2/glTF-Asset-Generator/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Gary Hsu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.bin


+ 63 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf

@@ -0,0 +1,63 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 1024,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_00.bin",
+      "byteLength": 12288
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 12288,
+      "name": "Positions"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          },
+          "mode": 0
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.bin


+ 63 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf

@@ -0,0 +1,63 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 8,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_01.bin",
+      "byteLength": 96
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 96,
+      "name": "Positions"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          },
+          "mode": 1
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.bin


+ 63 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf

@@ -0,0 +1,63 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 4,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_02.bin",
+      "byteLength": 48
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 48,
+      "name": "Positions"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          },
+          "mode": 2
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.bin


+ 63 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf

@@ -0,0 +1,63 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 5,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_03.bin",
+      "byteLength": 60
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 60,
+      "name": "Positions"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          },
+          "mode": 3
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.bin


+ 63 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf

@@ -0,0 +1,63 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 4,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_04.bin",
+      "byteLength": 48
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 48,
+      "name": "Positions"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          },
+          "mode": 5
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.bin


+ 63 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf

@@ -0,0 +1,63 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 4,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_05.bin",
+      "byteLength": 48
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 48,
+      "name": "Positions"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          },
+          "mode": 6
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.bin


+ 62 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf

@@ -0,0 +1,62 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 6,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_06.bin",
+      "byteLength": 72
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 72,
+      "name": "Positions"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          }
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.bin


+ 77 - 0
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf

@@ -0,0 +1,77 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 1024,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ],
+      "name": "Positions Accessor"
+    },
+    {
+      "bufferView": 1,
+      "componentType": 5125,
+      "count": 1024,
+      "type": "SCALAR",
+      "name": "Indices Accessor"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Asset Generator",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Mesh_PrimitiveMode_07.bin",
+      "byteLength": 16384
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 12288,
+      "name": "Positions"
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 12288,
+      "byteLength": 4096,
+      "name": "Indices"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0
+          },
+          "indices": 1,
+          "mode": 0
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

二進制
test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.bin


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