Pārlūkot izejas kodu

Merge branch 'master' into ps-devel-fix-contrib-zlib-static-compile-001

Kim Kulling 5 gadi atpakaļ
vecāks
revīzija
ea26cba420
100 mainītis faili ar 8794 papildinājumiem un 4705 dzēšanām
  1. 1 0
      .github/FUNDING.yml
  2. 45 32
      .github/workflows/ccpp.yml
  3. 56 0
      .github/workflows/sanitizer.yml
  4. 24 27
      CMakeLists.txt
  5. 50 50
      INSTALL
  6. 23 0
      Readme.md
  7. 0 705
      code/AMF/AMFImporter.cpp
  8. 0 978
      code/AMF/AMFImporter_Postprocess.cpp
  9. 253 311
      code/AssetLib/3DS/3DSConverter.cpp
  10. 107 122
      code/AssetLib/3DS/3DSExporter.cpp
  11. 0 0
      code/AssetLib/3DS/3DSExporter.h
  12. 0 0
      code/AssetLib/3DS/3DSHelper.h
  13. 246 303
      code/AssetLib/3DS/3DSLoader.cpp
  14. 0 0
      code/AssetLib/3DS/3DSLoader.h
  15. 0 0
      code/AssetLib/3MF/3MFXmlTags.h
  16. 120 130
      code/AssetLib/3MF/D3MFExporter.cpp
  17. 0 0
      code/AssetLib/3MF/D3MFExporter.h
  18. 138 140
      code/AssetLib/3MF/D3MFImporter.cpp
  19. 0 0
      code/AssetLib/3MF/D3MFImporter.h
  20. 0 0
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  21. 0 0
      code/AssetLib/3MF/D3MFOpcPackage.h
  22. 37 37
      code/AssetLib/AC/ACLoader.cpp
  23. 33 62
      code/AssetLib/AC/ACLoader.h
  24. 672 0
      code/AssetLib/AMF/AMFImporter.cpp
  25. 0 0
      code/AssetLib/AMF/AMFImporter.hpp
  26. 1 1
      code/AssetLib/AMF/AMFImporter_Geometry.cpp
  27. 1 1
      code/AssetLib/AMF/AMFImporter_Macro.hpp
  28. 1 1
      code/AssetLib/AMF/AMFImporter_Material.cpp
  29. 1 1
      code/AssetLib/AMF/AMFImporter_Node.hpp
  30. 872 0
      code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  31. 271 285
      code/AssetLib/ASE/ASELoader.cpp
  32. 0 0
      code/AssetLib/ASE/ASELoader.h
  33. 242 371
      code/AssetLib/ASE/ASEParser.cpp
  34. 1 1
      code/AssetLib/ASE/ASEParser.h
  35. 0 1
      code/AssetLib/Assbin/AssbinExporter.cpp
  36. 0 0
      code/AssetLib/Assbin/AssbinExporter.h
  37. 29 30
      code/AssetLib/Assbin/AssbinFileWriter.cpp
  38. 6 7
      code/AssetLib/Assbin/AssbinFileWriter.h
  39. 25 25
      code/AssetLib/Assbin/AssbinLoader.cpp
  40. 0 0
      code/AssetLib/Assbin/AssbinLoader.h
  41. 0 0
      code/AssetLib/Assjson/cencode.c
  42. 0 0
      code/AssetLib/Assjson/cencode.h
  43. 83 94
      code/AssetLib/Assjson/json_exporter.cpp
  44. 0 0
      code/AssetLib/Assjson/mesh_splitter.cpp
  45. 0 0
      code/AssetLib/Assjson/mesh_splitter.h
  46. 0 0
      code/AssetLib/Assxml/AssxmlExporter.cpp
  47. 0 0
      code/AssetLib/Assxml/AssxmlExporter.h
  48. 659 0
      code/AssetLib/Assxml/AssxmlFileWriter.cpp
  49. 0 0
      code/AssetLib/Assxml/AssxmlFileWriter.h
  50. 744 0
      code/AssetLib/B3D/B3DImporter.cpp
  51. 0 0
      code/AssetLib/B3D/B3DImporter.h
  52. 543 0
      code/AssetLib/BVH/BVHLoader.cpp
  53. 22 29
      code/AssetLib/BVH/BVHLoader.h
  54. 62 84
      code/AssetLib/Blender/BlenderBMesh.cpp
  55. 0 0
      code/AssetLib/Blender/BlenderBMesh.h
  56. 181 0
      code/AssetLib/Blender/BlenderCustomData.cpp
  57. 0 0
      code/AssetLib/Blender/BlenderCustomData.h
  58. 74 89
      code/AssetLib/Blender/BlenderDNA.cpp
  59. 162 205
      code/AssetLib/Blender/BlenderDNA.h
  60. 1 1
      code/AssetLib/Blender/BlenderDNA.inl
  61. 0 0
      code/AssetLib/Blender/BlenderIntermediate.h
  62. 0 0
      code/AssetLib/Blender/BlenderLoader.cpp
  63. 0 0
      code/AssetLib/Blender/BlenderLoader.h
  64. 15 15
      code/AssetLib/Blender/BlenderModifier.cpp
  65. 0 0
      code/AssetLib/Blender/BlenderModifier.h
  66. 838 0
      code/AssetLib/Blender/BlenderScene.cpp
  67. 252 277
      code/AssetLib/Blender/BlenderScene.h
  68. 0 0
      code/AssetLib/Blender/BlenderSceneGen.h
  69. 0 0
      code/AssetLib/Blender/BlenderTessellator.cpp
  70. 0 0
      code/AssetLib/Blender/BlenderTessellator.h
  71. 0 0
      code/AssetLib/C4D/C4DImporter.cpp
  72. 0 0
      code/AssetLib/C4D/C4DImporter.h
  73. 248 287
      code/AssetLib/COB/COBLoader.cpp
  74. 0 0
      code/AssetLib/COB/COBLoader.h
  75. 0 0
      code/AssetLib/COB/COBScene.h
  76. 0 0
      code/AssetLib/CSM/CSMLoader.cpp
  77. 0 0
      code/AssetLib/CSM/CSMLoader.h
  78. 1651 0
      code/AssetLib/Collada/ColladaExporter.cpp
  79. 0 0
      code/AssetLib/Collada/ColladaExporter.h
  80. 0 0
      code/AssetLib/Collada/ColladaHelper.cpp
  81. 0 0
      code/AssetLib/Collada/ColladaHelper.h
  82. 0 0
      code/AssetLib/Collada/ColladaLoader.cpp
  83. 0 0
      code/AssetLib/Collada/ColladaLoader.h
  84. 0 0
      code/AssetLib/Collada/ColladaParser.cpp
  85. 0 0
      code/AssetLib/Collada/ColladaParser.h
  86. 0 0
      code/AssetLib/DXF/DXFHelper.h
  87. 2 2
      code/AssetLib/DXF/DXFLoader.cpp
  88. 0 0
      code/AssetLib/DXF/DXFLoader.h
  89. 0 0
      code/AssetLib/FBX/FBXAnimation.cpp
  90. 0 0
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  91. 0 0
      code/AssetLib/FBX/FBXCommon.h
  92. 0 0
      code/AssetLib/FBX/FBXCompileConfig.h
  93. 2 1
      code/AssetLib/FBX/FBXConverter.cpp
  94. 0 0
      code/AssetLib/FBX/FBXConverter.h
  95. 0 0
      code/AssetLib/FBX/FBXDeformer.cpp
  96. 0 0
      code/AssetLib/FBX/FBXDocument.cpp
  97. 0 0
      code/AssetLib/FBX/FBXDocument.h
  98. 0 0
      code/AssetLib/FBX/FBXDocumentUtil.cpp
  99. 0 0
      code/AssetLib/FBX/FBXDocumentUtil.h
  100. 0 0
      code/AssetLib/FBX/FBXExportNode.cpp

+ 1 - 0
.github/FUNDING.yml

@@ -1,2 +1,3 @@
 patreon: assimp
 custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JRJVPXC4QJM4
+open_collective: assimp

+ 45 - 32
.github/workflows/ccpp.yml

@@ -7,40 +7,53 @@ on:
     branches: [ master ]
 
 jobs:
-  linux:
-    runs-on: ubuntu-latest
-    
+  job:
+    name: ${{ matrix.os }}-${{ matrix.cxx }}-build-and-test
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        name: [ubuntu-gcc, macos-clang, windows-msvc, ubuntu-clang]
+        # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
+        include:
+          - name: windows-msvc
+            os: windows-latest
+            cxx: cl.exe
+            cc: cl.exe
+          - name: ubuntu-clang
+            os: ubuntu-latest
+            cxx: clang++
+            cc: clang
+          - name: macos-clang
+            os: macos-latest
+            cxx: clang++
+            cc: clang
+          - name: ubuntu-gcc
+            os: ubuntu-latest
+            cxx: g++
+            cc: gcc
+
     steps:
     - uses: actions/checkout@v2
-    - name: configure
-      run: cmake CMakeLists.txt
-    - name: build
-      run: cmake --build .
-    - name: test
-      run: cd bin && ./unit
-  
-  mac:
-    runs-on: macos-latest
     
-    steps:
-    - uses: actions/checkout@v2
-    - name: configure
-      run: cmake CMakeLists.txt
-    - name: build
-      run: cmake --build .
-    - name: test
-      run: cd bin && ./unit
-
-  windows:
-    runs-on: windows-latest
+    - uses: lukka/get-cmake@latest
     
-    steps:
-    - uses: actions/checkout@v2
-    - name: configure
-      run: cmake CMakeLists.txt
-    - name: build
-      run: cmake --build . --config Release
+    - uses: ilammy/msvc-dev-cmd@v1
+    
+    - uses: lukka/set-shell-env@v1
+      with:
+        CXX: ${{ matrix.cxx }}
+        CC: ${{ matrix.cc }}
+    
+    - name: configure and build
+      uses: lukka/run-cmake@v2
+      with:
+        cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
+        cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
+        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Release'
+        buildWithCMakeArgs: '-- -v'
+        buildDirectory: '${{ github.workspace }}/build/'
+        
     - name: test
-      run: | 
-        cd bin\Release
-        .\unit
+      run: cd build/bin && ./unit
+      shell: bash

+ 56 - 0
.github/workflows/sanitizer.yml

@@ -0,0 +1,56 @@
+name: C/C++ Sanitizer
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  job1:
+    name: adress-sanitizer
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: lukka/get-cmake@latest    
+    - uses: lukka/set-shell-env@v1
+      with:
+        CXX: clang++
+        CC: clang
+    
+    - name: configure and build
+      uses: lukka/run-cmake@v2
+      with:
+        cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
+        cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
+        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Debug -DASSIMP_ASAN=ON'
+        buildWithCMakeArgs: '-- -v'
+        buildDirectory: '${{ github.workspace }}/build/'
+    
+    - name: test
+      run: cd build/bin && ./unit
+      shell: bash
+
+  job2:
+    name: undefined-behavior-sanitizer
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: lukka/get-cmake@latest    
+    - uses: lukka/set-shell-env@v1
+      with:
+        CXX: clang++
+        CC: clang
+    
+    - name: configure and build
+      uses: lukka/run-cmake@v2
+      with:
+        cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
+        cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
+        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Debug -DASSIMP_UBSAN=ON'
+        buildWithCMakeArgs: '-- -v'
+        buildDirectory: '${{ github.workspace }}/build/'
+    
+    - name: test
+      run: cd build/bin && ./unit
+      shell: bash

+ 24 - 27
CMakeLists.txt

@@ -108,10 +108,6 @@ OPTION ( ASSIMP_ERROR_MAX
   "Enable all warnings."
   OFF
 )
-OPTION ( ASSIMP_WERROR
-  "Treat warnings as errors."
-  OFF
-)
 OPTION ( ASSIMP_ASAN
   "Enable AddressSanitizer."
   OFF
@@ -138,6 +134,12 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
    OFF
 )
 
+IF ( WIN32 )
+    OPTION ( ASSIMP_BUILD_ASSIMP_VIEW 
+      "If the Assimp view tool is built. (requires DirectX)" 
+      OFF )
+ENDIF()
+
 IF (IOS AND NOT ASSIMP_HUNTER_ENABLED)
   IF (NOT CMAKE_BUILD_TYPE)
     SET(CMAKE_BUILD_TYPE "Release")
@@ -238,14 +240,19 @@ SET(LIBASSIMP-DEV_COMPONENT "libassimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_M
 SET(CPACK_COMPONENTS_ALL assimp-bin ${LIBASSIMP_COMPONENT} ${LIBASSIMP-DEV_COMPONENT} assimp-dev)
 SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names")
 
-# Grouped compiler settings
+IF( UNIX )
+  # Use GNUInstallDirs for Unix predefined directories
+  INCLUDE(GNUInstallDirs)
+ENDIF()
+
+# Grouped compiler settings ########################################
 IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
   IF(NOT ASSIMP_HUNTER_ENABLED)
-    SET(CMAKE_CXX_FLAGS "-fPIC -std=c++0x ${CMAKE_CXX_FLAGS}")
-    SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+    SET(CMAKE_CXX_STANDARD 11)
+    SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
   ENDIF()
   # hide all not-exported symbols
-  SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
+  SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
   SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
   SET(LIBSTDC++_LIBRARIES -lstdc++)
 ELSEIF(MSVC)
@@ -258,10 +265,10 @@ ELSEIF(MSVC)
   SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
 ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
   IF(NOT ASSIMP_HUNTER_ENABLED)
-    SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")
-    SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+    SET(CMAKE_CXX_STANDARD 11)
+    SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
   ENDIF()
-  SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
+  SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
 ELSEIF( CMAKE_COMPILER_IS_MINGW )
   IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
@@ -306,16 +313,6 @@ IF (ASSIMP_ERROR_MAX)
   ENDIF()
 ENDIF()
 
-IF (ASSIMP_WERROR)
-  MESSAGE(STATUS "Treating warnings as errors")
-  IF (MSVC)
-    ADD_COMPILE_OPTIONS(/WX)
-  ELSE()
-    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
-    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
-  ENDIF()
-ENDIF()
-
 IF (ASSIMP_ASAN)
   MESSAGE(STATUS "AddressSanitizer enabled")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
@@ -521,19 +518,19 @@ ENDIF()
 SET ( ASSIMP_BUILD_ARCHITECTURE "" CACHE STRING
   "describe the current architecture."
 )
-IF    ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
-ELSE  ()
+IF( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
+ELSE()
   ADD_DEFINITIONS ( -D'ASSIMP_BUILD_ARCHITECTURE="${ASSIMP_BUILD_ARCHITECTURE}"' )
-ENDIF ()
+ENDIF()
 
 # ${CMAKE_GENERATOR}
 SET ( ASSIMP_BUILD_COMPILER "" CACHE STRING
   "describe the current compiler."
 )
-IF    ( ASSIMP_BUILD_COMPILER STREQUAL "")
-ELSE  ()
+IF( ASSIMP_BUILD_COMPILER STREQUAL "")
+ELSE()
   ADD_DEFINITIONS ( -D'ASSIMP_BUILD_COMPILER="${ASSIMP_BUILD_COMPILER}"' )
-ENDIF ()
+ENDIF()
 
 MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
 

+ 50 - 50
INSTALL

@@ -1,50 +1,50 @@
-	
-========================================================================
-Open Asset Import Library (assimp) INSTALL 
-========================================================================
-
-------------------------------
-Getting the documentation
-------------------------------
-
-A regularly-updated copy is available at 
-http://assimp.sourceforge.net/lib_html/index.html
-
-A CHM file is included in the SVN repos: ./doc/AssimpDoc_Html/AssimpDoc.chm.
-To build the doxygen documentation on your own, follow these steps:
-
-a) download & install latest doxygen 
-b) make sure doxygen is in the executable search path
-c) navigate to ./doc
-d) and run 'doxygen'
-
-Open the generated HTML (AssimpDoc_Html/index.html) in the browser of your choice.
-Windows only: To generate the CHM doc, install 'Microsoft HTML Workshop'
-and configure the path to it in the DOXYFILE first. 
-
-------------------------------
-Building Assimp 
-------------------------------
-
-More detailed build instructions can be found in the documentation,
-this section is just for the inpatient among you.
-
-CMake is the preferred build system for Assimp. The minimum required version 
-is 2.6. If you don't have it yet, downloads for CMake can be found on
-http://www.cmake.org/. 
-
-For Unix:
-
-1. mkdir build && cd build
-2. cmake .. -G 'Unix Makefiles'
-3. make -j4
-
-For Windows:
-1. Open a command prompt
-2. mkdir build
-3. cd build
-4. cmake ..
-5. cmake --build .
-
-For iOS:
-Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS
+	
+========================================================================
+Open Asset Import Library (assimp) INSTALL 
+========================================================================
+
+------------------------------
+Getting the documentation
+------------------------------
+
+A regularly-updated copy is available at 
+http://assimp.sourceforge.net/lib_html/index.html
+
+A CHM file is included in the SVN repos: ./doc/AssimpDoc_Html/AssimpDoc.chm.
+To build the doxygen documentation on your own, follow these steps:
+
+a) download & install latest doxygen 
+b) make sure doxygen is in the executable search path
+c) navigate to ./doc
+d) and run 'doxygen'
+
+Open the generated HTML (AssimpDoc_Html/index.html) in the browser of your choice.
+Windows only: To generate the CHM doc, install 'Microsoft HTML Workshop'
+and configure the path to it in the DOXYFILE first. 
+
+------------------------------
+Building Assimp 
+------------------------------
+
+More detailed build instructions can be found in the documentation,
+this section is just for the inpatient among you.
+
+CMake is the preferred build system for Assimp. The minimum required version 
+is 2.6. If you don't have it yet, downloads for CMake can be found on
+http://www.cmake.org/. 
+
+For Unix:
+
+1. mkdir build && cd build
+2. cmake .. -G 'Unix Makefiles'
+3. make -j4
+
+For Windows:
+1. Open a command prompt
+2. mkdir build
+3. cd build
+4. cmake ..
+5. cmake --build .
+
+For iOS:
+Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS

+ 23 - 0
Readme.md

@@ -2,6 +2,7 @@ Open Asset Import Library (assimp)
 ==================================
 A library to import and export various 3d-model-formats including scene-post-processing to generate missing render data.
 ### Current project status ###
+[![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp) 
 ![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
 [![Linux Build Status](https://travis-ci.org/assimp/assimp.svg)](https://travis-ci.org/assimp/assimp)
 [![Windows Build Status](https://ci.appveyor.com/api/projects/status/tmo433wax6u6cjp4?svg=true)](https://ci.appveyor.com/project/kimkulling/assimp)
@@ -179,6 +180,28 @@ And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/a
 Contributions to assimp are highly appreciated. The easiest way to get involved is to submit
 a pull request with your changes against the main repository's `master` branch.
 
+## Contributors
+
+### Code Contributors
+
+This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
+
+<a href="https://github.com/assimp/assimp/graphs/contributors"><img src="https://opencollective.com/assimp/contributors.svg?width=890&button=false" /></a>
+
+### Financial Contributors
+
+Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/assimp/contribute)]
+
+#### Individuals
+
+<a href="https://opencollective.com/assimp"><img src="https://opencollective.com/assimp/individuals.svg?width=890"></a>
+
+#### Organizations
+
+Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/assimp/contribute)]
+
+<a href="https://opencollective.com/assimp/organization/0/website"><img src="https://opencollective.com/assimp/organization/0/avatar.svg"></a>
+
 ### License ###
 Our license is based on the modified, __3-clause BSD__-License.
 

+ 0 - 705
code/AMF/AMFImporter.cpp

@@ -1,705 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2020, assimp team
-
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
-copyright notice, this list of conditions and the
-following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the
-following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-contributors may be used to endorse or promote products
-derived from this software without specific prior
-written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/// \file AMFImporter.cpp
-/// \brief AMF-format files importer for Assimp: main algorithm implementation.
-/// \date 2016
-/// \author [email protected]
-
-#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-
-// Header files, Assimp.
-#include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
-
-#include <assimp/fast_atof.h>
-#include <assimp/DefaultIOSystem.h>
-
-// Header files, stdlib.
-#include <memory>
-
-namespace Assimp
-{
-
-/// \var aiImporterDesc AMFImporter::Description
-/// Conastant which hold importer description
-const aiImporterDesc AMFImporter::Description = {
-	"Additive manufacturing file format(AMF) Importer",
-	"smalcom",
-	"",
-	"See documentation in source code. Chapter: Limitations.",
-	aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
-	0,
-	0,
-	0,
-	0,
-	"amf"
-};
-
-void AMFImporter::Clear()
-{
-	mNodeElement_Cur = nullptr;
-	mUnit.clear();
-	mMaterial_Converted.clear();
-	mTexture_Converted.clear();
-	// Delete all elements
-	if(!mNodeElement_List.empty())
-	{
-		for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
-
-		mNodeElement_List.clear();
-	}
-}
-
-AMFImporter::~AMFImporter()
-{
-	if(mReader != nullptr) delete mReader;
-	// Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
-	Clear();
-}
-
-/*********************************************************************************************************************************************/
-/************************************************************ Functions: find set ************************************************************/
-/*********************************************************************************************************************************************/
-
-bool AMFImporter::Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const
-{
-	for(CAMFImporter_NodeElement* ne: mNodeElement_List)
-	{
-		if((ne->ID == pID) && (ne->Type == pType))
-		{
-			if(pNodeElement != nullptr) *pNodeElement = ne;
-
-			return true;
-		}
-	}// for(CAMFImporter_NodeElement* ne: mNodeElement_List)
-
-	return false;
-}
-
-bool AMFImporter::Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const
-{
-aiString node_name(pID.c_str());
-
-	for(aiNode* node: pNodeList)
-	{
-		if(node->mName == node_name)
-		{
-			if(pNode != nullptr) *pNode = node;
-
-			return true;
-		}
-	}// for(aiNode* node: pNodeList)
-
-	return false;
-}
-
-bool AMFImporter::Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const
-{
-	for(const SPP_Material& mat: mMaterial_Converted)
-	{
-		if(mat.ID == pID)
-		{
-			if(pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
-
-			return true;
-		}
-	}// for(const SPP_Material& mat: mMaterial_Converted)
-
-	return false;
-}
-
-/*********************************************************************************************************************************************/
-/************************************************************ Functions: throw set ***********************************************************/
-/*********************************************************************************************************************************************/
-
-void AMFImporter::Throw_CloseNotFound(const std::string& pNode)
-{
-	throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
-}
-
-void AMFImporter::Throw_IncorrectAttr(const std::string& pAttrName)
-{
-	throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
-}
-
-void AMFImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
-{
-	throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
-}
-
-void AMFImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
-{
-	throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
-}
-
-void AMFImporter::Throw_ID_NotFound(const std::string& pID) const
-{
-	throw DeadlyImportError("Not found node with name \"" + pID + "\".");
-}
-
-/*********************************************************************************************************************************************/
-/************************************************************* Functions: XML set ************************************************************/
-/*********************************************************************************************************************************************/
-
-void AMFImporter::XML_CheckNode_MustHaveChildren()
-{
-	if(mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
-}
-
-void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
-{
-    static const size_t Uns_Skip_Len = 3;
-    const char* Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
-
-    static bool skipped_before[Uns_Skip_Len] = { false, false, false };
-
-    std::string nn(mReader->getNodeName());
-    bool found = false;
-    bool close_found = false;
-    size_t sk_idx;
-
-	for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
-	{
-		if(nn != Uns_Skip[sk_idx]) continue;
-
-		found = true;
-		if(mReader->isEmptyElement())
-		{
-			close_found = true;
-
-			goto casu_cres;
-		}
-
-		while(mReader->read())
-		{
-			if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
-			{
-				close_found = true;
-
-				goto casu_cres;
-			}
-		}
-	}// for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
-
-casu_cres:
-
-	if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
-	if(!close_found) Throw_CloseNotFound(nn);
-
-	if(!skipped_before[sk_idx])
-	{
-		skipped_before[sk_idx] = true;
-        ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
-	}
-}
-
-bool AMFImporter::XML_SearchNode(const std::string& pNodeName)
-{
-	while(mReader->read())
-	{
-		if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
-	}
-
-	return false;
-}
-
-bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
-{
-    std::string val(mReader->getAttributeValue(pAttrIdx));
-
-	if((val == "false") || (val == "0"))
-		return false;
-	else if((val == "true") || (val == "1"))
-		return true;
-	else
-		throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
-}
-
-float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
-{
-    std::string val;
-    float tvalf;
-
-	ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
-	fast_atoreal_move(val.c_str(), tvalf, false);
-
-	return tvalf;
-}
-
-uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
-{
-	return strtoul10(mReader->getAttributeValue(pAttrIdx));
-}
-
-float AMFImporter::XML_ReadNode_GetVal_AsFloat()
-{
-    std::string val;
-    float tvalf;
-
-	if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
-	if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
-
-	ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
-	fast_atoreal_move(val.c_str(), tvalf, false);
-
-	return tvalf;
-}
-
-uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32()
-{
-	if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
-	if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
-
-	return strtoul10(mReader->getNodeData());
-}
-
-void AMFImporter::XML_ReadNode_GetVal_AsString(std::string& pValue)
-{
-	if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
-	if(mReader->getNodeType() != irr::io::EXN_TEXT)
-		throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
-
-	pValue = mReader->getNodeData();
-}
-
-/*********************************************************************************************************************************************/
-/************************************************************ Functions: parse set ***********************************************************/
-/*********************************************************************************************************************************************/
-
-void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
-{
-	mNodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
-	mNodeElement_Cur = pNode;// switch current element to new one.
-}
-
-void AMFImporter::ParseHelper_Node_Exit()
-{
-	// check if we can walk up.
-	if(mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
-}
-
-void AMFImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
-{
-    size_t instr_len;
-
-	pOutString.clear();
-	instr_len = strlen(pInStr);
-	if(!instr_len) return;
-
-	pOutString.reserve(instr_len * 3 / 2);
-	// check and correct floats in format ".x". Must be "x.y".
-	if(pInStr[0] == '.') pOutString.push_back('0');
-
-	pOutString.push_back(pInStr[0]);
-	for(size_t ci = 1; ci < instr_len; ci++)
-	{
-		if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
-		{
-			pOutString.push_back('0');
-			pOutString.push_back('.');
-		}
-		else
-		{
-			pOutString.push_back(pInStr[ci]);
-		}
-	}
-}
-
-static bool ParseHelper_Decode_Base64_IsBase64(const char pChar)
-{
-	return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
-}
-
-void AMFImporter::ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const
-{
-    // With help from
-    // René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
-    const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-    uint8_t tidx = 0;
-    uint8_t arr4[4], arr3[3];
-
-	// check input data
-	if(pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
-	// prepare output place
-	pOutputData.clear();
-	pOutputData.reserve(pInputBase64.size() / 4 * 3);
-
-	for(size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--)
-	{
-		if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
-		{
-			arr4[tidx++] = pInputBase64[in_idx++];
-			if(tidx == 4)
-			{
-				for(tidx = 0; tidx < 4; tidx++) arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
-
-				arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
-				arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
-				arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
-				for(tidx = 0; tidx < 3; tidx++) pOutputData.push_back(arr3[tidx]);
-
-				tidx = 0;
-			}// if(tidx == 4)
-		}// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
-		else
-		{
-			in_idx++;
-		}// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
-	}
-
-	if(tidx)
-	{
-		for(uint8_t i = tidx; i < 4; i++) arr4[i] = 0;
-		for(uint8_t i = 0; i < 4; i++) arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
-
-		arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
-		arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
-		arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
-		for(uint8_t i = 0; i < (tidx - 1); i++) pOutputData.push_back(arr3[i]);
-	}
-}
-
-void AMFImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
-{
-    irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
-    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
-
-	// Check whether we can read from the file
-	if(file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
-
-	// generate a XML reader for it
-	std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
-	mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
-	if(!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
-	//
-	// start reading
-	// search for root tag <amf>
-	if(XML_SearchNode("amf"))
-		ParseNode_Root();
-	else
-		throw DeadlyImportError("Root node \"amf\" not found.");
-
-	delete mReader;
-	// restore old XMLreader
-	mReader = OldReader;
-}
-
-// <amf
-// unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
-// version="" - Version of file format.
-// >
-// </amf>
-// Root XML element.
-// Multi elements - No.
-void AMFImporter::ParseNode_Root()
-{
-    std::string unit, version;
-    CAMFImporter_NodeElement *ne( nullptr );
-
-	// Read attributes for node <amf>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND_WSKIP;
-
-	// Check attributes
-	if(!mUnit.empty())
-	{
-		if((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
-	}
-
-	// create root node element.
-	ne = new CAMFImporter_NodeElement_Root(nullptr);
-	mNodeElement_Cur = ne;// set first "current" element
-	// and assign attribute's values
-	((CAMFImporter_NodeElement_Root*)ne)->Unit = unit;
-	((CAMFImporter_NodeElement_Root*)ne)->Version = version;
-
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		MACRO_NODECHECK_LOOPBEGIN("amf");
-			if(XML_CheckNode_NameEqual("object")) { ParseNode_Object(); continue; }
-			if(XML_CheckNode_NameEqual("material")) { ParseNode_Material(); continue; }
-			if(XML_CheckNode_NameEqual("texture")) { ParseNode_Texture(); continue; }
-			if(XML_CheckNode_NameEqual("constellation")) { ParseNode_Constellation(); continue; }
-			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
-		MACRO_NODECHECK_LOOPEND("amf");
-		mNodeElement_Cur = ne;// force restore "current" element
-	}// if(!mReader->isEmptyElement())
-
-	mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
-}
-
-// <constellation
-// id="" - The Object ID of the new constellation being defined.
-// >
-// </constellation>
-// A collection of objects or constellations with specific relative locations.
-// Multi elements - Yes.
-// Parent element - <amf>.
-void AMFImporter::ParseNode_Constellation()
-{
-    std::string id;
-    CAMFImporter_NodeElement* ne( nullptr );
-
-	// Read attributes for node <constellation>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create and if needed - define new grouping object.
-	ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Constellation& als = *((CAMFImporter_NodeElement_Constellation*)ne);// alias for convenience
-
-	if(!id.empty()) als.ID = id;
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("constellation");
-			if(XML_CheckNode_NameEqual("instance")) { ParseNode_Instance(); continue; }
-			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
-		MACRO_NODECHECK_LOOPEND("constellation");
-		ParseHelper_Node_Exit();
-	}// if(!mReader->isEmptyElement())
-	else
-	{
-		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
-	}// if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
-}
-
-// <instance
-// objectid="" - The Object ID of the new constellation being defined.
-// >
-// </instance>
-// A collection of objects or constellations with specific relative locations.
-// Multi elements - Yes.
-// Parent element - <amf>.
-void AMFImporter::ParseNode_Instance()
-{
-    std::string objectid;
-    CAMFImporter_NodeElement* ne( nullptr );
-
-	// Read attributes for node <constellation>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// used object id must be defined, check that.
-	if(objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
-	// create and define new grouping object.
-	ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);// alias for convenience
-
-	als.ObjectID = objectid;
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool read_flag[6] = { false, false, false, false, false, false };
-
-		als.Delta.Set(0, 0, 0);
-		als.Rotation.Set(0, 0, 0);
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("instance");
-			MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
-			MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
-			MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
-			MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
-			MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
-			MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
-		MACRO_NODECHECK_LOOPEND("instance");
-		ParseHelper_Node_Exit();
-		// also convert degrees to radians.
-		als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
-		als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
-		als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
-	}// if(!mReader->isEmptyElement())
-	else
-	{
-		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
-	}// if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
-}
-
-// <object
-// id="" - A unique ObjectID for the new object being defined.
-// >
-// </object>
-// An object definition.
-// Multi elements - Yes.
-// Parent element - <amf>.
-void AMFImporter::ParseNode_Object()
-{
-    std::string id;
-    CAMFImporter_NodeElement* ne( nullptr );
-
-	// Read attributes for node <object>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create and if needed - define new geometry object.
-	ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Object& als = *((CAMFImporter_NodeElement_Object*)ne);// alias for convenience
-
-	if(!id.empty()) als.ID = id;
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool col_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("object");
-			if(XML_CheckNode_NameEqual("color"))
-			{
-				// Check if color already defined for object.
-				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <object>.");
-				// read data and set flag about it
-				ParseNode_Color();
-				col_read = true;
-
-				continue;
-			}
-
-			if(XML_CheckNode_NameEqual("mesh")) { ParseNode_Mesh(); continue; }
-			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
-		MACRO_NODECHECK_LOOPEND("object");
-		ParseHelper_Node_Exit();
-	}// if(!mReader->isEmptyElement())
-	else
-	{
-		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
-	}// if(!mReader->isEmptyElement()) else
-
-	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
-}
-
-// <metadata
-// type="" - The type of the attribute.
-// >
-// </metadata>
-// Specify additional information about an entity.
-// Multi elements - Yes.
-// Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
-//
-// Reserved types are:
-// "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
-// "Description" - A description of the content of the entity
-// "URL" - A link to an external resource relating to the entity
-// "Author" - Specifies the name(s) of the author(s) of the entity
-// "Company" - Specifying the company generating the entity
-// "CAD" - specifies the name of the originating CAD software and version
-// "Revision" - specifies the revision of the entity
-// "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
-// "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
-void AMFImporter::ParseNode_Metadata()
-{
-    std::string type, value;
-    CAMFImporter_NodeElement* ne( nullptr );
-
-	// read attribute
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-	// and value of node.
-	value = mReader->getNodeData();
-	// Create node element and assign read data.
-	ne = new CAMFImporter_NodeElement_Metadata(mNodeElement_Cur);
-	((CAMFImporter_NodeElement_Metadata*)ne)->Type = type;
-	((CAMFImporter_NodeElement_Metadata*)ne)->Value = value;
-	mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
-	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
-}
-
-/*********************************************************************************************************************************************/
-/******************************************************** Functions: BaseImporter set ********************************************************/
-/*********************************************************************************************************************************************/
-
-bool AMFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const
-{
-    const std::string extension = GetExtension(pFile);
-
-    if ( extension == "amf" ) {
-        return true;
-    }
-
-	if(!extension.length() || pCheckSig)
-	{
-		const char* tokens[] = { "<amf" };
-
-		return SearchFileHeaderForToken( pIOHandler, pFile, tokens, 1 );
-	}
-
-	return false;
-}
-
-void AMFImporter::GetExtensionList(std::set<std::string>& pExtensionList)
-{
-	pExtensionList.insert("amf");
-}
-
-const aiImporterDesc* AMFImporter::GetInfo () const
-{
-	return &Description;
-}
-
-void AMFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
-{
-	Clear();// delete old graph.
-	ParseFile(pFile, pIOHandler);
-	Postprocess_BuildScene(pScene);
-	// scene graph is ready, exit.
-}
-
-}// namespace Assimp
-
-#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 0 - 978
code/AMF/AMFImporter_Postprocess.cpp

@@ -1,978 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2020, assimp team
-
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
-copyright notice, this list of conditions and the
-following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the
-following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-contributors may be used to endorse or promote products
-derived from this software without specific prior
-written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/// \file AMFImporter_Postprocess.cpp
-/// \brief Convert built scenegraph and objects to Assimp scenegraph.
-/// \date 2016
-/// \author [email protected]
-
-#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-
-#include "AMFImporter.hpp"
-
-// Header files, Assimp.
-#include <assimp/SceneCombiner.h>
-#include <assimp/StandardShapes.h>
-#include <assimp/StringUtils.h>
-
-// Header files, stdlib.
-#include <iterator>
-
-namespace Assimp
-{
-
-aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const
-{
-    aiColor4D tcol;
-
-	// Check if stored data are supported.
-	if(!Composition.empty())
-	{
-		throw DeadlyImportError("IME. GetColor for composition");
-	}
-	else if(Color->Composed)
-	{
-		throw DeadlyImportError("IME. GetColor, composed color");
-	}
-	else
-	{
-		tcol = Color->Color;
-	}
-
-	// Check if default color must be used
-	if((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0))
-	{
-		tcol.r = 0.5f;
-		tcol.g = 0.5f;
-		tcol.b = 0.5f;
-		tcol.a = 1;
-	}
-
-	return tcol;
-}
-
-void AMFImporter::PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray,
-														std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const
-{
-    CAMFImporter_NodeElement_Vertices* vn = nullptr;
-    size_t col_idx;
-
-	// All data stored in "vertices", search for it.
-	for(CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
-	{
-		if(ne_child->Type == CAMFImporter_NodeElement::ENET_Vertices) vn = (CAMFImporter_NodeElement_Vertices*)ne_child;
-	}
-
-	// If "vertices" not found then no work for us.
-	if(vn == nullptr) return;
-
-	pVertexCoordinateArray.reserve(vn->Child.size());// all coordinates stored as child and we need to reserve space for future push_back's.
-	pVertexColorArray.resize(vn->Child.size());// colors count equal vertices count.
-	col_idx = 0;
-	// Inside vertices collect all data and place to arrays
-	for(CAMFImporter_NodeElement* vn_child: vn->Child)
-	{
-		// vertices, colors
-		if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
-		{
-			// by default clear color for current vertex
-			pVertexColorArray[col_idx] = nullptr;
-
-			for(CAMFImporter_NodeElement* vtx: vn_child->Child)
-			{
-				if(vtx->Type == CAMFImporter_NodeElement::ENET_Coordinates)
-				{
-					pVertexCoordinateArray.push_back(((CAMFImporter_NodeElement_Coordinates*)vtx)->Coordinate);
-
-					continue;
-				}
-
-				if(vtx->Type == CAMFImporter_NodeElement::ENET_Color)
-				{
-					pVertexColorArray[col_idx] = (CAMFImporter_NodeElement_Color*)vtx;
-
-					continue;
-				}
-			}// for(CAMFImporter_NodeElement* vtx: vn_child->Child)
-
-			col_idx++;
-		}// if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
-	}// for(CAMFImporter_NodeElement* vn_child: vn->Child)
-}
-
-size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B,
-																const std::string& pID_A)
-{
-    size_t TextureConverted_Index;
-    std::string TextureConverted_ID;
-
-	// check input data
-	if(pID_R.empty() && pID_G.empty() && pID_B.empty() && pID_A.empty())
-		throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined.");
-
-	// Create ID
-	TextureConverted_ID = pID_R + "_" + pID_G + "_" + pID_B + "_" + pID_A;
-	// Check if texture specified by set of IDs is converted already.
-	TextureConverted_Index = 0;
-	for(const SPP_Texture& tex_convd: mTexture_Converted)
-	{
-        if ( tex_convd.ID == TextureConverted_ID ) {
-            return TextureConverted_Index;
-        } else {
-            ++TextureConverted_Index;
-        }
-	}
-
-	//
-	// Converted texture not found, create it.
-	//
-	CAMFImporter_NodeElement_Texture* src_texture[4]{nullptr};
-	std::vector<CAMFImporter_NodeElement_Texture*> src_texture_4check;
-	SPP_Texture converted_texture;
-
-	{// find all specified source textures
-		CAMFImporter_NodeElement* t_tex;
-
-		// R
-		if(!pID_R.empty())
-		{
-			if(!Find_NodeElement(pID_R, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R);
-
-			src_texture[0] = (CAMFImporter_NodeElement_Texture*)t_tex;
-			src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
-		}
-		else
-		{
-			src_texture[0] = nullptr;
-		}
-
-		// G
-		if(!pID_G.empty())
-		{
-			if(!Find_NodeElement(pID_G, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_G);
-
-			src_texture[1] = (CAMFImporter_NodeElement_Texture*)t_tex;
-			src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
-		}
-		else
-		{
-			src_texture[1] = nullptr;
-		}
-
-		// B
-		if(!pID_B.empty())
-		{
-			if(!Find_NodeElement(pID_B, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_B);
-
-			src_texture[2] = (CAMFImporter_NodeElement_Texture*)t_tex;
-			src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
-		}
-		else
-		{
-			src_texture[2] = nullptr;
-		}
-
-		// A
-		if(!pID_A.empty())
-		{
-			if(!Find_NodeElement(pID_A, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_A);
-
-			src_texture[3] = (CAMFImporter_NodeElement_Texture*)t_tex;
-			src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
-		}
-		else
-		{
-			src_texture[3] = nullptr;
-		}
-	}// END: find all specified source textures
-
-	// check that all textures has same size
-	if(src_texture_4check.size() > 1)
-	{
-		for (size_t i = 0, i_e = (src_texture_4check.size() - 1); i < i_e; i++)
-		{
-			if((src_texture_4check[i]->Width != src_texture_4check[i + 1]->Width) || (src_texture_4check[i]->Height != src_texture_4check[i + 1]->Height) ||
-				(src_texture_4check[i]->Depth != src_texture_4check[i + 1]->Depth))
-			{
-				throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size.");
-			}
-		}
-	}// if(src_texture_4check.size() > 1)
-
-	// set texture attributes
-	converted_texture.Width = src_texture_4check[0]->Width;
-	converted_texture.Height = src_texture_4check[0]->Height;
-	converted_texture.Depth = src_texture_4check[0]->Depth;
-	// if one of source texture is tiled then converted texture is tiled too.
-	converted_texture.Tiled = false;
-	for(uint8_t i = 0; i < src_texture_4check.size(); i++) converted_texture.Tiled |= src_texture_4check[i]->Tiled;
-
-	// Create format hint.
-	strcpy(converted_texture.FormatHint, "rgba0000");// copy initial string.
-	if(!pID_R.empty()) converted_texture.FormatHint[4] = '8';
-	if(!pID_G.empty()) converted_texture.FormatHint[5] = '8';
-	if(!pID_B.empty()) converted_texture.FormatHint[6] = '8';
-	if(!pID_A.empty()) converted_texture.FormatHint[7] = '8';
-
-	//
-	// Сopy data of textures.
-	//
-	size_t tex_size = 0;
-	size_t step = 0;
-	size_t off_g = 0;
-	size_t off_b = 0;
-
-	// Calculate size of the target array and rule how data will be copied.
-    if(!pID_R.empty() && nullptr != src_texture[ 0 ] ) {
-        tex_size += src_texture[0]->Data.size(); step++, off_g++, off_b++;
-    }
-    if(!pID_G.empty() && nullptr != src_texture[ 1 ] ) {
-        tex_size += src_texture[1]->Data.size(); step++, off_b++;
-    }
-    if(!pID_B.empty() && nullptr != src_texture[ 2 ] ) {
-        tex_size += src_texture[2]->Data.size(); step++;
-    }
-    if(!pID_A.empty() && nullptr != src_texture[ 3 ] ) {
-        tex_size += src_texture[3]->Data.size(); step++;
-    }
-
-    // Create target array.
-	converted_texture.Data = new uint8_t[tex_size];
-	// And copy data
-	auto CopyTextureData = [&](const std::string& pID, const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
-	{
-		if(!pID.empty())
-		{
-			for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) {
-				CAMFImporter_NodeElement_Texture* tex = src_texture[pSrcTexNum];
-				ai_assert(tex);
-				converted_texture.Data[idx_target] = tex->Data.at(idx_src);
-			}
-		}
-	};// auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
-
-	CopyTextureData(pID_R, 0, step, 0);
-	CopyTextureData(pID_G, off_g, step, 1);
-	CopyTextureData(pID_B, off_b, step, 2);
-	CopyTextureData(pID_A, step - 1, step, 3);
-
-	// Store new converted texture ID
-	converted_texture.ID = TextureConverted_ID;
-	// Store new converted texture
-	mTexture_Converted.push_back(converted_texture);
-
-	return TextureConverted_Index;
-}
-
-void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> >& pOutputList_Separated)
-{
-    auto texmap_is_equal = [](const CAMFImporter_NodeElement_TexMap* pTexMap1, const CAMFImporter_NodeElement_TexMap* pTexMap2) -> bool
-    {
-	    if((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true;
-	    if(pTexMap1 == nullptr) return false;
-	    if(pTexMap2 == nullptr) return false;
-
-	    if(pTexMap1->TextureID_R != pTexMap2->TextureID_R) return false;
-	    if(pTexMap1->TextureID_G != pTexMap2->TextureID_G) return false;
-	    if(pTexMap1->TextureID_B != pTexMap2->TextureID_B) return false;
-	    if(pTexMap1->TextureID_A != pTexMap2->TextureID_A) return false;
-
-	    return true;
-    };
-
-	pOutputList_Separated.clear();
-	if(pInputList.empty()) return;
-
-	do
-	{
-		SComplexFace face_start = pInputList.front();
-		std::list<SComplexFace> face_list_cur;
-
-		for(std::list<SComplexFace>::iterator it = pInputList.begin(), it_end = pInputList.end(); it != it_end;)
-		{
-			if(texmap_is_equal(face_start.TexMap, it->TexMap))
-			{
-				auto it_old = it;
-
-				++it;
-				face_list_cur.push_back(*it_old);
-				pInputList.erase(it_old);
-			}
-			else
-			{
-				++it;
-			}
-		}
-
-		if(!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur);
-
-	} while(!pInputList.empty());
-}
-
-void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& metadataList, aiNode& sceneNode) const
-{
-	if ( !metadataList.empty() )
-	{
-		if(sceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
-
-		// copy collected metadata to output node.
-        sceneNode.mMetaData = aiMetadata::Alloc( static_cast<unsigned int>(metadataList.size()) );
-		size_t meta_idx( 0 );
-
-		for(const CAMFImporter_NodeElement_Metadata& metadata: metadataList)
-		{
-			sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata.Type, aiString(metadata.Value));
-		}
-	}// if(!metadataList.empty())
-}
-
-void AMFImporter::Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode)
-{
-CAMFImporter_NodeElement_Color* object_color = nullptr;
-
-	// create new aiNode and set name as <object> has.
-	*pSceneNode = new aiNode;
-	(*pSceneNode)->mName = pNodeElement.ID;
-	// read mesh and color
-	for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
-	{
-		std::vector<aiVector3D> vertex_arr;
-		std::vector<CAMFImporter_NodeElement_Color*> color_arr;
-
-		// color for object
-		if(ne_child->Type == CAMFImporter_NodeElement::ENET_Color) object_color = (CAMFImporter_NodeElement_Color*)ne_child;
-
-		if(ne_child->Type == CAMFImporter_NodeElement::ENET_Mesh)
-		{
-			// Create arrays from children of mesh: vertices.
-			PostprocessHelper_CreateMeshDataArray(*((CAMFImporter_NodeElement_Mesh*)ne_child), vertex_arr, color_arr);
-			// Use this arrays as a source when creating every aiMesh
-			Postprocess_BuildMeshSet(*((CAMFImporter_NodeElement_Mesh*)ne_child), vertex_arr, color_arr, object_color, pMeshList, **pSceneNode);
-		}
-	}// for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
-}
-
-void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh& pNodeElement, const std::vector<aiVector3D>& pVertexCoordinateArray,
-											const std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray,
-											const CAMFImporter_NodeElement_Color* pObjectColor, std::list<aiMesh*>& pMeshList, aiNode& pSceneNode)
-{
-std::list<unsigned int> mesh_idx;
-
-	// all data stored in "volume", search for it.
-	for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
-	{
-		const CAMFImporter_NodeElement_Color* ne_volume_color = nullptr;
-		const SPP_Material* cur_mat = nullptr;
-
-		if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
-		{
-			/******************* Get faces *******************/
-			const CAMFImporter_NodeElement_Volume* ne_volume = reinterpret_cast<const CAMFImporter_NodeElement_Volume*>(ne_child);
-
-			std::list<SComplexFace> complex_faces_list;// List of the faces of the volume.
-			std::list<std::list<SComplexFace> > complex_faces_toplist;// List of the face list for every mesh.
-
-			// check if volume use material
-			if(!ne_volume->MaterialID.empty())
-			{
-				if(!Find_ConvertedMaterial(ne_volume->MaterialID, &cur_mat)) Throw_ID_NotFound(ne_volume->MaterialID);
-			}
-
-			// inside "volume" collect all data and place to arrays or create new objects
-			for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
-			{
-				// color for volume
-				if(ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Color)
-				{
-					ne_volume_color = reinterpret_cast<const CAMFImporter_NodeElement_Color*>(ne_volume_child);
-				}
-				else if(ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Triangle)// triangles, triangles colors
-				{
-					const CAMFImporter_NodeElement_Triangle& tri_al = *reinterpret_cast<const CAMFImporter_NodeElement_Triangle*>(ne_volume_child);
-
-					SComplexFace complex_face;
-
-					// initialize pointers
-					complex_face.Color = nullptr;
-					complex_face.TexMap = nullptr;
-					// get data from triangle children: color, texture coordinates.
-					if(tri_al.Child.size())
-					{
-						for(const CAMFImporter_NodeElement* ne_triangle_child: tri_al.Child)
-						{
-							if(ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_Color)
-								complex_face.Color = reinterpret_cast<const CAMFImporter_NodeElement_Color*>(ne_triangle_child);
-							else if(ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_TexMap)
-								complex_face.TexMap = reinterpret_cast<const CAMFImporter_NodeElement_TexMap*>(ne_triangle_child);
-						}
-					}// if(tri_al.Child.size())
-
-					// create new face and store it.
-					complex_face.Face.mNumIndices = 3;
-					complex_face.Face.mIndices = new unsigned int[3];
-					complex_face.Face.mIndices[0] = static_cast<unsigned int>(tri_al.V[0]);
-					complex_face.Face.mIndices[1] = static_cast<unsigned int>(tri_al.V[1]);
-					complex_face.Face.mIndices[2] = static_cast<unsigned int>(tri_al.V[2]);
-					complex_faces_list.push_back(complex_face);
-				}
-			}// for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
-
-			/**** Split faces list: one list per mesh ****/
-			PostprocessHelper_SplitFacesByTextureID(complex_faces_list, complex_faces_toplist);
-
-			/***** Create mesh for every faces list ******/
-			for(std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
-			{
-				auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
-				{
-					size_t rv=0;
-
-					if(pBiggerThan != nullptr)
-					{
-						bool found = false;
-
-						for(const SComplexFace& face: pFaceList)
-						{
-							for(size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++)
-							{
-								if(face.Face.mIndices[idx_vert] > *pBiggerThan)
-								{
-									rv = face.Face.mIndices[idx_vert];
-									found = true;
-
-									break;
-								}
-							}
-
-							if(found) break;
-						}
-
-						if(!found) return *pBiggerThan;
-					}
-					else
-					{
-						rv = pFaceList.front().Face.mIndices[0];
-					}// if(pBiggerThan != nullptr) else
-
-					for(const SComplexFace& face: pFaceList)
-					{
-						for(size_t vi = 0; vi < face.Face.mNumIndices; vi++)
-						{
-							if(face.Face.mIndices[vi] < rv)
-							{
-								if(pBiggerThan != nullptr)
-								{
-									if(face.Face.mIndices[vi] > *pBiggerThan) rv = face.Face.mIndices[vi];
-								}
-								else
-								{
-									rv = face.Face.mIndices[vi];
-								}
-							}
-						}
-					}// for(const SComplexFace& face: pFaceList)
-
-					return rv;
-				};// auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
-
-				auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
-				{
-					for(const SComplexFace& face: pFaceList)
-					{
-						for(size_t vi = 0; vi < face.Face.mNumIndices; vi++)
-						{
-							if(face.Face.mIndices[vi] == pIdx_From) face.Face.mIndices[vi] = static_cast<unsigned int>(pIdx_To);
-						}
-					}
-				};// auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
-
-				auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
-				{
-					// Color priorities(In descending order):
-					// 1. triangle color;
-					// 2. vertex color;
-					// 3. volume color;
-					// 4. object color;
-					// 5. material;
-					// 6. default - invisible coat.
-					//
-					// Fill vertices colors in color priority list above that's points from 1 to 6.
-					if((pIdx < pVertexColorArray.size()) && (pVertexColorArray[pIdx] != nullptr))// check for vertex color
-					{
-						if(pVertexColorArray[pIdx]->Composed)
-							throw DeadlyImportError("IME: vertex color composed");
-						else
-							return pVertexColorArray[pIdx]->Color;
-					}
-					else if(ne_volume_color != nullptr)// check for volume color
-					{
-						if(ne_volume_color->Composed)
-							throw DeadlyImportError("IME: volume color composed");
-						else
-							return ne_volume_color->Color;
-					}
-					else if(pObjectColor != nullptr)// check for object color
-					{
-						if(pObjectColor->Composed)
-							throw DeadlyImportError("IME: object color composed");
-						else
-							return pObjectColor->Color;
-					}
-					else if(cur_mat != nullptr)// check for material
-					{
-						return cur_mat->GetColor(pVertexCoordinateArray.at(pIdx).x, pVertexCoordinateArray.at(pIdx).y, pVertexCoordinateArray.at(pIdx).z);
-					}
-					else// set default color.
-					{
-						return {0, 0, 0, 0};
-					}// if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else
-
-				};// auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
-
-				aiMesh* tmesh = new aiMesh;
-
-				tmesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;// Only triangles is supported by AMF.
-				//
-				// set geometry and colors (vertices)
-				//
-				// copy faces/triangles
-				tmesh->mNumFaces = static_cast<unsigned int>(face_list_cur.size());
-				tmesh->mFaces = new aiFace[tmesh->mNumFaces];
-
-				// Create vertices list and optimize indices. Optimisation mean following.In AMF all volumes use one big list of vertices. And one volume
-				// can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10.
-				// Do you need all this thousands of garbage? Of course no. So, optimisation step transformate sparse indices set to continuous.
-				size_t VertexCount_Max = tmesh->mNumFaces * 3;// 3 - triangles.
-				std::vector<aiVector3D> vert_arr, texcoord_arr;
-				std::vector<aiColor4D> col_arr;
-
-				vert_arr.reserve(VertexCount_Max * 2);// "* 2" - see below TODO.
-				col_arr.reserve(VertexCount_Max * 2);
-
-				{// fill arrays
-					size_t vert_idx_from, vert_idx_to;
-
-					// first iteration.
-					vert_idx_to = 0;
-					vert_idx_from = VertexIndex_GetMinimal(face_list_cur, nullptr);
-					vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
-					col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
-					if(vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
-
-					// rest iterations
-					do
-					{
-						vert_idx_from = VertexIndex_GetMinimal(face_list_cur, &vert_idx_to);
-						if(vert_idx_from == vert_idx_to) break;// all indices are transferred,
-
-						vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
-						col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
-						vert_idx_to++;
-						if(vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
-
-					} while(true);
-				}// fill arrays. END.
-
-				//
-				// check if triangle colors are used and create additional faces if needed.
-				//
-				for(const SComplexFace& face_cur: face_list_cur)
-				{
-					if(face_cur.Color != nullptr)
-					{
-						aiColor4D face_color;
-						size_t vert_idx_new = vert_arr.size();
-
-						if(face_cur.Color->Composed)
-							throw DeadlyImportError("IME: face color composed");
-						else
-							face_color = face_cur.Color->Color;
-
-						for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
-						{
-							vert_arr.push_back(vert_arr.at(face_cur.Face.mIndices[idx_ind]));
-							col_arr.push_back(face_color);
-							face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(vert_idx_new++);
-						}
-					}// if(face_cur.Color != nullptr)
-				}// for(const SComplexFace& face_cur: face_list_cur)
-
-				//
-				// if texture is used then copy texture coordinates too.
-				//
-				if(face_list_cur.front().TexMap != nullptr)
-				{
-					size_t idx_vert_new = vert_arr.size();
-					///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for
-					/// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about
-					/// optimisation.
-					bool* idx_vert_used;
-
-					idx_vert_used = new bool[VertexCount_Max * 2];
-					for(size_t i = 0, i_e = VertexCount_Max * 2; i < i_e; i++) idx_vert_used[i] = false;
-
-					// This ID's will be used when set materials ID in scene.
-					tmesh->mMaterialIndex = static_cast<unsigned int>(PostprocessHelper_GetTextureID_Or_Create(face_list_cur.front().TexMap->TextureID_R,
-																						face_list_cur.front().TexMap->TextureID_G,
-																						face_list_cur.front().TexMap->TextureID_B,
-																						face_list_cur.front().TexMap->TextureID_A));
-					texcoord_arr.resize(VertexCount_Max * 2);
-					for(const SComplexFace& face_cur: face_list_cur)
-					{
-						for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
-						{
-							const size_t idx_vert = face_cur.Face.mIndices[idx_ind];
-
-							if(!idx_vert_used[idx_vert])
-							{
-								texcoord_arr.at(idx_vert) = face_cur.TexMap->TextureCoordinate[idx_ind];
-								idx_vert_used[idx_vert] = true;
-							}
-							else if(texcoord_arr.at(idx_vert) != face_cur.TexMap->TextureCoordinate[idx_ind])
-							{
-								// in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture
-								// coordinates.
-								vert_arr.push_back(vert_arr.at(idx_vert));
-								col_arr.push_back(col_arr.at(idx_vert));
-								texcoord_arr.at(idx_vert_new) = face_cur.TexMap->TextureCoordinate[idx_ind];
-								face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(idx_vert_new++);
-							}
-						}// for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
-					}// for(const SComplexFace& face_cur: face_list_cur)
-
-					delete [] idx_vert_used;
-					// shrink array
-					texcoord_arr.resize(idx_vert_new);
-				}// if(face_list_cur.front().TexMap != nullptr)
-
-				//
-				// copy collected data to mesh
-				//
-				tmesh->mNumVertices = static_cast<unsigned int>(vert_arr.size());
-				tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
-				tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
-
-				memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
-				memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D));
-				if(texcoord_arr.size() > 0)
-				{
-					tmesh->mTextureCoords[0] = new aiVector3D[tmesh->mNumVertices];
-					memcpy(tmesh->mTextureCoords[0], texcoord_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
-					tmesh->mNumUVComponents[0] = 2;// U and V stored in "x", "y" of aiVector3D.
-				}
-
-				size_t idx_face = 0;
-				for(const SComplexFace& face_cur: face_list_cur) tmesh->mFaces[idx_face++] = face_cur.Face;
-
-				// store new aiMesh
-				mesh_idx.push_back(static_cast<unsigned int>(pMeshList.size()));
-				pMeshList.push_back(tmesh);
-			}// for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
-		}// if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
-	}// for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
-
-	// if meshes was created then assign new indices with current aiNode
-	if(!mesh_idx.empty())
-	{
-		std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
-
-		pSceneNode.mNumMeshes = static_cast<unsigned int>(mesh_idx.size());
-		pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
-		for(size_t i = 0; i < pSceneNode.mNumMeshes; i++) pSceneNode.mMeshes[i] = *mit++;
-	}// if(mesh_idx.size() > 0)
-}
-
-void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial)
-{
-SPP_Material new_mat;
-
-	new_mat.ID = pMaterial.ID;
-	for(const CAMFImporter_NodeElement* mat_child: pMaterial.Child)
-	{
-		if(mat_child->Type == CAMFImporter_NodeElement::ENET_Color)
-		{
-			new_mat.Color = (CAMFImporter_NodeElement_Color*)mat_child;
-		}
-		else if(mat_child->Type == CAMFImporter_NodeElement::ENET_Metadata)
-		{
-			new_mat.Metadata.push_back((CAMFImporter_NodeElement_Metadata*)mat_child);
-		}
-	}// for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
-
-	// place converted material to special list
-	mMaterial_Converted.push_back(new_mat);
-}
-
-void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const
-{
-aiNode* con_node;
-std::list<aiNode*> ch_node;
-
-	// We will build next hierarchy:
-	// aiNode as parent (<constellation>) for set of nodes as a children
-	//  |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
-	//  ...
-	//  \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
-	con_node = new aiNode;
-	con_node->mName = pConstellation.ID;
-	// Walk through children and search for instances of another objects, constellations.
-	for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
-	{
-		aiMatrix4x4 tmat;
-		aiNode* t_node;
-		aiNode* found_node;
-
-		if(ne->Type == CAMFImporter_NodeElement::ENET_Metadata) continue;
-		if(ne->Type != CAMFImporter_NodeElement::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
-
-		// create alias for conveniance
-		CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);
-		// find referenced object
-		if(!Find_ConvertedNode(als.ObjectID, pNodeList, &found_node)) Throw_ID_NotFound(als.ObjectID);
-
-		// create node for applying transformation
-		t_node = new aiNode;
-		t_node->mParent = con_node;
-		// apply transformation
-		aiMatrix4x4::Translation(als.Delta, tmat), t_node->mTransformation *= tmat;
-		aiMatrix4x4::RotationX(als.Rotation.x, tmat), t_node->mTransformation *= tmat;
-		aiMatrix4x4::RotationY(als.Rotation.y, tmat), t_node->mTransformation *= tmat;
-		aiMatrix4x4::RotationZ(als.Rotation.z, tmat), t_node->mTransformation *= tmat;
-		// create array for one child node
-		t_node->mNumChildren = 1;
-		t_node->mChildren = new aiNode*[t_node->mNumChildren];
-		SceneCombiner::Copy(&t_node->mChildren[0], found_node);
-		t_node->mChildren[0]->mParent = t_node;
-		ch_node.push_back(t_node);
-	}// for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
-
-	// copy found aiNode's as children
-	if(ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>.");
-
-	size_t ch_idx = 0;
-
-	con_node->mNumChildren = static_cast<unsigned int>(ch_node.size());
-	con_node->mChildren = new aiNode*[con_node->mNumChildren];
-	for(aiNode* node: ch_node) con_node->mChildren[ch_idx++] = node;
-
-	// and place "root" of <constellation> node to node list
-	pNodeList.push_back(con_node);
-}
-
-void AMFImporter::Postprocess_BuildScene(aiScene* pScene)
-{
-std::list<aiNode*> node_list;
-std::list<aiMesh*> mesh_list;
-std::list<CAMFImporter_NodeElement_Metadata*> meta_list;
-
-	//
-	// Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
-	// For building aiScene we are must to do few steps:
-	// at first creating root node for aiScene.
-	pScene->mRootNode = new aiNode;
-	pScene->mRootNode->mParent = nullptr;
-	pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
-	// search for root(<amf>) element
-	CAMFImporter_NodeElement* root_el = nullptr;
-
-	for(CAMFImporter_NodeElement* ne: mNodeElement_List)
-	{
-		if(ne->Type != CAMFImporter_NodeElement::ENET_Root) continue;
-
-		root_el = ne;
-
-		break;
-	}// for(const CAMFImporter_NodeElement* ne: mNodeElement_List)
-
-	// Check if root element are found.
-	if(root_el == nullptr) throw DeadlyImportError("Root(<amf>) element not found.");
-
-	// after that walk through children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>,
-	// <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read
-	// at any moment.
-	//
-	// 1. <material>
-	// 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet
-	for(const CAMFImporter_NodeElement* root_child: root_el->Child)
-	{
-		if(root_child->Type == CAMFImporter_NodeElement::ENET_Material) Postprocess_BuildMaterial(*((CAMFImporter_NodeElement_Material*)root_child));
-	}
-
-	// After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
-	//
-	// 3. <object>
-	for(const CAMFImporter_NodeElement* root_child: root_el->Child)
-	{
-		if(root_child->Type == CAMFImporter_NodeElement::ENET_Object)
-		{
-			aiNode* tnode = nullptr;
-
-			// for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance>
-			Postprocess_BuildNodeAndObject(*((CAMFImporter_NodeElement_Object*)root_child), mesh_list, &tnode);
-			if(tnode != nullptr) node_list.push_back(tnode);
-
-		}
-	}// for(const CAMFImporter_NodeElement* root_child: root_el->Child)
-
-	// And finally read rest of nodes.
-	//
-	for(const CAMFImporter_NodeElement* root_child: root_el->Child)
-	{
-		// 4. <constellation>
-		if(root_child->Type == CAMFImporter_NodeElement::ENET_Constellation)
-		{
-			// <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's.
-			Postprocess_BuildConstellation(*((CAMFImporter_NodeElement_Constellation*)root_child), node_list);
-		}
-
-		// 5, <metadata>
-		if(root_child->Type == CAMFImporter_NodeElement::ENET_Metadata) meta_list.push_back((CAMFImporter_NodeElement_Metadata*)root_child);
-	}// for(const CAMFImporter_NodeElement* root_child: root_el->Child)
-
-	// at now we can add collected metadata to root node
-	Postprocess_AddMetadata(meta_list, *pScene->mRootNode);
-	//
-	// Check constellation children
-	//
-	// As said in specification:
-	// "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing."
-	// What that means? For example: if some object is used in constellation then you must show only constellation but not original object.
-	// And at this step we are checking that relations.
-nl_clean_loop:
-
-	if(node_list.size() > 1)
-	{
-		// walk through all nodes
-		for(std::list<aiNode*>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it)
-		{
-			// and try to find them in another top nodes.
-			std::list<aiNode*>::const_iterator next_it = nl_it;
-
-			++next_it;
-			for(; next_it != node_list.end(); ++next_it)
-			{
-				if((*next_it)->FindNode((*nl_it)->mName) != nullptr)
-				{
-					// if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
-					node_list.erase(nl_it);
-
-					goto nl_clean_loop;
-				}
-			}// for(; next_it != node_list.end(); next_it++)
-		}// for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
-	}
-
-	//
-	// move created objects to aiScene
-	//
-	//
-	// Nodes
-	if(!node_list.empty())
-	{
-		std::list<aiNode*>::const_iterator nl_it = node_list.begin();
-
-		pScene->mRootNode->mNumChildren = static_cast<unsigned int>(node_list.size());
-		pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
-		for(size_t i = 0; i < pScene->mRootNode->mNumChildren; i++)
-		{
-			// Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have
-			// mRootNode only as parent.
-			(*nl_it)->mParent = pScene->mRootNode;
-			pScene->mRootNode->mChildren[i] = *nl_it++;
-		}
-	}// if(node_list.size() > 0)
-
-	//
-	// Meshes
-	if(!mesh_list.empty())
-	{
-		std::list<aiMesh*>::const_iterator ml_it = mesh_list.begin();
-
-		pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
-		pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
-		for(size_t i = 0; i < pScene->mNumMeshes; i++) pScene->mMeshes[i] = *ml_it++;
-	}// if(mesh_list.size() > 0)
-
-	//
-	// Textures
-	pScene->mNumTextures = static_cast<unsigned int>(mTexture_Converted.size());
-	if(pScene->mNumTextures > 0)
-	{
-		size_t idx;
-
-		idx = 0;
-		pScene->mTextures = new aiTexture*[pScene->mNumTextures];
-		for(const SPP_Texture& tex_convd: mTexture_Converted)
-		{
-			pScene->mTextures[idx] = new aiTexture;
-			pScene->mTextures[idx]->mWidth = static_cast<unsigned int>(tex_convd.Width);
-			pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height);
-			pScene->mTextures[idx]->pcData = (aiTexel*)tex_convd.Data;
-			// texture format description.
-			strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint);
-			idx++;
-		}// for(const SPP_Texture& tex_convd: mTexture_Converted)
-
-		// Create materials for embedded textures.
-		idx = 0;
-		pScene->mNumMaterials = static_cast<unsigned int>(mTexture_Converted.size());
-		pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
-		for(const SPP_Texture& tex_convd: mTexture_Converted)
-		{
-			const aiString texture_id(AI_EMBEDDED_TEXNAME_PREFIX + to_string(idx));
-			const int mode = aiTextureOp_Multiply;
-			const int repeat = tex_convd.Tiled ? 1 : 0;
-
-			pScene->mMaterials[idx] = new aiMaterial;
-			pScene->mMaterials[idx]->AddProperty(&texture_id, AI_MATKEY_TEXTURE_DIFFUSE(0));
-			pScene->mMaterials[idx]->AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
-			pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
-			pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
-			idx++;
-		}
-	}// if(pScene->mNumTextures > 0)
-}// END: after that walk through children of root and collect data
-
-}// namespace Assimp
-
-#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 253 - 311
code/3DS/3DSConverter.cpp → code/AssetLib/3DS/3DSConverter.cpp

@@ -46,11 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // internal headers
 #include "3DSLoader.h"
 #include "Common/TargetAnimation.h"
+#include <assimp/StringComparison.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
-#include <assimp/StringComparison.h>
-#include <memory>
 #include <cctype>
+#include <memory>
 
 using namespace Assimp;
 
@@ -58,75 +58,66 @@ static const unsigned int NotSet = 0xcdcdcdcd;
 
 // ------------------------------------------------------------------------------------------------
 // Setup final material indices, generae a default material if necessary
-void Discreet3DSImporter::ReplaceDefaultMaterial()
-{
+void Discreet3DSImporter::ReplaceDefaultMaterial() {
     // Try to find an existing material that matches the
     // typical default material setting:
     // - no textures
     // - diffuse color (in grey!)
     // NOTE: This is here to workaround the fact that some
     // exporters are writing a default material, too.
-    unsigned int idx( NotSet );
-    for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
-    {
+    unsigned int idx(NotSet);
+    for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) {
         std::string s = mScene->mMaterials[i].mName;
-        for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) {
-            *it = static_cast< char >( ::tolower( *it ) );
+        for (std::string::iterator it = s.begin(); it != s.end(); ++it) {
+            *it = static_cast<char>(::tolower(*it));
         }
 
-        if (std::string::npos == s.find("default"))continue;
+        if (std::string::npos == s.find("default")) continue;
 
         if (mScene->mMaterials[i].mDiffuse.r !=
-            mScene->mMaterials[i].mDiffuse.g ||
-            mScene->mMaterials[i].mDiffuse.r !=
-            mScene->mMaterials[i].mDiffuse.b)continue;
-
-        if (mScene->mMaterials[i].sTexDiffuse.mMapName.length()   != 0  ||
-            mScene->mMaterials[i].sTexBump.mMapName.length()      != 0  ||
-            mScene->mMaterials[i].sTexOpacity.mMapName.length()   != 0  ||
-            mScene->mMaterials[i].sTexEmissive.mMapName.length()  != 0  ||
-            mScene->mMaterials[i].sTexSpecular.mMapName.length()  != 0  ||
-            mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )
-        {
+                        mScene->mMaterials[i].mDiffuse.g ||
+                mScene->mMaterials[i].mDiffuse.r !=
+                        mScene->mMaterials[i].mDiffuse.b) continue;
+
+        if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
+                mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
+                mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
+                mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
+                mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
+                mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) {
             continue;
         }
         idx = i;
     }
-    if ( NotSet == idx ) {
-        idx = ( unsigned int )mScene->mMaterials.size();
+    if (NotSet == idx) {
+        idx = (unsigned int)mScene->mMaterials.size();
     }
 
     // now iterate through all meshes and through all faces and
     // find all faces that are using the default material
     unsigned int cnt = 0;
     for (std::vector<D3DS::Mesh>::iterator
-        i =  mScene->mMeshes.begin();
-        i != mScene->mMeshes.end();++i)
-    {
+                    i = mScene->mMeshes.begin();
+            i != mScene->mMeshes.end(); ++i) {
         for (std::vector<unsigned int>::iterator
-            a =  (*i).mFaceMaterials.begin();
-            a != (*i).mFaceMaterials.end();++a)
-        {
+                        a = (*i).mFaceMaterials.begin();
+                a != (*i).mFaceMaterials.end(); ++a) {
             // NOTE: The additional check seems to be necessary,
             // some exporters seem to generate invalid data here
-            if (0xcdcdcdcd == (*a))
-            {
+            if (0xcdcdcdcd == (*a)) {
                 (*a) = idx;
                 ++cnt;
-            }
-            else if ( (*a) >= mScene->mMaterials.size())
-            {
+            } else if ((*a) >= mScene->mMaterials.size()) {
                 (*a) = idx;
                 ASSIMP_LOG_WARN("Material index overflow in 3DS file. Using default material");
                 ++cnt;
             }
         }
     }
-    if (cnt && idx == mScene->mMaterials.size())
-    {
+    if (cnt && idx == mScene->mMaterials.size()) {
         // We need to create our own default material
         D3DS::Material sMat("%%%DEFAULT");
-        sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
+        sMat.mDiffuse = aiColor3D(0.3f, 0.3f, 0.3f);
         mScene->mMaterials.push_back(sMat);
 
         ASSIMP_LOG_INFO("3DS: Generating default material");
@@ -135,22 +126,17 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
 
 // ------------------------------------------------------------------------------------------------
 // Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
-void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
-{
-    for (std::vector< D3DS::Face >::iterator i =  sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i)
-    {
+void Discreet3DSImporter::CheckIndices(D3DS::Mesh &sMesh) {
+    for (std::vector<D3DS::Face>::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end(); ++i) {
         // check whether all indices are in range
-        for (unsigned int a = 0; a < 3;++a)
-        {
-            if ((*i).mIndices[a] >= sMesh.mPositions.size())
-            {
+        for (unsigned int a = 0; a < 3; ++a) {
+            if ((*i).mIndices[a] >= sMesh.mPositions.size()) {
                 ASSIMP_LOG_WARN("3DS: Vertex index overflow)");
-                (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
+                (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size() - 1;
             }
-            if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size())
-            {
+            if (!sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) {
                 ASSIMP_LOG_WARN("3DS: Texture coordinate index overflow)");
-                (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1;
+                (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size() - 1;
             }
         }
     }
@@ -158,24 +144,21 @@ void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
 
 // ------------------------------------------------------------------------------------------------
 // Generate out unique verbose format representation
-void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
-{
+void Discreet3DSImporter::MakeUnique(D3DS::Mesh &sMesh) {
     // TODO: really necessary? I don't think. Just a waste of memory and time
     // to do it now in a separate buffer.
 
     // Allocate output storage
-    std::vector<aiVector3D> vNew  (sMesh.mFaces.size() * 3);
+    std::vector<aiVector3D> vNew(sMesh.mFaces.size() * 3);
     std::vector<aiVector3D> vNew2;
     if (sMesh.mTexCoords.size())
         vNew2.resize(sMesh.mFaces.size() * 3);
 
-    for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i)
-    {
-        D3DS::Face& face = sMesh.mFaces[i];
+    for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size(); ++i) {
+        D3DS::Face &face = sMesh.mFaces[i];
 
         // Positions
-        for (unsigned int a = 0; a < 3;++a,++base)
-        {
+        for (unsigned int a = 0; a < 3; ++a, ++base) {
             vNew[base] = sMesh.mPositions[face.mIndices[a]];
             if (sMesh.mTexCoords.size())
                 vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
@@ -189,26 +172,24 @@ void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
 
 // ------------------------------------------------------------------------------------------------
 // Convert a 3DS texture to texture keys in an aiMaterial
-void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
-{
+void CopyTexture(aiMaterial &mat, D3DS::Texture &texture, aiTextureType type) {
     // Setup the texture name
     aiString tex;
-    tex.Set( texture.mMapName);
-    mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
+    tex.Set(texture.mMapName);
+    mat.AddProperty(&tex, AI_MATKEY_TEXTURE(type, 0));
 
     // Setup the texture blend factor
     if (is_not_qnan(texture.mTextureBlend))
-        mat.AddProperty<ai_real>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
+        mat.AddProperty<ai_real>(&texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type, 0));
 
     // Setup the texture mapping mode
     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));
+    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 ...
-    if (texture.mMapMode == aiTextureMapMode_Mirror)
-    {
+    if (texture.mMapMode == aiTextureMapMode_Mirror) {
         texture.mScaleU *= 2.0;
         texture.mScaleV *= 2.0;
         texture.mOffsetU /= 2.0;
@@ -216,21 +197,19 @@ void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type)
     }
 
     // Setup texture UV transformations
-    mat.AddProperty<ai_real>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
+    mat.AddProperty<ai_real>(&texture.mOffsetU, 5, AI_MATKEY_UVTRANSFORM(type, 0));
 }
 
 // ------------------------------------------------------------------------------------------------
 // Convert a 3DS material to an aiMaterial
-void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
-    aiMaterial& mat)
-{
+void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat,
+        aiMaterial &mat) {
     // NOTE: Pass the background image to the viewer by bypassing the
     // material system. This is an evil hack, never do it again!
-    if (0 != mBackgroundImage.length() && bHasBG)
-    {
+    if (0 != mBackgroundImage.length() && bHasBG) {
         aiString tex;
-        tex.Set( mBackgroundImage);
-        mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
+        tex.Set(mBackgroundImage);
+        mat.AddProperty(&tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
 
         // Be sure this is only done for the first material
         mBackgroundImage = std::string("");
@@ -242,143 +221,138 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
     oldMat.mAmbient.b += mClrAmbient.b;
 
     aiString name;
-    name.Set( oldMat.mName);
-    mat.AddProperty( &name, AI_MATKEY_NAME);
+    name.Set(oldMat.mName);
+    mat.AddProperty(&name, AI_MATKEY_NAME);
 
     // Material colors
-    mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
-    mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
-    mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
-    mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+    mat.AddProperty(&oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+    mat.AddProperty(&oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+    mat.AddProperty(&oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+    mat.AddProperty(&oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
 
     // Phong shininess and shininess strength
     if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
-        D3DS::Discreet3DS::Metal == oldMat.mShading)
-    {
-        if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength)
-        {
+            D3DS::Discreet3DS::Metal == oldMat.mShading) {
+        if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) {
             oldMat.mShading = D3DS::Discreet3DS::Gouraud;
-        }
-        else
-        {
-            mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
-            mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
+        } else {
+            mat.AddProperty(&oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+            mat.AddProperty(&oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
         }
     }
 
     // Opacity
-    mat.AddProperty<ai_real>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
+    mat.AddProperty<ai_real>(&oldMat.mTransparency, 1, AI_MATKEY_OPACITY);
 
     // Bump height scaling
-    mat.AddProperty<ai_real>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
+    mat.AddProperty<ai_real>(&oldMat.mBumpHeight, 1, AI_MATKEY_BUMPSCALING);
 
     // Two sided rendering?
-    if (oldMat.mTwoSided)
-    {
+    if (oldMat.mTwoSided) {
         int i = 1;
-        mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
+        mat.AddProperty<int>(&i, 1, AI_MATKEY_TWOSIDED);
     }
 
     // Shading mode
     aiShadingMode eShading = aiShadingMode_NoShading;
-    switch (oldMat.mShading)
-    {
-        case D3DS::Discreet3DS::Flat:
-            eShading = aiShadingMode_Flat; break;
-
-        // I don't know what "Wire" shading should be,
-        // assume it is simple lambertian diffuse shading
-        case D3DS::Discreet3DS::Wire:
-            {
-                // Set the wireframe flag
-                unsigned int iWire = 1;
-                mat.AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
-            }
-
-        case D3DS::Discreet3DS::Gouraud:
-            eShading = aiShadingMode_Gouraud; break;
-
-        // assume cook-torrance shading for metals.
-        case D3DS::Discreet3DS::Phong :
-            eShading = aiShadingMode_Phong; break;
-
-        case D3DS::Discreet3DS::Metal :
-            eShading = aiShadingMode_CookTorrance; break;
-
-            // FIX to workaround a warning with GCC 4 who complained
-            // about a missing case Blinn: here - Blinn isn't a valid
-            // value in the 3DS Loader, it is just needed for ASE
-        case D3DS::Discreet3DS::Blinn :
-            eShading = aiShadingMode_Blinn; break;
+    switch (oldMat.mShading) {
+    case D3DS::Discreet3DS::Flat:
+        eShading = aiShadingMode_Flat;
+        break;
+
+    // I don't know what "Wire" shading should be,
+    // assume it is simple lambertian diffuse shading
+    case D3DS::Discreet3DS::Wire: {
+        // Set the wireframe flag
+        unsigned int iWire = 1;
+        mat.AddProperty<int>((int *)&iWire, 1, AI_MATKEY_ENABLE_WIREFRAME);
+    }
+
+    case D3DS::Discreet3DS::Gouraud:
+        eShading = aiShadingMode_Gouraud;
+        break;
+
+    // assume cook-torrance shading for metals.
+    case D3DS::Discreet3DS::Phong:
+        eShading = aiShadingMode_Phong;
+        break;
+
+    case D3DS::Discreet3DS::Metal:
+        eShading = aiShadingMode_CookTorrance;
+        break;
+
+        // FIX to workaround a warning with GCC 4 who complained
+        // about a missing case Blinn: here - Blinn isn't a valid
+        // value in the 3DS Loader, it is just needed for ASE
+    case D3DS::Discreet3DS::Blinn:
+        eShading = aiShadingMode_Blinn;
+        break;
     }
     int eShading_ = static_cast<int>(eShading);
     mat.AddProperty<int>(&eShading_, 1, AI_MATKEY_SHADING_MODEL);
 
     // DIFFUSE texture
-    if( oldMat.sTexDiffuse.mMapName.length() > 0)
-        CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
+    if (oldMat.sTexDiffuse.mMapName.length() > 0)
+        CopyTexture(mat, oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
 
     // SPECULAR texture
-    if( oldMat.sTexSpecular.mMapName.length() > 0)
-        CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR);
+    if (oldMat.sTexSpecular.mMapName.length() > 0)
+        CopyTexture(mat, oldMat.sTexSpecular, aiTextureType_SPECULAR);
 
     // OPACITY texture
-    if( oldMat.sTexOpacity.mMapName.length() > 0)
-        CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY);
+    if (oldMat.sTexOpacity.mMapName.length() > 0)
+        CopyTexture(mat, oldMat.sTexOpacity, aiTextureType_OPACITY);
 
     // EMISSIVE texture
-    if( oldMat.sTexEmissive.mMapName.length() > 0)
-        CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE);
+    if (oldMat.sTexEmissive.mMapName.length() > 0)
+        CopyTexture(mat, oldMat.sTexEmissive, aiTextureType_EMISSIVE);
 
     // BUMP texture
-    if( oldMat.sTexBump.mMapName.length() > 0)
-        CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT);
+    if (oldMat.sTexBump.mMapName.length() > 0)
+        CopyTexture(mat, oldMat.sTexBump, aiTextureType_HEIGHT);
 
     // SHININESS texture
-    if( oldMat.sTexShininess.mMapName.length() > 0)
-        CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS);
+    if (oldMat.sTexShininess.mMapName.length() > 0)
+        CopyTexture(mat, oldMat.sTexShininess, aiTextureType_SHININESS);
 
     // REFLECTION texture
-    if( oldMat.sTexReflective.mMapName.length() > 0)
-        CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION);
+    if (oldMat.sTexReflective.mMapName.length() > 0)
+        CopyTexture(mat, oldMat.sTexReflective, aiTextureType_REFLECTION);
 
     // Store the name of the material itself, too
-    if( oldMat.mName.length())  {
+    if (oldMat.mName.length()) {
         aiString tex;
-        tex.Set( oldMat.mName);
-        mat.AddProperty( &tex, AI_MATKEY_NAME);
+        tex.Set(oldMat.mName);
+        mat.AddProperty(&tex, AI_MATKEY_NAME);
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Split meshes by their materials and generate output aiMesh'es
-void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
-{
-    std::vector<aiMesh*> avOutMeshes;
+void Discreet3DSImporter::ConvertMeshes(aiScene *pcOut) {
+    std::vector<aiMesh *> avOutMeshes;
     avOutMeshes.reserve(mScene->mMeshes.size() * 2);
 
-    unsigned int iFaceCnt = 0,num = 0;
+    unsigned int iFaceCnt = 0, num = 0;
     aiString name;
 
     // we need to split all meshes by their materials
-    for (std::vector<D3DS::Mesh>::iterator i =  mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i)    {
-        std::unique_ptr< std::vector<unsigned int>[] > aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
+    for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end(); ++i) {
+        std::unique_ptr<std::vector<unsigned int>[]> aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
 
-        name.length = ASSIMP_itoa10(name.data,num++);
+        name.length = ASSIMP_itoa10(name.data, num++);
 
         unsigned int iNum = 0;
-        for (std::vector<unsigned int>::const_iterator a =  (*i).mFaceMaterials.begin();
-            a != (*i).mFaceMaterials.end();++a,++iNum)
-        {
+        for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
+                a != (*i).mFaceMaterials.end(); ++a, ++iNum) {
             aiSplit[*a].push_back(iNum);
         }
         // now generate submeshes
-        for (unsigned int p = 0; p < mScene->mMaterials.size();++p)
-        {
+        for (unsigned int p = 0; p < mScene->mMaterials.size(); ++p) {
             if (aiSplit[p].empty()) {
                 continue;
             }
-            aiMesh* meshOut = new aiMesh();
+            aiMesh *meshOut = new aiMesh();
             meshOut->mName = name;
             meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
 
@@ -386,36 +360,33 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
             meshOut->mMaterialIndex = p;
 
             // use the color data as temporary storage
-            meshOut->mColors[0] = (aiColor4D*)(&*i);
+            meshOut->mColors[0] = (aiColor4D *)(&*i);
             avOutMeshes.push_back(meshOut);
 
             // convert vertices
             meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
-            meshOut->mNumVertices = meshOut->mNumFaces*3;
+            meshOut->mNumVertices = meshOut->mNumFaces * 3;
 
             // allocate enough storage for faces
             meshOut->mFaces = new aiFace[meshOut->mNumFaces];
             iFaceCnt += meshOut->mNumFaces;
 
             meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
-            meshOut->mNormals  = new aiVector3D[meshOut->mNumVertices];
-            if ((*i).mTexCoords.size())
-            {
+            meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
+            if ((*i).mTexCoords.size()) {
                 meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
             }
-            for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q)
-            {
+            for (unsigned int q = 0, base = 0; q < aiSplit[p].size(); ++q) {
                 unsigned int index = aiSplit[p][q];
-                aiFace& face = meshOut->mFaces[q];
+                aiFace &face = meshOut->mFaces[q];
 
                 face.mIndices = new unsigned int[3];
                 face.mNumIndices = 3;
 
-                for (unsigned int a = 0; a < 3;++a,++base)
-                {
+                for (unsigned int a = 0; a < 3; ++a, ++base) {
                     unsigned int idx = (*i).mFaces[index].mIndices[a];
-                    meshOut->mVertices[base]  = (*i).mPositions[idx];
-                    meshOut->mNormals [base]  = (*i).mNormals[idx];
+                    meshOut->mVertices[base] = (*i).mPositions[idx];
+                    meshOut->mNormals[base] = (*i).mNormals[idx];
 
                     if ((*i).mTexCoords.size())
                         meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
@@ -428,8 +399,8 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
 
     // Copy them to the output array
     pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
-    pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
-    for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) {
+    pcOut->mMeshes = new aiMesh *[pcOut->mNumMeshes]();
+    for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) {
         pcOut->mMeshes[a] = avOutMeshes[a];
     }
 
@@ -441,47 +412,44 @@ void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
 
 // ------------------------------------------------------------------------------------------------
 // Add a node to the scenegraph and setup its final transformation
-void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
-    D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/)
-{
+void Discreet3DSImporter::AddNodeToGraph(aiScene *pcSOut, aiNode *pcOut,
+        D3DS::Node *pcIn, aiMatrix4x4 & /*absTrafo*/) {
     std::vector<unsigned int> iArray;
     iArray.reserve(3);
 
     aiMatrix4x4 abs;
 
     // Find all meshes with the same name as the node
-    for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
-    {
-        const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
-        ai_assert(NULL != pcMesh);
+    for (unsigned int a = 0; a < pcSOut->mNumMeshes; ++a) {
+        const D3DS::Mesh *pcMesh = (const D3DS::Mesh *)pcSOut->mMeshes[a]->mColors[0];
+        ai_assert(nullptr != pcMesh);
 
         if (pcIn->mName == pcMesh->mName)
             iArray.push_back(a);
     }
-    if (!iArray.empty())
-    {
+    if (!iArray.empty()) {
         // The matrix should be identical for all meshes with the
         // same name. It HAS to be identical for all meshes .....
-        D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
+        D3DS::Mesh *imesh = ((D3DS::Mesh *)pcSOut->mMeshes[iArray[0]]->mColors[0]);
 
         // Compute the inverse of the transformation matrix to move the
         // vertices back to their relative and local space
         aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
-        mInv.Inverse();mInvTransposed.Transpose();
+        mInv.Inverse();
+        mInvTransposed.Transpose();
         aiVector3D pivot = pcIn->vPivot;
 
         pcOut->mNumMeshes = (unsigned int)iArray.size();
         pcOut->mMeshes = new unsigned int[iArray.size()];
-        for (unsigned int i = 0;i < iArray.size();++i)  {
+        for (unsigned int i = 0; i < iArray.size(); ++i) {
             const unsigned int iIndex = iArray[i];
-            aiMesh* const mesh = pcSOut->mMeshes[iIndex];
+            aiMesh *const mesh = pcSOut->mMeshes[iIndex];
 
-            if (mesh->mColors[1] == NULL)
-            {
+            if (mesh->mColors[1] == nullptr) {
                 // Transform the vertices back into their local space
                 // fixme: consider computing normals after this, so we don't need to transform them
-                const aiVector3D* const pvEnd = mesh->mVertices + mesh->mNumVertices;
-                aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
+                const aiVector3D *const pvEnd = mesh->mVertices + mesh->mNumVertices;
+                aiVector3D *pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
 
                 for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
                     *pvCurrent = mInv * (*pvCurrent);
@@ -489,8 +457,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
                 }
 
                 // Handle negative transformation matrix determinant -> invert vertex x
-                if (imesh->mMat.Determinant() < 0.0f)
-                {
+                if (imesh->mMat.Determinant() < 0.0f) {
                     /* we *must* have normals */
                     for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) {
                         pvCurrent->x *= -1.f;
@@ -500,17 +467,15 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
                 }
 
                 // Handle pivot point
-                if (pivot.x || pivot.y || pivot.z)
-                {
-                    for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent)  {
+                if (pivot.x || pivot.y || pivot.z) {
+                    for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) {
                         *pvCurrent -= pivot;
                     }
                 }
 
-                mesh->mColors[1] = (aiColor4D*)1;
-            }
-            else
-                mesh->mColors[1] = (aiColor4D*)1;
+                mesh->mColors[1] = (aiColor4D *)1;
+            } else
+                mesh->mColors[1] = (aiColor4D *)1;
 
             // Setup the mesh index
             pcOut->mMeshes[i] = iIndex;
@@ -519,78 +484,75 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
 
     // Setup the name of the node
     // First instance keeps its name otherwise something might break, all others will be postfixed with their instance number
-    if (pcIn->mInstanceNumber > 1)
-    {
+    if (pcIn->mInstanceNumber > 1) {
         char tmp[12];
         ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
         std::string tempStr = pcIn->mName + "_inst_";
         tempStr += tmp;
         pcOut->mName.Set(tempStr);
-    }
-    else
+    } else
         pcOut->mName.Set(pcIn->mName);
 
     // Now build the transformation matrix of the node
     // ROTATION
-    if (pcIn->aRotationKeys.size()){
+    if (pcIn->aRotationKeys.size()) {
 
         // FIX to get to Assimp's quaternion conventions
         for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
             (*it).mValue.w *= -1.f;
         }
 
-        pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() );
-    }
-    else if (pcIn->aCameraRollKeys.size())
-    {
-        aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue),
-            pcOut->mTransformation);
+        pcOut->mTransformation = aiMatrix4x4(pcIn->aRotationKeys[0].mValue.GetMatrix());
+    } else if (pcIn->aCameraRollKeys.size()) {
+        aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(-pcIn->aCameraRollKeys[0].mValue),
+                pcOut->mTransformation);
     }
 
     // SCALING
-    aiMatrix4x4& m = pcOut->mTransformation;
-    if (pcIn->aScalingKeys.size())
-    {
-        const aiVector3D& v = pcIn->aScalingKeys[0].mValue;
-        m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x;
-        m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y;
-        m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z;
+    aiMatrix4x4 &m = pcOut->mTransformation;
+    if (pcIn->aScalingKeys.size()) {
+        const aiVector3D &v = pcIn->aScalingKeys[0].mValue;
+        m.a1 *= v.x;
+        m.b1 *= v.x;
+        m.c1 *= v.x;
+        m.a2 *= v.y;
+        m.b2 *= v.y;
+        m.c2 *= v.y;
+        m.a3 *= v.z;
+        m.b3 *= v.z;
+        m.c3 *= v.z;
     }
 
     // TRANSLATION
-    if (pcIn->aPositionKeys.size())
-    {
-        const aiVector3D& v = pcIn->aPositionKeys[0].mValue;
+    if (pcIn->aPositionKeys.size()) {
+        const aiVector3D &v = pcIn->aPositionKeys[0].mValue;
         m.a4 += v.x;
         m.b4 += v.y;
         m.c4 += v.z;
     }
 
     // Generate animation channels for the node
-    if (pcIn->aPositionKeys.size()  > 1  || pcIn->aRotationKeys.size()   > 1 ||
-        pcIn->aScalingKeys.size()   > 1  || pcIn->aCameraRollKeys.size() > 1 ||
-        pcIn->aTargetPositionKeys.size() > 1)
-    {
-        aiAnimation* anim = pcSOut->mAnimations[0];
+    if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
+            pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
+            pcIn->aTargetPositionKeys.size() > 1) {
+        aiAnimation *anim = pcSOut->mAnimations[0];
         ai_assert(nullptr != anim);
 
-        if (pcIn->aCameraRollKeys.size() > 1)
-        {
+        if (pcIn->aCameraRollKeys.size() > 1) {
             ASSIMP_LOG_DEBUG("3DS: Converting camera roll track ...");
 
             // Camera roll keys - in fact they're just rotations
             // around the camera's z axis. The angles are given
             // in degrees (and they're clockwise).
             pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
-            for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i)
-            {
-                aiQuatKey&  q = pcIn->aRotationKeys[i];
-                aiFloatKey& f = pcIn->aCameraRollKeys[i];
+            for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size(); ++i) {
+                aiQuatKey &q = pcIn->aRotationKeys[i];
+                aiFloatKey &f = pcIn->aCameraRollKeys[i];
 
-                q.mTime  = f.mTime;
+                q.mTime = f.mTime;
 
                 // FIX to get to Assimp quaternion conventions
-                q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue));
+                q.mValue = aiQuaternion(0.f, 0.f, AI_DEG_TO_RAD(/*-*/ f.mValue));
             }
         }
 #if 0
@@ -636,102 +598,93 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
         // Cameras or lights define their transformation in their parent node and in the
         // corresponding light or camera chunks. However, we read and process the latter
         // to to be able to return valid cameras/lights even if no scenegraph is given.
-        for (unsigned int n = 0; n < pcSOut->mNumCameras;++n)   {
+        for (unsigned int n = 0; n < pcSOut->mNumCameras; ++n) {
             if (pcSOut->mCameras[n]->mName == pcOut->mName) {
-                pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f);
+                pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f, 0.f, 1.f);
             }
         }
-        for (unsigned int n = 0; n < pcSOut->mNumLights;++n)    {
+        for (unsigned int n = 0; n < pcSOut->mNumLights; ++n) {
             if (pcSOut->mLights[n]->mName == pcOut->mName) {
-                pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f);
+                pcSOut->mLights[n]->mDirection = aiVector3D(0.f, 0.f, 1.f);
             }
         }
 
         // Allocate a new node anim and setup its name
-        aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
+        aiNodeAnim *nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
         nda->mNodeName.Set(pcIn->mName);
 
         // POSITION keys
-        if (pcIn->aPositionKeys.size()  > 0)
-        {
+        if (pcIn->aPositionKeys.size() > 0) {
             nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
             nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
-            ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0],
-                sizeof(aiVectorKey)*nda->mNumPositionKeys);
+            ::memcpy(nda->mPositionKeys, &pcIn->aPositionKeys[0],
+                    sizeof(aiVectorKey) * nda->mNumPositionKeys);
         }
 
         // ROTATION keys
-        if (pcIn->aRotationKeys.size()  > 0)
-        {
+        if (pcIn->aRotationKeys.size() > 0) {
             nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
             nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
 
             // Rotations are quaternion offsets
             aiQuaternion abs1;
-            for (unsigned int n = 0; n < nda->mNumRotationKeys;++n)
-            {
-                const aiQuatKey& q = pcIn->aRotationKeys[n];
+            for (unsigned int n = 0; n < nda->mNumRotationKeys; ++n) {
+                const aiQuatKey &q = pcIn->aRotationKeys[n];
 
                 abs1 = (n ? abs1 * q.mValue : q.mValue);
-                nda->mRotationKeys[n].mTime  = q.mTime;
+                nda->mRotationKeys[n].mTime = q.mTime;
                 nda->mRotationKeys[n].mValue = abs1.Normalize();
             }
         }
 
         // SCALING keys
-        if (pcIn->aScalingKeys.size()  > 0)
-        {
+        if (pcIn->aScalingKeys.size() > 0) {
             nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
             nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
-            ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0],
-                sizeof(aiVectorKey)*nda->mNumScalingKeys);
+            ::memcpy(nda->mScalingKeys, &pcIn->aScalingKeys[0],
+                    sizeof(aiVectorKey) * nda->mNumScalingKeys);
         }
     }
 
     // Allocate storage for children
     pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
-    pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
+    pcOut->mChildren = new aiNode *[pcIn->mChildren.size()];
 
     // Recursively process all children
     const unsigned int size = static_cast<unsigned int>(pcIn->mChildren.size());
-    for (unsigned int i = 0; i < size;++i)
-    {
+    for (unsigned int i = 0; i < size; ++i) {
         pcOut->mChildren[i] = new aiNode();
         pcOut->mChildren[i]->mParent = pcOut;
-        AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs);
+        AddNodeToGraph(pcSOut, pcOut->mChildren[i], pcIn->mChildren[i], abs);
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Find out how many node animation channels we'll have finally
-void CountTracks(D3DS::Node* node, unsigned int& cnt)
-{
+void CountTracks(D3DS::Node *node, unsigned int &cnt) {
     //////////////////////////////////////////////////////////////////////////////
     // We will never generate more than one channel for a node, so
     // this is rather easy here.
 
-    if (node->aPositionKeys.size()  > 1  || node->aRotationKeys.size()   > 1   ||
-        node->aScalingKeys.size()   > 1  || node->aCameraRollKeys.size() > 1 ||
-        node->aTargetPositionKeys.size()  > 1)
-    {
+    if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
+            node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
+            node->aTargetPositionKeys.size() > 1) {
         ++cnt;
 
         // account for the additional channel for the camera/spotlight target position
-        if (node->aTargetPositionKeys.size()  > 1)++cnt;
+        if (node->aTargetPositionKeys.size() > 1) ++cnt;
     }
 
     // Recursively process all children
-    for (unsigned int i = 0; i < node->mChildren.size();++i)
-        CountTracks(node->mChildren[i],cnt);
+    for (unsigned int i = 0; i < node->mChildren.size(); ++i)
+        CountTracks(node->mChildren[i], cnt);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Generate the output node graph
-void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
-{
+void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) {
     pcOut->mRootNode = new aiNode();
-    if (0 == mRootNode->mChildren.size())
-    {
+    if (0 == mRootNode->mChildren.size()) {
         //////////////////////////////////////////////////////////////////////////////
         // It seems the file is so messed up that it has not even a hierarchy.
         // generate a flat hiearachy which looks like this:
@@ -745,29 +698,27 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
         ASSIMP_LOG_WARN("No hierarchy information has been found in the file. ");
 
         pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
-            static_cast<unsigned int>(mScene->mCameras.size() + mScene->mLights.size());
+                                         static_cast<unsigned int>(mScene->mCameras.size() + mScene->mLights.size());
 
-        pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ];
+        pcOut->mRootNode->mChildren = new aiNode *[pcOut->mRootNode->mNumChildren];
         pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
 
         // Build dummy nodes for all meshes
         unsigned int a = 0;
-        for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a)
-        {
-            aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+        for (unsigned int i = 0; i < pcOut->mNumMeshes; ++i, ++a) {
+            aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
             pcNode->mParent = pcOut->mRootNode;
             pcNode->mMeshes = new unsigned int[1];
             pcNode->mMeshes[0] = i;
             pcNode->mNumMeshes = 1;
 
             // Build a name for the node
-            pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "3DSMesh_%u",i);
+            pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "3DSMesh_%u", i);
         }
 
         // Build dummy nodes for all cameras
-        for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a)
-        {
-            aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+        for (unsigned int i = 0; i < (unsigned int)mScene->mCameras.size(); ++i, ++a) {
+            aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
             pcNode->mParent = pcOut->mRootNode;
 
             // Build a name for the node
@@ -775,75 +726,68 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
         }
 
         // Build dummy nodes for all lights
-        for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a)
-        {
-            aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+        for (unsigned int i = 0; i < (unsigned int)mScene->mLights.size(); ++i, ++a) {
+            aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
             pcNode->mParent = pcOut->mRootNode;
 
             // Build a name for the node
             pcNode->mName = mScene->mLights[i]->mName;
         }
-    }
-    else
-    {
+    } else {
         // First of all: find out how many scaling, rotation and translation
         // animation tracks we'll have afterwards
         unsigned int numChannel = 0;
-        CountTracks(mRootNode,numChannel);
+        CountTracks(mRootNode, numChannel);
 
-        if (numChannel)
-        {
+        if (numChannel) {
             // Allocate a primary animation channel
             pcOut->mNumAnimations = 1;
-            pcOut->mAnimations    = new aiAnimation*[1];
-            aiAnimation* anim     = pcOut->mAnimations[0] = new aiAnimation();
+            pcOut->mAnimations = new aiAnimation *[1];
+            aiAnimation *anim = pcOut->mAnimations[0] = new aiAnimation();
 
             anim->mName.Set("3DSMasterAnim");
 
             // Allocate enough storage for all node animation channels,
             // but don't set the mNumChannels member - we'll use it to
             // index into the array
-            anim->mChannels = new aiNodeAnim*[numChannel];
+            anim->mChannels = new aiNodeAnim *[numChannel];
         }
 
         aiMatrix4x4 m;
-        AddNodeToGraph(pcOut,  pcOut->mRootNode, mRootNode,m);
+        AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode, m);
     }
 
     // We used the first and second vertex color set to store some temporary values so we need to cleanup here
-    for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a)
-    {
-        pcOut->mMeshes[a]->mColors[0] = NULL;
-        pcOut->mMeshes[a]->mColors[1] = NULL;
+    for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) {
+        pcOut->mMeshes[a]->mColors[0] = nullptr;
+        pcOut->mMeshes[a]->mColors[1] = nullptr;
     }
 
     pcOut->mRootNode->mTransformation = aiMatrix4x4(
-        1.f,0.f,0.f,0.f,
-        0.f,0.f,1.f,0.f,
-        0.f,-1.f,0.f,0.f,
-        0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation;
+                                                1.f, 0.f, 0.f, 0.f,
+                                                0.f, 0.f, 1.f, 0.f,
+                                                0.f, -1.f, 0.f, 0.f,
+                                                0.f, 0.f, 0.f, 1.f) *
+                                        pcOut->mRootNode->mTransformation;
 
     // If the root node is unnamed name it "<3DSRoot>"
-    if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) ||
-        (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') )
-    {
+    if (::strstr(pcOut->mRootNode->mName.data, "UNNAMED") ||
+            (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$')) {
         pcOut->mRootNode->mName.Set("<3DSRoot>");
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Convert all meshes in the scene and generate the final output scene.
-void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
-{
+void Discreet3DSImporter::ConvertScene(aiScene *pcOut) {
     // Allocate enough storage for all output materials
     pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
-    pcOut->mMaterials    = new aiMaterial*[pcOut->mNumMaterials];
+    pcOut->mMaterials = new aiMaterial *[pcOut->mNumMaterials];
 
     //  ... and convert the 3DS materials to aiMaterial's
-    for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
-    {
-        aiMaterial* pcNew = new aiMaterial();
-        ConvertMaterial(mScene->mMaterials[i],*pcNew);
+    for (unsigned int i = 0; i < pcOut->mNumMaterials; ++i) {
+        aiMaterial *pcNew = new aiMaterial();
+        ConvertMaterial(mScene->mMaterials[i], *pcNew);
         pcOut->mMaterials[i] = pcNew;
     }
 
@@ -852,18 +796,16 @@ void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
 
     // Now copy all light sources to the output scene
     pcOut->mNumLights = (unsigned int)mScene->mLights.size();
-    if (pcOut->mNumLights)
-    {
-        pcOut->mLights = new aiLight*[pcOut->mNumLights];
-        ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights);
+    if (pcOut->mNumLights) {
+        pcOut->mLights = new aiLight *[pcOut->mNumLights];
+        ::memcpy(pcOut->mLights, &mScene->mLights[0], sizeof(void *) * pcOut->mNumLights);
     }
 
     // Now copy all cameras to the output scene
     pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
-    if (pcOut->mNumCameras)
-    {
-        pcOut->mCameras = new aiCamera*[pcOut->mNumCameras];
-        ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras);
+    if (pcOut->mNumCameras) {
+        pcOut->mCameras = new aiCamera *[pcOut->mNumCameras];
+        ::memcpy(pcOut->mCameras, &mScene->mCameras[0], sizeof(void *) * pcOut->mNumCameras);
     }
 }
 

+ 107 - 122
code/3DS/3DSExporter.cpp → code/AssetLib/3DS/3DSExporter.cpp

@@ -43,120 +43,117 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
 
-#include "3DS/3DSExporter.h"
-#include "3DS/3DSLoader.h"
-#include "3DS/3DSHelper.h"
+#include "AssetLib/3DS/3DSExporter.h"
+#include "AssetLib/3DS/3DSHelper.h"
+#include "AssetLib/3DS/3DSLoader.h"
 #include "PostProcessing/SplitLargeMeshes.h"
 
 #include <assimp/SceneCombiner.h>
 #include <assimp/StringComparison.h>
-#include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
 
 #include <memory>
 
 using namespace Assimp;
-namespace Assimp    {
+namespace Assimp {
 using namespace D3DS;
 
 namespace {
 
-    //////////////////////////////////////////////////////////////////////////////////////
-    // Scope utility to write a 3DS file chunk.
-    //
-    // Upon construction, the chunk header is written with the chunk type (flags)
-    // filled out, but the chunk size left empty. Upon destruction, the correct chunk
-    // size based on the then-position of the output stream cursor is filled in.
-    class ChunkWriter {
-        enum {
-              CHUNK_SIZE_NOT_SET = 0xdeadbeef
-            , SIZE_OFFSET        = 2
-        };
-    public:
-
-        ChunkWriter(StreamWriterLE& writer, uint16_t chunk_type)
-            : writer(writer)
-        {
-            chunk_start_pos = writer.GetCurrentPos();
-            writer.PutU2(chunk_type);
-            writer.PutU4((uint32_t)CHUNK_SIZE_NOT_SET);
-        }
-
-        ~ChunkWriter() {
-            std::size_t head_pos = writer.GetCurrentPos();
+//////////////////////////////////////////////////////////////////////////////////////
+// Scope utility to write a 3DS file chunk.
+//
+// Upon construction, the chunk header is written with the chunk type (flags)
+// filled out, but the chunk size left empty. Upon destruction, the correct chunk
+// size based on the then-position of the output stream cursor is filled in.
+class ChunkWriter {
+    enum {
+        CHUNK_SIZE_NOT_SET = 0xdeadbeef,
+        SIZE_OFFSET = 2
+    };
 
-            ai_assert(head_pos > chunk_start_pos);
-            const std::size_t chunk_size = head_pos - chunk_start_pos;
+public:
+    ChunkWriter(StreamWriterLE &writer, uint16_t chunk_type) :
+            writer(writer) {
+        chunk_start_pos = writer.GetCurrentPos();
+        writer.PutU2(chunk_type);
+        writer.PutU4((uint32_t)CHUNK_SIZE_NOT_SET);
+    }
 
-            writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
-            writer.PutU4(static_cast<uint32_t>(chunk_size));
-            writer.SetCurrentPos(head_pos);
-        }
+    ~ChunkWriter() {
+        std::size_t head_pos = writer.GetCurrentPos();
 
-    private:
-        StreamWriterLE& writer;
-        std::size_t chunk_start_pos;
-    };
+        ai_assert(head_pos > chunk_start_pos);
+        const std::size_t chunk_size = head_pos - chunk_start_pos;
 
+        writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
+        writer.PutU4(static_cast<uint32_t>(chunk_size));
+        writer.SetCurrentPos(head_pos);
+    }
 
-    // Return an unique name for a given |mesh| attached to |node| that
-    // preserves the mesh's given name if it has one. |index| is the index
-    // of the mesh in |aiScene::mMeshes|.
-    std::string GetMeshName(const aiMesh& mesh, unsigned int index, const aiNode& node) {
-        static const std::string underscore = "_";
-        char postfix[10] = {0};
-        ASSIMP_itoa10(postfix, index);
+private:
+    StreamWriterLE &writer;
+    std::size_t chunk_start_pos;
+};
+
+// Return an unique name for a given |mesh| attached to |node| that
+// preserves the mesh's given name if it has one. |index| is the index
+// of the mesh in |aiScene::mMeshes|.
+std::string GetMeshName(const aiMesh &mesh, unsigned int index, const aiNode &node) {
+    static const std::string underscore = "_";
+    char postfix[10] = { 0 };
+    ASSIMP_itoa10(postfix, index);
+
+    std::string result = node.mName.C_Str();
+    if (mesh.mName.length > 0) {
+        result += underscore + mesh.mName.C_Str();
+    }
+    return result + underscore + postfix;
+}
 
-        std::string result = node.mName.C_Str();
-        if (mesh.mName.length > 0) {
-            result += underscore + mesh.mName.C_Str();
-        }
-        return result + underscore + postfix;
+// Return an unique name for a given |mat| with original position |index|
+// in |aiScene::mMaterials|. The name preserves the original material
+// name if possible.
+std::string GetMaterialName(const aiMaterial &mat, unsigned int index) {
+    static const std::string underscore = "_";
+    char postfix[10] = { 0 };
+    ASSIMP_itoa10(postfix, index);
+
+    aiString mat_name;
+    if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
+        return mat_name.C_Str() + underscore + postfix;
     }
 
-    // Return an unique name for a given |mat| with original position |index|
-    // in |aiScene::mMaterials|. The name preserves the original material
-    // name if possible.
-    std::string GetMaterialName(const aiMaterial& mat, unsigned int index) {
-        static const std::string underscore = "_";
-        char postfix[10] = {0};
-        ASSIMP_itoa10(postfix, index);
-
-        aiString mat_name;
-        if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
-            return mat_name.C_Str() + underscore + postfix;
-        }
+    return "Material" + underscore + postfix;
+}
 
-        return "Material" + underscore + postfix;
+// Collect world transformations for each node
+void CollectTrafos(const aiNode *node, std::map<const aiNode *, aiMatrix4x4> &trafos) {
+    const aiMatrix4x4 &parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
+    trafos[node] = parent * node->mTransformation;
+    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+        CollectTrafos(node->mChildren[i], trafos);
     }
+}
 
-    // Collect world transformations for each node
-    void CollectTrafos(const aiNode* node, std::map<const aiNode*, aiMatrix4x4>& trafos) {
-        const aiMatrix4x4& parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
-        trafos[node] = parent * node->mTransformation;
-        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
-            CollectTrafos(node->mChildren[i], trafos);
-        }
+// Generate a flat list of the meshes (by index) assigned to each node
+void CollectMeshes(const aiNode *node, std::multimap<const aiNode *, unsigned int> &meshes) {
+    for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+        meshes.insert(std::make_pair(node, node->mMeshes[i]));
     }
-
-    // Generate a flat list of the meshes (by index) assigned to each node
-    void CollectMeshes(const aiNode* node, std::multimap<const aiNode*, unsigned int>& meshes) {
-        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
-            meshes.insert(std::make_pair(node, node->mMeshes[i]));
-        }
-        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
-            CollectMeshes(node->mChildren[i], meshes);
-        }
+    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+        CollectMeshes(node->mChildren[i], meshes);
     }
 }
+} // namespace
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
-void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
-{
-    std::shared_ptr<IOStream> outfile (pIOSystem->Open(pFile, "wb"));
-    if(!outfile) {
+void ExportScene3DS(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    std::shared_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wb"));
+    if (!outfile) {
         throw DeadlyExportError("Could not open output .3ds file: " + std::string(pFile));
     }
 
@@ -167,8 +164,8 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen
     // SplitLargeMeshes can do this, but it requires the correct limit to be set
     // which is not possible with the current way of specifying preprocess steps
     // in |Exporter::ExportFormatEntry|.
-    aiScene* scenecopy_tmp;
-    SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
+    aiScene *scenecopy_tmp;
+    SceneCombiner::CopyScene(&scenecopy_tmp, pScene);
     std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
 
     SplitLargeMeshesProcess_Triangle tri_splitter;
@@ -186,10 +183,8 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen
 } // end of namespace Assimp
 
 // ------------------------------------------------------------------------------------------------
-Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene* scene)
-: scene(scene)
-, writer(outfile)
-{
+Discreet3DSExporter::Discreet3DSExporter(std::shared_ptr<IOStream> &outfile, const aiScene *scene) :
+        scene(scene), writer(outfile) {
     CollectTrafos(scene->mRootNode, trafos);
     CollectMeshes(scene->mRootNode, meshes);
 
@@ -217,10 +212,8 @@ Discreet3DSExporter::~Discreet3DSExporter() {
     // empty
 }
 
-
 // ------------------------------------------------------------------------------------------------
-int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling_level)
-{
+int Discreet3DSExporter::WriteHierarchy(const aiNode &node, int seq, int sibling_level) {
     // 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec
     {
         ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
@@ -237,7 +230,7 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
 
             int16_t hierarchy_pos = static_cast<int16_t>(seq);
             if (sibling_level != -1) {
-                hierarchy_pos =(uint16_t) sibling_level;
+                hierarchy_pos = (uint16_t)sibling_level;
             }
 
             // Write the hierarchy position
@@ -260,7 +253,7 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
         const bool first_child = node.mNumChildren == 0 && i == 0;
 
         const unsigned int mesh_idx = node.mMeshes[i];
-        const aiMesh& mesh = *scene->mMeshes[mesh_idx];
+        const aiMesh &mesh = *scene->mMeshes[mesh_idx];
 
         ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
         {
@@ -276,15 +269,14 @@ int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteMaterials()
-{
+void Discreet3DSExporter::WriteMaterials() {
     for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
         ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
-        const aiMaterial& mat = *scene->mMaterials[i];
+        const aiMaterial &mat = *scene->mMaterials[i];
 
         {
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATNAME);
-            const std::string& name = GetMaterialName(mat, i);
+            const std::string &name = GetMaterialName(mat, i);
             WriteString(name);
         }
 
@@ -314,7 +306,7 @@ void Discreet3DSExporter::WriteMaterials()
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
 
             Discreet3DS::shadetype3ds shading_mode_out;
-            switch(shading_mode) {
+            switch (shading_mode) {
             case aiShadingMode_Flat:
             case aiShadingMode_NoShading:
                 shading_mode_out = Discreet3DS::Flat;
@@ -341,7 +333,6 @@ void Discreet3DSExporter::WriteMaterials()
             writer.PutU2(static_cast<uint16_t>(shading_mode_out));
         }
 
-
         float f;
         if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
@@ -370,8 +361,7 @@ void Discreet3DSExporter::WriteMaterials()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags)
-{
+void Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type, uint16_t chunk_flags) {
     aiString path;
     aiTextureMapMode map_mode[2] = {
         aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
@@ -400,8 +390,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
         uint16_t val = 0; // WRAP
         if (map_mode[0] == aiTextureMapMode_Mirror) {
             val = 0x2;
-        }
-        else if (map_mode[0] == aiTextureMapMode_Decal) {
+        } else if (map_mode[0] == aiTextureMapMode_Decal) {
             val = 0x10;
         }
         writer.PutU2(val);
@@ -410,8 +399,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteMeshes()
-{
+void Discreet3DSExporter::WriteMeshes() {
     // NOTE: 3DS allows for instances. However:
     //   i)  not all importers support reading them
     //   ii) instances are not as flexible as they are in assimp, in particular,
@@ -423,25 +411,24 @@ void Discreet3DSExporter::WriteMeshes()
     // Furthermore, the TRIMESH is transformed into world space so that it will
     // appear correctly if importers don't read the scene hierarchy at all.
     for (MeshesByNodeMap::const_iterator it = meshes.begin(); it != meshes.end(); ++it) {
-        const aiNode& node = *(*it).first;
+        const aiNode &node = *(*it).first;
         const unsigned int mesh_idx = (*it).second;
 
-        const aiMesh& mesh = *scene->mMeshes[mesh_idx];
+        const aiMesh &mesh = *scene->mMeshes[mesh_idx];
 
         // This should not happen if the SLM step is correctly executed
         // before the scene is handed to the exporter
         ai_assert(mesh.mNumVertices <= 0xffff);
         ai_assert(mesh.mNumFaces <= 0xffff);
 
-        const aiMatrix4x4& trafo = trafos[&node];
+        const aiMatrix4x4 &trafo = trafos[&node];
 
         ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK);
 
         // Mesh name is tied to the node it is attached to so it can later be referenced
-        const std::string& name = GetMeshName(mesh, mesh_idx, node);
+        const std::string &name = GetMeshName(mesh, mesh_idx, node);
         WriteString(name);
 
-
         // TRIMESH chunk
         ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH);
 
@@ -452,7 +439,7 @@ void Discreet3DSExporter::WriteMeshes()
             const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
             writer.PutU2(count);
             for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
-                const aiVector3D& v = trafo * mesh.mVertices[i];
+                const aiVector3D &v = trafo * mesh.mVertices[i];
                 writer.PutF4(v.x);
                 writer.PutF4(v.y);
                 writer.PutF4(v.z);
@@ -466,7 +453,7 @@ void Discreet3DSExporter::WriteMeshes()
             writer.PutU2(count);
 
             for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
-                const aiVector3D& v = mesh.mTextureCoords[0][i];
+                const aiVector3D &v = mesh.mTextureCoords[0][i];
                 writer.PutF4(v.x);
                 writer.PutF4(v.y);
             }
@@ -481,7 +468,7 @@ void Discreet3DSExporter::WriteMeshes()
             // Count triangles, discard lines and points
             uint16_t count = 0;
             for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
-                const aiFace& f = mesh.mFaces[i];
+                const aiFace &f = mesh.mFaces[i];
                 if (f.mNumIndices < 3) {
                     continue;
                 }
@@ -492,7 +479,7 @@ void Discreet3DSExporter::WriteMeshes()
 
             writer.PutU2(count);
             for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
-                const aiFace& f = mesh.mFaces[i];
+                const aiFace &f = mesh.mFaces[i];
                 if (f.mNumIndices < 3) {
                     continue;
                 }
@@ -524,10 +511,9 @@ void Discreet3DSExporter::WriteMeshes()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
-{
+void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh &mesh) {
     ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACEMAT);
-    const std::string& name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
+    const std::string &name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
     WriteString(name);
 
     // Because assimp splits meshes by material, only a single
@@ -542,7 +528,7 @@ void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteString(const std::string& s) {
+void Discreet3DSExporter::WriteString(const std::string &s) {
     for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
         writer.PutI1(*it);
     }
@@ -550,7 +536,7 @@ void Discreet3DSExporter::WriteString(const std::string& s) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteString(const aiString& s) {
+void Discreet3DSExporter::WriteString(const aiString &s) {
     for (std::size_t i = 0; i < s.length; ++i) {
         writer.PutI1(s.data[i]);
     }
@@ -558,7 +544,7 @@ void Discreet3DSExporter::WriteString(const aiString& s) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteColor(const aiColor3D& color) {
+void Discreet3DSExporter::WriteColor(const aiColor3D &color) {
     ChunkWriter curChunk(writer, Discreet3DS::CHUNK_RGBF);
     writer.PutF4(color.r);
     writer.PutF4(color.g);
@@ -577,6 +563,5 @@ void Discreet3DSExporter::WritePercentChunk(double f) {
     writer.PutF8(f);
 }
 
-
 #endif // ASSIMP_BUILD_NO_3DS_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 0 - 0
code/3DS/3DSExporter.h → code/AssetLib/3DS/3DSExporter.h


+ 0 - 0
code/3DS/3DSHelper.h → code/AssetLib/3DS/3DSHelper.h


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 246 - 303
code/AssetLib/3DS/3DSLoader.cpp


+ 0 - 0
code/3DS/3DSLoader.h → code/AssetLib/3DS/3DSLoader.h


+ 0 - 0
code/3MF/3MFXmlTags.h → code/AssetLib/3MF/3MFXmlTags.h


+ 120 - 130
code/3MF/D3MFExporter.cpp → code/AssetLib/3MF/D3MFExporter.cpp

@@ -44,81 +44,74 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "D3MFExporter.h"
 
+#include <assimp/Exceptional.h>
+#include <assimp/StringUtils.h>
 #include <assimp/scene.h>
-#include <assimp/IOSystem.hpp>
-#include <assimp/IOStream.hpp>
-#include <assimp/Exporter.hpp>
 #include <assimp/DefaultLogger.hpp>
-#include <assimp/StringUtils.h>
-#include <assimp/Exceptional.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
 
 #include "3MFXmlTags.h"
 #include "D3MFOpcPackage.h"
 
 #ifdef ASSIMP_USE_HUNTER
-#  include <zip/zip.h>
+#include <zip/zip.h>
 #else
-#  include <contrib/zip/src/zip.h>
+#include <contrib/zip/src/zip.h>
 #endif
 
 namespace Assimp {
 
-void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) {
-    if ( nullptr == pIOSystem ) {
-        throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
+void ExportScene3MF(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    if (nullptr == pIOSystem) {
+        throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
     }
-    D3MF::D3MFExporter myExporter( pFile, pScene );
-    if ( myExporter.validate() ) {
-        if ( pIOSystem->Exists( pFile ) ) {
-            if ( !pIOSystem->DeleteFile( pFile ) ) {
-                throw DeadlyExportError( "File exists, cannot override : " + std::string( pFile ) );
+    D3MF::D3MFExporter myExporter(pFile, pScene);
+    if (myExporter.validate()) {
+        if (pIOSystem->Exists(pFile)) {
+            if (!pIOSystem->DeleteFile(pFile)) {
+                throw DeadlyExportError("File exists, cannot override : " + std::string(pFile));
             }
         }
         bool ok = myExporter.exportArchive(pFile);
-        if ( !ok ) {
-            throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
+        if (!ok) {
+            throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
         }
     }
 }
 
 namespace D3MF {
 
-D3MFExporter::D3MFExporter( const char* pFile, const aiScene* pScene )
-: mArchiveName( pFile )
-, m_zipArchive( nullptr )
-, mScene( pScene )
-, mModelOutput()
-, mRelOutput()
-, mContentOutput()
-, mBuildItems()
-, mRelations() {
+D3MFExporter::D3MFExporter(const char *pFile, const aiScene *pScene) :
+        mArchiveName(pFile), m_zipArchive(nullptr), mScene(pScene), mModelOutput(), mRelOutput(), mContentOutput(), mBuildItems(), mRelations() {
     // empty
 }
 
 D3MFExporter::~D3MFExporter() {
-    for ( size_t i = 0; i < mRelations.size(); ++i ) {
-        delete mRelations[ i ];
+    for (size_t i = 0; i < mRelations.size(); ++i) {
+        delete mRelations[i];
     }
     mRelations.clear();
 }
 
 bool D3MFExporter::validate() {
-    if ( mArchiveName.empty() ) {
+    if (mArchiveName.empty()) {
         return false;
     }
 
-    if ( nullptr == mScene ) {
+    if (nullptr == mScene) {
         return false;
     }
 
     return true;
 }
 
-bool D3MFExporter::exportArchive( const char *file ) {
-    bool ok( true );
+bool D3MFExporter::exportArchive(const char *file) {
+    bool ok(true);
 
-    m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' );
-    if ( nullptr == m_zipArchive ) {
+    m_zipArchive = zip_open(file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
+    if (nullptr == m_zipArchive) {
         return false;
     }
 
@@ -126,7 +119,7 @@ bool D3MFExporter::exportArchive( const char *file ) {
     ok |= export3DModel();
     ok |= exportRelations();
 
-    zip_close( m_zipArchive );
+    zip_close(m_zipArchive);
     m_zipArchive = nullptr;
 
     return ok;
@@ -145,7 +138,7 @@ bool D3MFExporter::exportContentTypes() {
     mContentOutput << std::endl;
     mContentOutput << "</Types>";
     mContentOutput << std::endl;
-    exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE );
+    exportContentTyp(XmlTag::CONTENT_TYPES_ARCHIVE);
 
     return true;
 }
@@ -157,20 +150,20 @@ bool D3MFExporter::exportRelations() {
     mRelOutput << std::endl;
     mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
 
-    for ( size_t i = 0; i < mRelations.size(); ++i ) {
-        if ( mRelations[ i ]->target[ 0 ] == '/' ) {
-            mRelOutput << "<Relationship Target=\"" << mRelations[ i ]->target << "\" ";
+    for (size_t i = 0; i < mRelations.size(); ++i) {
+        if (mRelations[i]->target[0] == '/') {
+            mRelOutput << "<Relationship Target=\"" << mRelations[i]->target << "\" ";
         } else {
-            mRelOutput << "<Relationship Target=\"/" << mRelations[ i ]->target << "\" ";
+            mRelOutput << "<Relationship Target=\"/" << mRelations[i]->target << "\" ";
         }
         mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
-        mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />";
+        mRelOutput << "Type=\"" << mRelations[i]->type << "\" />";
         mRelOutput << std::endl;
     }
     mRelOutput << "</Relationships>";
     mRelOutput << std::endl;
 
-    writeRelInfoToFile( "_rels", ".rels" );
+    writeRelInfoToFile("_rels", ".rels");
     mRelOutput.flush();
 
     return true;
@@ -181,8 +174,8 @@ bool D3MFExporter::export3DModel() {
 
     writeHeader();
     mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\""
-            << " xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
-            << std::endl;
+                 << " xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
+                 << std::endl;
     mModelOutput << "<" << XmlTag::resources << ">";
     mModelOutput << std::endl;
 
@@ -192,7 +185,6 @@ bool D3MFExporter::export3DModel() {
 
     writeObjects();
 
-
     mModelOutput << "</" << XmlTag::resources << ">";
     mModelOutput << std::endl;
     writeBuild();
@@ -203,9 +195,9 @@ bool D3MFExporter::export3DModel() {
     info->id = "rel0";
     info->target = "/3D/3DModel.model";
     info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
-    mRelations.push_back( info );
+    mRelations.push_back(info);
 
-    writeModelToArchive( "3D", "3DModel.model" );
+    writeModelToArchive("3D", "3DModel.model");
     mModelOutput.flush();
 
     return true;
@@ -217,22 +209,22 @@ void D3MFExporter::writeHeader() {
 }
 
 void D3MFExporter::writeMetaData() {
-    if ( nullptr == mScene->mMetaData ) {
+    if (nullptr == mScene->mMetaData) {
         return;
     }
 
-    const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties );
-    if ( 0 == numMetaEntries ) {
+    const unsigned int numMetaEntries(mScene->mMetaData->mNumProperties);
+    if (0 == numMetaEntries) {
         return;
     }
 
-	const aiString *key = nullptr;
+    const aiString *key = nullptr;
     const aiMetadataEntry *entry(nullptr);
-    for ( size_t i = 0; i < numMetaEntries; ++i ) {
-        mScene->mMetaData->Get( i, key, entry );
-        std::string k( key->C_Str() );
+    for (size_t i = 0; i < numMetaEntries; ++i) {
+        mScene->mMetaData->Get(i, key, entry);
+        std::string k(key->C_Str());
         aiString value;
-        mScene->mMetaData->Get(  k, value );
+        mScene->mMetaData->Get(k, value);
         mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
         mModelOutput << value.C_Str();
         mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
@@ -241,115 +233,114 @@ void D3MFExporter::writeMetaData() {
 
 void D3MFExporter::writeBaseMaterials() {
     mModelOutput << "<basematerials id=\"1\">\n";
-    std::string strName, hexDiffuseColor , tmp;
-    for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) {
-        aiMaterial *mat = mScene->mMaterials[ i ];
+    std::string strName, hexDiffuseColor, tmp;
+    for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
+        aiMaterial *mat = mScene->mMaterials[i];
         aiString name;
-        if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) {
-            strName = "basemat_" + to_string( i );
+        if (mat->Get(AI_MATKEY_NAME, name) != aiReturn_SUCCESS) {
+            strName = "basemat_" + to_string(i);
         } else {
             strName = name.C_Str();
         }
         aiColor4D color;
-        if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) {
+        if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, color) == aiReturn_SUCCESS) {
             hexDiffuseColor.clear();
             tmp.clear();
             // rgbs %
-    		if(color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1){
-    	
-    			 hexDiffuseColor = Rgba2Hex(
-    			 	(int)((ai_real)color.r)*255,
-    			 	(int)((ai_real)color.g)*255,
-    			 	(int)((ai_real)color.b)*255,
-    			 	(int)((ai_real)color.a)*255,
-    			 	true
-    			 );
-    			 
-    		}else{
-    			hexDiffuseColor = "#";
-            	tmp = DecimalToHexa( (ai_real) color.r );
-            	hexDiffuseColor += tmp;
-            	tmp = DecimalToHexa((ai_real)color.g);
-            	hexDiffuseColor += tmp;
-            	tmp = DecimalToHexa((ai_real)color.b);
-            	hexDiffuseColor += tmp;
-            	tmp = DecimalToHexa((ai_real)color.a);
-            	hexDiffuseColor += tmp;
-    		}
+            if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) {
+
+                hexDiffuseColor = Rgba2Hex(
+                        (int)((ai_real)color.r) * 255,
+                        (int)((ai_real)color.g) * 255,
+                        (int)((ai_real)color.b) * 255,
+                        (int)((ai_real)color.a) * 255,
+                        true);
+
+            } else {
+                hexDiffuseColor = "#";
+                tmp = DecimalToHexa((ai_real)color.r);
+                hexDiffuseColor += tmp;
+                tmp = DecimalToHexa((ai_real)color.g);
+                hexDiffuseColor += tmp;
+                tmp = DecimalToHexa((ai_real)color.b);
+                hexDiffuseColor += tmp;
+                tmp = DecimalToHexa((ai_real)color.a);
+                hexDiffuseColor += tmp;
+            }
         } else {
             hexDiffuseColor = "#FFFFFFFF";
         }
 
-        mModelOutput << "<base name=\""+strName+"\" "+" displaycolor=\""+hexDiffuseColor+"\" />\n";
+        mModelOutput << "<base name=\"" + strName + "\" " + " displaycolor=\"" + hexDiffuseColor + "\" />\n";
     }
     mModelOutput << "</basematerials>\n";
 }
 
 void D3MFExporter::writeObjects() {
-    if ( nullptr == mScene->mRootNode ) {
+    if (nullptr == mScene->mRootNode) {
         return;
     }
 
     aiNode *root = mScene->mRootNode;
-    for ( unsigned int i = 0; i < root->mNumChildren; ++i ) {
-        aiNode *currentNode( root->mChildren[ i ] );
-        if ( nullptr == currentNode ) {
+    for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+        aiNode *currentNode(root->mChildren[i]);
+        if (nullptr == currentNode) {
             continue;
         }
         mModelOutput << "<" << XmlTag::object << " id=\"" << i + 2 << "\" type=\"model\">";
         mModelOutput << std::endl;
-        for ( unsigned int j = 0; j < currentNode->mNumMeshes; ++j ) {
-            aiMesh *currentMesh = mScene->mMeshes[ currentNode->mMeshes[ j ] ];
-            if ( nullptr == currentMesh ) {
+        for (unsigned int j = 0; j < currentNode->mNumMeshes; ++j) {
+            aiMesh *currentMesh = mScene->mMeshes[currentNode->mMeshes[j]];
+            if (nullptr == currentMesh) {
                 continue;
             }
-            writeMesh( currentMesh );
+            writeMesh(currentMesh);
         }
-        mBuildItems.push_back( i );
+        mBuildItems.push_back(i);
 
         mModelOutput << "</" << XmlTag::object << ">";
         mModelOutput << std::endl;
     }
 }
 
-void D3MFExporter::writeMesh( aiMesh *mesh ) {
-    if ( nullptr == mesh ) {
+void D3MFExporter::writeMesh(aiMesh *mesh) {
+    if (nullptr == mesh) {
         return;
     }
 
     mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
     mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
-    for ( unsigned int i = 0; i < mesh->mNumVertices; ++i ) {
-        writeVertex( mesh->mVertices[ i ] );
+    for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+        writeVertex(mesh->mVertices[i]);
     }
     mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
 
-    const unsigned int matIdx( mesh->mMaterialIndex );
+    const unsigned int matIdx(mesh->mMaterialIndex);
 
-    writeFaces( mesh, matIdx );
+    writeFaces(mesh, matIdx);
 
     mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
 }
 
-void D3MFExporter::writeVertex( const aiVector3D &pos ) {
+void D3MFExporter::writeVertex(const aiVector3D &pos) {
     mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
     mModelOutput << std::endl;
 }
 
-void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
-    if ( nullptr == mesh ) {
+void D3MFExporter::writeFaces(aiMesh *mesh, unsigned int matIdx) {
+    if (nullptr == mesh) {
         return;
     }
 
-    if ( !mesh->HasFaces() ) {
+    if (!mesh->HasFaces()) {
         return;
     }
     mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
-    for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) {
-        aiFace &currentFace = mesh->mFaces[ i ];
-        mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\""
-                << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ]
-                << "\" pid=\"1\" p1=\""+to_string(matIdx)+"\" />";
+    for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+        aiFace &currentFace = mesh->mFaces[i];
+        mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[0] << "\" v2=\""
+                     << currentFace.mIndices[1] << "\" v3=\"" << currentFace.mIndices[2]
+                     << "\" pid=\"1\" p1=\"" + to_string(matIdx) + "\" />";
         mModelOutput << std::endl;
     }
     mModelOutput << "</" << XmlTag::triangles << ">";
@@ -359,7 +350,7 @@ void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) {
 void D3MFExporter::writeBuild() {
     mModelOutput << "<" << XmlTag::build << ">" << std::endl;
 
-    for ( size_t i = 0; i < mBuildItems.size(); ++i ) {
+    for (size_t i = 0; i < mBuildItems.size(); ++i) {
         mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 2 << "\"/>";
         mModelOutput << std::endl;
     }
@@ -367,46 +358,45 @@ void D3MFExporter::writeBuild() {
     mModelOutput << std::endl;
 }
 
-void D3MFExporter::exportContentTyp( const std::string &filename ) {
-    if ( nullptr == m_zipArchive ) {
-        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+void D3MFExporter::exportContentTyp(const std::string &filename) {
+    if (nullptr == m_zipArchive) {
+        throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
     }
     const std::string entry = filename;
-    zip_entry_open( m_zipArchive, entry.c_str() );
+    zip_entry_open(m_zipArchive, entry.c_str());
 
-    const std::string &exportTxt( mContentOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+    const std::string &exportTxt(mContentOutput.str());
+    zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
 
-    zip_entry_close( m_zipArchive );
+    zip_entry_close(m_zipArchive);
 }
 
-void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) {
-    if ( nullptr == m_zipArchive ) {
-        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+void D3MFExporter::writeModelToArchive(const std::string &folder, const std::string &modelName) {
+    if (nullptr == m_zipArchive) {
+        throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
     }
     const std::string entry = folder + "/" + modelName;
-    zip_entry_open( m_zipArchive, entry.c_str() );
+    zip_entry_open(m_zipArchive, entry.c_str());
 
-    const std::string &exportTxt( mModelOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+    const std::string &exportTxt(mModelOutput.str());
+    zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
 
-    zip_entry_close( m_zipArchive );
+    zip_entry_close(m_zipArchive);
 }
 
-void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) {
-    if ( nullptr == m_zipArchive ) {
-        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
+void D3MFExporter::writeRelInfoToFile(const std::string &folder, const std::string &relName) {
+    if (nullptr == m_zipArchive) {
+        throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
     }
     const std::string entry = folder + "/" + relName;
-    zip_entry_open( m_zipArchive, entry.c_str() );
+    zip_entry_open(m_zipArchive, entry.c_str());
 
-    const std::string &exportTxt( mRelOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+    const std::string &exportTxt(mRelOutput.str());
+    zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
 
-    zip_entry_close( m_zipArchive );
+    zip_entry_close(m_zipArchive);
 }
 
-
 } // Namespace D3MF
 } // Namespace Assimp
 

+ 0 - 0
code/3MF/D3MFExporter.h → code/AssetLib/3MF/D3MFExporter.h


+ 138 - 140
code/3MF/D3MFImporter.cpp → code/AssetLib/3MF/D3MFImporter.cpp

@@ -44,24 +44,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "D3MFImporter.h"
 
-#include <assimp/scene.h>
-#include <assimp/IOSystem.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/importerdesc.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
 #include <assimp/ZipArchiveIOSystem.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
 
-#include <string>
-#include <vector>
-#include <map>
 #include <cassert>
+#include <map>
 #include <memory>
+#include <string>
+#include <vector>
 
-#include "D3MFOpcPackage.h"
-#include <assimp/irrXMLWrapper.h>
 #include "3MFXmlTags.h"
+#include "D3MFOpcPackage.h"
 #include <assimp/fast_atof.h>
+#include <assimp/irrXMLWrapper.h>
 
 #include <iomanip>
 
@@ -70,90 +70,90 @@ namespace D3MF {
 
 class XmlSerializer {
 public:
-    using MatArray = std::vector<aiMaterial*>;
+    using MatArray = std::vector<aiMaterial *>;
     using MatId2MatArray = std::map<unsigned int, std::vector<unsigned int>>;
 
-    XmlSerializer(XmlReader* xmlReader)
-    : mMeshes()
-    , mMatArray()
-    , mActiveMatGroup( 99999999 )
-    , mMatId2MatArray()
-    , xmlReader(xmlReader){
-		// empty
+    XmlSerializer(XmlReader *xmlReader) :
+            mMeshes(),
+            mMatArray(),
+            mActiveMatGroup(99999999),
+            mMatId2MatArray(),
+            xmlReader(xmlReader) {
+        // empty
     }
 
     ~XmlSerializer() {
         // empty
     }
 
-    void ImportXml(aiScene* scene) {
-        if ( nullptr == scene ) {
+    void ImportXml(aiScene *scene) {
+        if (nullptr == scene) {
             return;
         }
 
         scene->mRootNode = new aiNode();
-        std::vector<aiNode*> children;
+        std::vector<aiNode *> children;
 
         std::string nodeName;
-        while(ReadToEndElement(D3MF::XmlTag::model)) {
+        while (ReadToEndElement(D3MF::XmlTag::model)) {
             nodeName = xmlReader->getNodeName();
-            if( nodeName == D3MF::XmlTag::object) {
+            if (nodeName == D3MF::XmlTag::object) {
                 children.push_back(ReadObject(scene));
-            } else if( nodeName == D3MF::XmlTag::build) {
-                // 
-            } else if ( nodeName == D3MF::XmlTag::basematerials ) {
+            } else if (nodeName == D3MF::XmlTag::build) {
+                //
+            } else if (nodeName == D3MF::XmlTag::basematerials) {
                 ReadBaseMaterials();
-            } else if ( nodeName == D3MF::XmlTag::meta ) {
+            } else if (nodeName == D3MF::XmlTag::meta) {
                 ReadMetadata();
             }
         }
 
-        if ( scene->mRootNode->mName.length == 0 ) {
-            scene->mRootNode->mName.Set( "3MF" );
+        if (scene->mRootNode->mName.length == 0) {
+            scene->mRootNode->mName.Set("3MF");
         }
 
         // import the metadata
-        if ( !mMetaData.empty() ) {
-            const size_t numMeta( mMetaData.size() );
-            scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>( numMeta ) );
-            for ( size_t i = 0; i < numMeta; ++i ) {
-                aiString val( mMetaData[ i ].value );
-                scene->mMetaData->Set(static_cast<unsigned int>( i ), mMetaData[ i ].name, val );
+        if (!mMetaData.empty()) {
+            const size_t numMeta(mMetaData.size());
+            scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
+            for (size_t i = 0; i < numMeta; ++i) {
+                aiString val(mMetaData[i].value);
+                scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
             }
         }
 
         // import the meshes
-        scene->mNumMeshes = static_cast<unsigned int>( mMeshes.size());
-        scene->mMeshes = new aiMesh*[scene->mNumMeshes]();
-        std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes);
+        scene->mNumMeshes = static_cast<unsigned int>(mMeshes.size());
+        scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
+        std::copy(mMeshes.begin(), mMeshes.end(), scene->mMeshes);
 
         // import the materials
-        scene->mNumMaterials = static_cast<unsigned int>( mMatArray.size() );
-        if ( 0 != scene->mNumMaterials ) {
-            scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ];
-            std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials );
+        scene->mNumMaterials = static_cast<unsigned int>(mMatArray.size());
+        if (0 != scene->mNumMaterials) {
+            scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
+            std::copy(mMatArray.begin(), mMatArray.end(), scene->mMaterials);
         }
 
         // create the scenegraph
         scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
-        scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren]();
+        scene->mRootNode->mChildren = new aiNode *[scene->mRootNode->mNumChildren]();
         std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
     }
 
 private:
-    aiNode* ReadObject(aiScene* scene) {
+    aiNode *ReadObject(aiScene *scene) {
         std::unique_ptr<aiNode> node(new aiNode());
 
         std::vector<unsigned long> meshIds;
 
-        const char *attrib( nullptr );
+        const char *attrib(nullptr);
         std::string name, type;
-        attrib = xmlReader->getAttributeValue( D3MF::XmlTag::id.c_str() );
-        if ( nullptr != attrib ) {
+        attrib = xmlReader->getAttributeValue(D3MF::XmlTag::id.c_str());
+        if (nullptr != attrib) {
             name = attrib;
         }
-        attrib = xmlReader->getAttributeValue( D3MF::XmlTag::type.c_str() );
-        if ( nullptr != attrib ) {
+        attrib = xmlReader->getAttributeValue(D3MF::XmlTag::type.c_str());
+        if (nullptr != attrib) {
             type = attrib;
         }
 
@@ -162,8 +162,8 @@ private:
 
         size_t meshIdx = mMeshes.size();
 
-        while(ReadToEndElement(D3MF::XmlTag::object)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
+        while (ReadToEndElement(D3MF::XmlTag::object)) {
+            if (xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
                 auto mesh = ReadMesh();
 
                 mesh->mName.Set(name);
@@ -183,11 +183,11 @@ private:
     }
 
     aiMesh *ReadMesh() {
-        aiMesh* mesh = new aiMesh();
-        while(ReadToEndElement(D3MF::XmlTag::mesh)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
+        aiMesh *mesh = new aiMesh();
+        while (ReadToEndElement(D3MF::XmlTag::mesh)) {
+            if (xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
                 ImportVertices(mesh);
-            } else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
+            } else if (xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
                 ImportTriangles(mesh);
             }
         }
@@ -196,24 +196,24 @@ private:
     }
 
     void ReadMetadata() {
-        const std::string name = xmlReader->getAttributeValue( D3MF::XmlTag::meta_name.c_str() );
+        const std::string name = xmlReader->getAttributeValue(D3MF::XmlTag::meta_name.c_str());
         xmlReader->read();
         const std::string value = xmlReader->getNodeData();
 
-        if ( name.empty() ) {
+        if (name.empty()) {
             return;
         }
 
         MetaEntry entry;
         entry.name = name;
         entry.value = value;
-        mMetaData.push_back( entry );
+        mMetaData.push_back(entry);
     }
 
-    void ImportVertices(aiMesh* mesh) {
+    void ImportVertices(aiMesh *mesh) {
         std::vector<aiVector3D> vertices;
-        while(ReadToEndElement(D3MF::XmlTag::vertices)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
+        while (ReadToEndElement(D3MF::XmlTag::vertices)) {
+            if (xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
                 vertices.push_back(ReadVertex());
             }
         }
@@ -233,20 +233,20 @@ private:
         return vertex;
     }
 
-    void ImportTriangles(aiMesh* mesh) {
-         std::vector<aiFace> faces;
-
-         while(ReadToEndElement(D3MF::XmlTag::triangles)) {
-             const std::string nodeName( xmlReader->getNodeName() );
-             if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
-                 faces.push_back(ReadTriangle());
-                 const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::p1.c_str() ) );
-                 if ( nullptr != pidToken ) {
-                     int matIdx( std::atoi( pidToken ) );
-                     mesh->mMaterialIndex = matIdx;
-                 }
-             }
-         }
+    void ImportTriangles(aiMesh *mesh) {
+        std::vector<aiFace> faces;
+
+        while (ReadToEndElement(D3MF::XmlTag::triangles)) {
+            const std::string nodeName(xmlReader->getNodeName());
+            if (xmlReader->getNodeName() == D3MF::XmlTag::triangle) {
+                faces.push_back(ReadTriangle());
+                const char *pidToken(xmlReader->getAttributeValue(D3MF::XmlTag::p1.c_str()));
+                if (nullptr != pidToken) {
+                    int matIdx(std::atoi(pidToken));
+                    mesh->mMaterialIndex = matIdx;
+                }
+            }
+        }
 
         mesh->mNumFaces = static_cast<unsigned int>(faces.size());
         mesh->mFaces = new aiFace[mesh->mNumFaces];
@@ -269,117 +269,115 @@ private:
 
     void ReadBaseMaterials() {
         std::vector<unsigned int> MatIdArray;
-        const char *baseMaterialId( xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_id.c_str() ) );
-        if ( nullptr != baseMaterialId ) {
-            unsigned int id = std::atoi( baseMaterialId );
-            const size_t newMatIdx( mMatArray.size() );
-            if ( id != mActiveMatGroup ) {
+        const char *baseMaterialId(xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_id.c_str()));
+        if (nullptr != baseMaterialId) {
+            unsigned int id = std::atoi(baseMaterialId);
+            const size_t newMatIdx(mMatArray.size());
+            if (id != mActiveMatGroup) {
                 mActiveMatGroup = id;
-                MatId2MatArray::const_iterator it( mMatId2MatArray.find( id ) );
-                if ( mMatId2MatArray.end() == it ) {
+                MatId2MatArray::const_iterator it(mMatId2MatArray.find(id));
+                if (mMatId2MatArray.end() == it) {
                     MatIdArray.clear();
-                    mMatId2MatArray[ id ] = MatIdArray;
+                    mMatId2MatArray[id] = MatIdArray;
                 } else {
                     MatIdArray = it->second;
                 }
             }
-            MatIdArray.push_back( static_cast<unsigned int>( newMatIdx ) );
-            mMatId2MatArray[ mActiveMatGroup ] = MatIdArray;
+            MatIdArray.push_back(static_cast<unsigned int>(newMatIdx));
+            mMatId2MatArray[mActiveMatGroup] = MatIdArray;
         }
 
-        while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) {
-            mMatArray.push_back( readMaterialDef() );
+        while (ReadToEndElement(D3MF::XmlTag::basematerials)) {
+            mMatArray.push_back(readMaterialDef());
         }
     }
 
-    bool parseColor( const char *color, aiColor4D &diffuse ) {
-        if ( nullptr == color ) {
+    bool parseColor(const char *color, aiColor4D &diffuse) {
+        if (nullptr == color) {
             return false;
         }
 
         //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
-        const size_t len( strlen( color ) );
-        if ( 9 != len && 7 != len) {
+        const size_t len(strlen(color));
+        if (9 != len && 7 != len) {
             return false;
         }
 
-        const char *buf( color );
-        if ( '#' != *buf ) {
+        const char *buf(color);
+        if ('#' != *buf) {
             return false;
         }
         ++buf;
-        char comp[ 3 ] = { 0,0,'\0' };
+        char comp[3] = { 0, 0, '\0' };
 
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.r = static_cast<ai_real>( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
-
+        diffuse.r = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
+        diffuse.g = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
+        diffuse.b = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
-        if(7 == len)
+        if (7 == len)
             return true;
-        comp[ 0 ] = *buf;
+        comp[0] = *buf;
         ++buf;
-        comp[ 1 ] = *buf;
+        comp[1] = *buf;
         ++buf;
-        diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
+        diffuse.a = static_cast<ai_real>(strtol(comp, NULL, 16)) / ai_real(255.0);
 
         return true;
     }
 
-    void assignDiffuseColor( aiMaterial *mat ) {
-        const char *color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() );
+    void assignDiffuseColor(aiMaterial *mat) {
+        const char *color = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_displaycolor.c_str());
         aiColor4D diffuse;
-        if ( parseColor( color, diffuse ) ) {
-            mat->AddProperty<aiColor4D>( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
+        if (parseColor(color, diffuse)) {
+            mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
         }
-
     }
     aiMaterial *readMaterialDef() {
-        aiMaterial *mat( nullptr );
-        const char *name( nullptr );
-        const std::string nodeName( xmlReader->getNodeName() );
-        if ( nodeName == D3MF::XmlTag::basematerials_base ) {
-            name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() );
+        aiMaterial *mat(nullptr);
+        const char *name(nullptr);
+        const std::string nodeName(xmlReader->getNodeName());
+        if (nodeName == D3MF::XmlTag::basematerials_base) {
+            name = xmlReader->getAttributeValue(D3MF::XmlTag::basematerials_name.c_str());
             std::string stdMatName;
             aiString matName;
-            std::string strId( to_string( mActiveMatGroup ) );
+            std::string strId(to_string(mActiveMatGroup));
             stdMatName += "id";
             stdMatName += strId;
             stdMatName += "_";
-            if ( nullptr != name ) {
-                stdMatName += std::string( name );
+            if (nullptr != name) {
+                stdMatName += std::string(name);
             } else {
                 stdMatName += "basemat";
             }
-            matName.Set( stdMatName );
+            matName.Set(stdMatName);
 
             mat = new aiMaterial;
-            mat->AddProperty( &matName, AI_MATKEY_NAME );
+            mat->AddProperty(&matName, AI_MATKEY_NAME);
 
-            assignDiffuseColor( mat );
+            assignDiffuseColor(mat);
         }
 
         return mat;
     }
 
 private:
-    bool ReadToStartElement(const std::string& startTag) {
-        while(xmlReader->read()) {
-            const std::string &nodeName( xmlReader->getNodeName() );
+    bool ReadToStartElement(const std::string &startTag) {
+        while (xmlReader->read()) {
+            const std::string &nodeName(xmlReader->getNodeName());
             if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && nodeName == startTag) {
                 return true;
             } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == startTag) {
@@ -390,9 +388,9 @@ private:
         return false;
     }
 
-    bool ReadToEndElement(const std::string& closeTag) {
-        while(xmlReader->read()) {
-            const std::string &nodeName( xmlReader->getNodeName() );
+    bool ReadToEndElement(const std::string &closeTag) {
+        while (xmlReader->read()) {
+            const std::string &nodeName(xmlReader->getNodeName());
             if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) {
                 return true;
             } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == closeTag) {
@@ -410,11 +408,11 @@ private:
         std::string value;
     };
     std::vector<MetaEntry> mMetaData;
-    std::vector<aiMesh*> mMeshes;
+    std::vector<aiMesh *> mMeshes;
     MatArray mMatArray;
     unsigned int mActiveMatGroup;
     MatId2MatArray mMatId2MatArray;
-    XmlReader* xmlReader;
+    XmlReader *xmlReader;
 };
 
 } //namespace D3MF
@@ -432,8 +430,8 @@ static const aiImporterDesc desc = {
     "3mf"
 };
 
-D3MFImporter::D3MFImporter()
-: BaseImporter() {
+D3MFImporter::D3MFImporter() :
+        BaseImporter() {
     // empty
 }
 
@@ -442,17 +440,17 @@ D3MFImporter::~D3MFImporter() {
 }
 
 bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
-    const std::string extension( GetExtension( filename ) );
-    if(extension == desc.mFileExtensions ) {
+    const std::string extension(GetExtension(filename));
+    if (extension == desc.mFileExtensions) {
         return true;
-    } else if ( !extension.length() || checkSig ) {
-        if ( nullptr == pIOHandler ) {
+    } else if (!extension.length() || checkSig) {
+        if (nullptr == pIOHandler) {
             return false;
         }
-        if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
+        if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
             return false;
         }
-        D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );
+        D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
         return opcPackage.validate();
     }
 
@@ -467,7 +465,7 @@ const aiImporterDesc *D3MFImporter::GetInfo() const {
     return &desc;
 }
 
-void D3MFImporter::InternReadFile( const std::string &filename, aiScene *pScene, IOSystem *pIOHandler ) {
+void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, IOSystem *pIOHandler) {
     D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
 
     std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream()));

+ 0 - 0
code/3MF/D3MFImporter.h → code/AssetLib/3MF/D3MFImporter.h


+ 0 - 0
code/3MF/D3MFOpcPackage.cpp → code/AssetLib/3MF/D3MFOpcPackage.cpp


+ 0 - 0
code/3MF/D3MFOpcPackage.h → code/AssetLib/3MF/D3MFOpcPackage.h


+ 37 - 37
code/AC/ACLoader.cpp → code/AssetLib/AC/ACLoader.cpp

@@ -192,7 +192,7 @@ void AC3DImporter::LoadObjectSection(std::vector<Object> &objects) {
     objects.push_back(Object());
     Object &obj = objects.back();
 
-    aiLight *light = NULL;
+    aiLight *light = nullptr;
     if (!ASSIMP_strincmp(buffer, "light", 5)) {
         // This is a light source. Add it to the list
         mLights->push_back(light = new aiLight());
@@ -472,29 +472,29 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                 }
 
                 switch ((*it).flags & 0xf) {
-                        // closed line
-                    case 0x1:
-                        needMat[idx].first += (unsigned int)(*it).entries.size();
-                        needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
-                        break;
+                    // closed line
+                case 0x1:
+                    needMat[idx].first += (unsigned int)(*it).entries.size();
+                    needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
+                    break;
 
-                        // unclosed line
-                    case 0x2:
-                        needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
-                        needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
-                        break;
+                    // unclosed line
+                case 0x2:
+                    needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
+                    needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
+                    break;
 
-                        // 0 == polygon, else unknown
-                    default:
-                        if ((*it).flags & 0xf) {
-                            ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown");
-                            (*it).flags &= ~(0xf);
-                        }
+                    // 0 == polygon, else unknown
+                default:
+                    if ((*it).flags & 0xf) {
+                        ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown");
+                        (*it).flags &= ~(0xf);
+                    }
 
-                        // the number of faces increments by one, the number
-                        // of vertices by surface.numref.
-                        needMat[idx].first++;
-                        needMat[idx].second += (unsigned int)(*it).entries.size();
+                    // the number of faces increments by one, the number
+                    // of vertices by surface.numref.
+                    needMat[idx].first++;
+                    needMat[idx].second += (unsigned int)(*it).entries.size();
                 };
             }
             unsigned int *pip = node->mMeshes = new unsigned int[node->mNumMeshes];
@@ -535,7 +535,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
 
                 // allocate UV coordinates, but only if the texture name for the
                 // surface is not empty
-                aiVector3D *uv = NULL;
+                aiVector3D *uv = nullptr;
                 if (object.texture.length()) {
                     uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
                     mesh->mNumUVComponents[0] = 2;
@@ -644,20 +644,20 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
     else {
         // generate a name depending on the type of the node
         switch (object.type) {
-            case Object::Group:
-                node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++);
-                break;
-            case Object::Poly:
-                node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++);
-                break;
-            case Object::Light:
-                node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++);
-                break;
-
-                // there shouldn't be more than one world, but we don't care
-            case Object::World:
-                node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++);
-                break;
+        case Object::Group:
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACGroup_%i", mGroupsCounter++);
+            break;
+        case Object::Poly:
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACPoly_%i", mPolysCounter++);
+            break;
+        case Object::Light:
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACLight_%i", mLightsCounter++);
+            break;
+
+            // there shouldn't be more than one world, but we don't care
+        case Object::World:
+            node->mName.length = ::ai_snprintf(node->mName.data, MAXLEN, "ACWorld_%i", mWorldsCounter++);
+            break;
         }
     }
 
@@ -696,7 +696,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
     std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
 
     // Check whether we can read from the file
-    if ( file.get() == nullptr ) {
+    if (file.get() == nullptr) {
         throw DeadlyImportError("Failed to open AC3D file " + pFile + ".");
     }
 

+ 33 - 62
code/AC/ACLoader.h → code/AssetLib/AC/ACLoader.h

@@ -56,27 +56,20 @@ struct aiMesh;
 struct aiMaterial;
 struct aiLight;
 
-
-namespace Assimp    {
+namespace Assimp {
 
 // ---------------------------------------------------------------------------
 /** AC3D (*.ac) importer class
 */
-class AC3DImporter : public BaseImporter
-{
+class AC3DImporter : public BaseImporter {
 public:
     AC3DImporter();
     ~AC3DImporter();
 
     // Represents an AC3D material
-    struct Material
-    {
-        Material()
-            :   rgb     (0.6f,0.6f,0.6f)
-            ,   spec    (1.f,1.f,1.f)
-            ,   shin    (0.f)
-            ,   trans   (0.f)
-        {}
+    struct Material {
+        Material() :
+                rgb(0.6f, 0.6f, 0.6f), spec(1.f, 1.f, 1.f), shin(0.f), trans(0.f) {}
 
         // base color of the material
         aiColor3D rgb;
@@ -101,43 +94,25 @@ public:
     };
 
     // Represents an AC3D surface
-    struct Surface
-    {
-        Surface()
-            :   mat     (0)
-            ,   flags   (0)
-        {}
+    struct Surface {
+        Surface() :
+                mat(0), flags(0) {}
 
-        unsigned int mat,flags;
+        unsigned int mat, flags;
 
-        typedef std::pair<unsigned int, aiVector2D > SurfaceEntry;
-        std::vector< SurfaceEntry > entries;
+        typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
+        std::vector<SurfaceEntry> entries;
     };
 
     // Represents an AC3D object
-    struct Object
-    {
-        Object()
-            :   type    (World)
-            ,   name( "" )
-            ,   children()
-            ,   texture( "" )
-            ,   texRepeat( 1.f, 1.f )
-            ,   texOffset( 0.0f, 0.0f )
-            ,   rotation()
-            ,   translation()
-            ,   vertices()
-            ,   surfaces()
-            ,   numRefs (0)
-            ,   subDiv  (0)
-            ,   crease()
-        {}
+    struct Object {
+        Object() :
+                type(World), name(""), children(), texture(""), texRepeat(1.f, 1.f), texOffset(0.0f, 0.0f), rotation(), translation(), vertices(), surfaces(), numRefs(0), subDiv(0), crease() {}
 
         // Type description
-        enum Type
-        {
+        enum Type {
             World = 0x0,
-            Poly  = 0x1,
+            Poly = 0x1,
             Group = 0x2,
             Light = 0x4
         } type;
@@ -177,37 +152,33 @@ public:
         float crease;
     };
 
-
 public:
-
     // -------------------------------------------------------------------
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details.
      */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+            bool checkSig) const;
 
 protected:
-
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc *GetInfo() const;
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details*/
-    void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
+    void InternReadFile(const std::string &pFile, aiScene *pScene,
+            IOSystem *pIOHandler);
 
     // -------------------------------------------------------------------
     /** Called prior to ReadFile().
     * The function is a request to the importer to update its configuration
     * basing on the Importer's configuration property list.*/
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer *pImp);
 
 private:
-
     // -------------------------------------------------------------------
     /** Get the next line from the file.
      *  @return false if the end of the file was reached*/
@@ -218,7 +189,7 @@ private:
      *  load subobjects, the method returns after a 'kids 0' was
      *  encountered.
      *  @objects List of output objects*/
-    void LoadObjectSection(std::vector<Object>& objects);
+    void LoadObjectSection(std::vector<Object> &objects);
 
     // -------------------------------------------------------------------
     /** Convert all objects into meshes and nodes.
@@ -227,24 +198,24 @@ private:
      *  @param outMaterials List of output materials
      *  @param materials Material list
      *  @param Scenegraph node for the object */
-    aiNode* ConvertObjectSection(Object& object,
-        std::vector<aiMesh*>& meshes,
-        std::vector<aiMaterial*>& outMaterials,
-        const std::vector<Material>& materials,
-        aiNode* parent = NULL);
+    aiNode *ConvertObjectSection(Object &object,
+            std::vector<aiMesh *> &meshes,
+            std::vector<aiMaterial *> &outMaterials,
+            const std::vector<Material> &materials,
+            aiNode *parent = nullptr);
 
     // -------------------------------------------------------------------
     /** Convert a material
      *  @param object Current object
      *  @param matSrc Source material description
      *  @param matDest Destination material to be filled */
-    void ConvertMaterial(const Object& object,
-        const Material& matSrc,
-        aiMaterial& matDest);
+    void ConvertMaterial(const Object &object,
+            const Material &matSrc,
+            aiMaterial &matDest);
 
 private:
     // points to the next data line
-    const char* buffer;
+    const char *buffer;
 
     // Configuration option: if enabled, up to two meshes
     // are generated per material: those faces who have
@@ -261,7 +232,7 @@ private:
     unsigned int mNumMeshes;
 
     // current list of light sources
-    std::vector<aiLight*>* mLights;
+    std::vector<aiLight *> *mLights;
 
     // name counters
     unsigned int mLightsCounter, mGroupsCounter, mPolysCounter, mWorldsCounter;

+ 672 - 0
code/AssetLib/AMF/AMFImporter.cpp

@@ -0,0 +1,672 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/// \file AMFImporter.cpp
+/// \brief AMF-format files importer for Assimp: main algorithm implementation.
+/// \date 2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+
+// Header files, Assimp.
+#include "AMFImporter.hpp"
+#include "AMFImporter_Macro.hpp"
+
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/fast_atof.h>
+
+// Header files, stdlib.
+#include <memory>
+
+namespace Assimp {
+
+/// \var aiImporterDesc AMFImporter::Description
+/// Conastant which hold importer description
+const aiImporterDesc AMFImporter::Description = {
+    "Additive manufacturing file format(AMF) Importer",
+    "smalcom",
+    "",
+    "See documentation in source code. Chapter: Limitations.",
+    aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+    0,
+    0,
+    0,
+    0,
+    "amf"
+};
+
+void AMFImporter::Clear() {
+    mNodeElement_Cur = nullptr;
+    mUnit.clear();
+    mMaterial_Converted.clear();
+    mTexture_Converted.clear();
+    // Delete all elements
+    if (!mNodeElement_List.empty()) {
+        for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
+            delete ne;
+        }
+
+        mNodeElement_List.clear();
+    }
+}
+
+AMFImporter::~AMFImporter() {
+    if (mReader != nullptr) delete mReader;
+    // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
+    Clear();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: find set ************************************************************/
+/*********************************************************************************************************************************************/
+
+bool AMFImporter::Find_NodeElement(const std::string &pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement **pNodeElement) const {
+    for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
+        if ((ne->ID == pID) && (ne->Type == pType)) {
+            if (pNodeElement != nullptr) *pNodeElement = ne;
+
+            return true;
+        }
+    } // for(CAMFImporter_NodeElement* ne: mNodeElement_List)
+
+    return false;
+}
+
+bool AMFImporter::Find_ConvertedNode(const std::string &pID, std::list<aiNode *> &pNodeList, aiNode **pNode) const {
+    aiString node_name(pID.c_str());
+
+    for (aiNode *node : pNodeList) {
+        if (node->mName == node_name) {
+            if (pNode != nullptr) *pNode = node;
+
+            return true;
+        }
+    } // for(aiNode* node: pNodeList)
+
+    return false;
+}
+
+bool AMFImporter::Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const {
+    for (const SPP_Material &mat : mMaterial_Converted) {
+        if (mat.ID == pID) {
+            if (pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
+
+            return true;
+        }
+    } // for(const SPP_Material& mat: mMaterial_Converted)
+
+    return false;
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: throw set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::Throw_CloseNotFound(const std::string &pNode) {
+    throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
+}
+
+void AMFImporter::Throw_IncorrectAttr(const std::string &pAttrName) {
+    throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
+}
+
+void AMFImporter::Throw_IncorrectAttrValue(const std::string &pAttrName) {
+    throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
+}
+
+void AMFImporter::Throw_MoreThanOnceDefined(const std::string &pNodeType, const std::string &pDescription) {
+    throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
+}
+
+void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
+    throw DeadlyImportError("Not found node with name \"" + pID + "\".");
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************* Functions: XML set ************************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::XML_CheckNode_MustHaveChildren() {
+    if (mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
+}
+
+void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string &pParentNodeName) {
+    static const size_t Uns_Skip_Len = 3;
+    const char *Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
+
+    static bool skipped_before[Uns_Skip_Len] = { false, false, false };
+
+    std::string nn(mReader->getNodeName());
+    bool found = false;
+    bool close_found = false;
+    size_t sk_idx;
+
+    for (sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++) {
+        if (nn != Uns_Skip[sk_idx]) continue;
+
+        found = true;
+        if (mReader->isEmptyElement()) {
+            close_found = true;
+
+            goto casu_cres;
+        }
+
+        while (mReader->read()) {
+            if ((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName())) {
+                close_found = true;
+
+                goto casu_cres;
+            }
+        }
+    } // for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
+
+casu_cres:
+
+    if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
+    if (!close_found) Throw_CloseNotFound(nn);
+
+    if (!skipped_before[sk_idx]) {
+        skipped_before[sk_idx] = true;
+        ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
+    }
+}
+
+bool AMFImporter::XML_SearchNode(const std::string &pNodeName) {
+    while (mReader->read()) {
+        if ((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
+    }
+
+    return false;
+}
+
+bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx) {
+    std::string val(mReader->getAttributeValue(pAttrIdx));
+
+    if ((val == "false") || (val == "0"))
+        return false;
+    else if ((val == "true") || (val == "1"))
+        return true;
+    else
+        throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
+}
+
+float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx) {
+    std::string val;
+    float tvalf;
+
+    ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
+    fast_atoreal_move(val.c_str(), tvalf, false);
+
+    return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx) {
+    return strtoul10(mReader->getAttributeValue(pAttrIdx));
+}
+
+float AMFImporter::XML_ReadNode_GetVal_AsFloat() {
+    std::string val;
+    float tvalf;
+
+    if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
+    if (mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
+
+    ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
+    fast_atoreal_move(val.c_str(), tvalf, false);
+
+    return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32() {
+    if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
+    if (mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
+
+    return strtoul10(mReader->getNodeData());
+}
+
+void AMFImporter::XML_ReadNode_GetVal_AsString(std::string &pValue) {
+    if (!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
+    if (mReader->getNodeType() != irr::io::EXN_TEXT)
+        throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
+
+    pValue = mReader->getNodeData();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: parse set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement *pNode) {
+    mNodeElement_Cur->Child.push_back(pNode); // add new element to current element child list.
+    mNodeElement_Cur = pNode; // switch current element to new one.
+}
+
+void AMFImporter::ParseHelper_Node_Exit() {
+    // check if we can walk up.
+    if (mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
+}
+
+void AMFImporter::ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString) {
+    size_t instr_len;
+
+    pOutString.clear();
+    instr_len = strlen(pInStr);
+    if (!instr_len) return;
+
+    pOutString.reserve(instr_len * 3 / 2);
+    // check and correct floats in format ".x". Must be "x.y".
+    if (pInStr[0] == '.') pOutString.push_back('0');
+
+    pOutString.push_back(pInStr[0]);
+    for (size_t ci = 1; ci < instr_len; ci++) {
+        if ((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t'))) {
+            pOutString.push_back('0');
+            pOutString.push_back('.');
+        } else {
+            pOutString.push_back(pInStr[ci]);
+        }
+    }
+}
+
+static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) {
+    return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
+}
+
+void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector<uint8_t> &pOutputData) const {
+    // With help from
+    // René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
+    const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    uint8_t tidx = 0;
+    uint8_t arr4[4], arr3[3];
+
+    // check input data
+    if (pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
+    // prepare output place
+    pOutputData.clear();
+    pOutputData.reserve(pInputBase64.size() / 4 * 3);
+
+    for (size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--) {
+        if (ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) {
+            arr4[tidx++] = pInputBase64[in_idx++];
+            if (tidx == 4) {
+                for (tidx = 0; tidx < 4; tidx++)
+                    arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
+
+                arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+                arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+                arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+                for (tidx = 0; tidx < 3; tidx++)
+                    pOutputData.push_back(arr3[tidx]);
+
+                tidx = 0;
+            } // if(tidx == 4)
+        } // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
+        else {
+            in_idx++;
+        } // if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
+    }
+
+    if (tidx) {
+        for (uint8_t i = tidx; i < 4; i++)
+            arr4[i] = 0;
+        for (uint8_t i = 0; i < 4; i++)
+            arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
+
+        arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+        arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+        arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+        for (uint8_t i = 0; i < (tidx - 1); i++)
+            pOutputData.push_back(arr3[i]);
+    }
+}
+
+void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) {
+    irr::io::IrrXMLReader *OldReader = mReader; // store current XMLreader.
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
+
+    // Check whether we can read from the file
+    if (file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
+
+    // generate a XML reader for it
+    std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
+    mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+    if (!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
+    //
+    // start reading
+    // search for root tag <amf>
+    if (XML_SearchNode("amf"))
+        ParseNode_Root();
+    else
+        throw DeadlyImportError("Root node \"amf\" not found.");
+
+    delete mReader;
+    // restore old XMLreader
+    mReader = OldReader;
+}
+
+// <amf
+// unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
+// version="" - Version of file format.
+// >
+// </amf>
+// Root XML element.
+// Multi elements - No.
+void AMFImporter::ParseNode_Root() {
+    std::string unit, version;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <amf>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
+    MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND_WSKIP;
+
+    // Check attributes
+    if (!mUnit.empty()) {
+        if ((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
+    }
+
+    // create root node element.
+    ne = new CAMFImporter_NodeElement_Root(nullptr);
+    mNodeElement_Cur = ne; // set first "current" element
+    // and assign attribute's values
+    ((CAMFImporter_NodeElement_Root *)ne)->Unit = unit;
+    ((CAMFImporter_NodeElement_Root *)ne)->Version = version;
+
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        MACRO_NODECHECK_LOOPBEGIN("amf");
+        if (XML_CheckNode_NameEqual("object")) {
+            ParseNode_Object();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("material")) {
+            ParseNode_Material();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("texture")) {
+            ParseNode_Texture();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("constellation")) {
+            ParseNode_Constellation();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("metadata")) {
+            ParseNode_Metadata();
+            continue;
+        }
+        MACRO_NODECHECK_LOOPEND("amf");
+        mNodeElement_Cur = ne; // force restore "current" element
+    } // if(!mReader->isEmptyElement())
+
+    mNodeElement_List.push_back(ne); // add to node element list because its a new object in graph.
+}
+
+// <constellation
+// id="" - The Object ID of the new constellation being defined.
+// >
+// </constellation>
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - <amf>.
+void AMFImporter::ParseNode_Constellation() {
+    std::string id;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <constellation>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+
+    // create and if needed - define new grouping object.
+    ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
+
+    CAMFImporter_NodeElement_Constellation &als = *((CAMFImporter_NodeElement_Constellation *)ne); // alias for convenience
+
+    if (!id.empty()) als.ID = id;
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        ParseHelper_Node_Enter(ne);
+        MACRO_NODECHECK_LOOPBEGIN("constellation");
+        if (XML_CheckNode_NameEqual("instance")) {
+            ParseNode_Instance();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("metadata")) {
+            ParseNode_Metadata();
+            continue;
+        }
+        MACRO_NODECHECK_LOOPEND("constellation");
+        ParseHelper_Node_Exit();
+    } // if(!mReader->isEmptyElement())
+    else {
+        mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    } // if(!mReader->isEmptyElement()) else
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <instance
+// objectid="" - The Object ID of the new constellation being defined.
+// >
+// </instance>
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - <amf>.
+void AMFImporter::ParseNode_Instance() {
+    std::string objectid;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <constellation>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+
+    // used object id must be defined, check that.
+    if (objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
+    // create and define new grouping object.
+    ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
+
+    CAMFImporter_NodeElement_Instance &als = *((CAMFImporter_NodeElement_Instance *)ne); // alias for convenience
+
+    als.ObjectID = objectid;
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        bool read_flag[6] = { false, false, false, false, false, false };
+
+        als.Delta.Set(0, 0, 0);
+        als.Rotation.Set(0, 0, 0);
+        ParseHelper_Node_Enter(ne);
+        MACRO_NODECHECK_LOOPBEGIN("instance");
+        MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
+        MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
+        MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
+        MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
+        MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
+        MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
+        MACRO_NODECHECK_LOOPEND("instance");
+        ParseHelper_Node_Exit();
+        // also convert degrees to radians.
+        als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
+        als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
+        als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
+    } // if(!mReader->isEmptyElement())
+    else {
+        mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    } // if(!mReader->isEmptyElement()) else
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <object
+// id="" - A unique ObjectID for the new object being defined.
+// >
+// </object>
+// An object definition.
+// Multi elements - Yes.
+// Parent element - <amf>.
+void AMFImporter::ParseNode_Object() {
+    std::string id;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // Read attributes for node <object>.
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+
+    // create and if needed - define new geometry object.
+    ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
+
+    CAMFImporter_NodeElement_Object &als = *((CAMFImporter_NodeElement_Object *)ne); // alias for convenience
+
+    if (!id.empty()) als.ID = id;
+    // Check for child nodes
+    if (!mReader->isEmptyElement()) {
+        bool col_read = false;
+
+        ParseHelper_Node_Enter(ne);
+        MACRO_NODECHECK_LOOPBEGIN("object");
+        if (XML_CheckNode_NameEqual("color")) {
+            // Check if color already defined for object.
+            if (col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <object>.");
+            // read data and set flag about it
+            ParseNode_Color();
+            col_read = true;
+
+            continue;
+        }
+
+        if (XML_CheckNode_NameEqual("mesh")) {
+            ParseNode_Mesh();
+            continue;
+        }
+        if (XML_CheckNode_NameEqual("metadata")) {
+            ParseNode_Metadata();
+            continue;
+        }
+        MACRO_NODECHECK_LOOPEND("object");
+        ParseHelper_Node_Exit();
+    } // if(!mReader->isEmptyElement())
+    else {
+        mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    } // if(!mReader->isEmptyElement()) else
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <metadata
+// type="" - The type of the attribute.
+// >
+// </metadata>
+// Specify additional information about an entity.
+// Multi elements - Yes.
+// Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
+//
+// Reserved types are:
+// "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
+// "Description" - A description of the content of the entity
+// "URL" - A link to an external resource relating to the entity
+// "Author" - Specifies the name(s) of the author(s) of the entity
+// "Company" - Specifying the company generating the entity
+// "CAD" - specifies the name of the originating CAD software and version
+// "Revision" - specifies the revision of the entity
+// "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
+// "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
+void AMFImporter::ParseNode_Metadata() {
+    std::string type, value;
+    CAMFImporter_NodeElement *ne(nullptr);
+
+    // read attribute
+    MACRO_ATTRREAD_LOOPBEG;
+    MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
+    MACRO_ATTRREAD_LOOPEND;
+    // and value of node.
+    value = mReader->getNodeData();
+    // Create node element and assign read data.
+    ne = new CAMFImporter_NodeElement_Metadata(mNodeElement_Cur);
+    ((CAMFImporter_NodeElement_Metadata *)ne)->Type = type;
+    ((CAMFImporter_NodeElement_Metadata *)ne)->Value = value;
+    mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+/*********************************************************************************************************************************************/
+/******************************************************** Functions: BaseImporter set ********************************************************/
+/*********************************************************************************************************************************************/
+
+bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const {
+    const std::string extension = GetExtension(pFile);
+
+    if (extension == "amf") {
+        return true;
+    }
+
+    if (!extension.length() || pCheckSig) {
+        const char *tokens[] = { "<amf" };
+
+        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+    }
+
+    return false;
+}
+
+void AMFImporter::GetExtensionList(std::set<std::string> &pExtensionList) {
+    pExtensionList.insert("amf");
+}
+
+const aiImporterDesc *AMFImporter::GetInfo() const {
+    return &Description;
+}
+
+void AMFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    Clear(); // delete old graph.
+    ParseFile(pFile, pIOHandler);
+    Postprocess_BuildScene(pScene);
+    // scene graph is ready, exit.
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 0 - 0
code/AMF/AMFImporter.hpp → code/AssetLib/AMF/AMFImporter.hpp


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

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

+ 1 - 1
code/AMF/AMFImporter_Macro.hpp → code/AssetLib/AMF/AMFImporter_Macro.hpp

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

+ 1 - 1
code/AMF/AMFImporter_Material.cpp → code/AssetLib/AMF/AMFImporter_Material.cpp

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

+ 1 - 1
code/AMF/AMFImporter_Node.hpp → code/AssetLib/AMF/AMFImporter_Node.hpp

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

+ 872 - 0
code/AssetLib/AMF/AMFImporter_Postprocess.cpp

@@ -0,0 +1,872 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/// \file AMFImporter_Postprocess.cpp
+/// \brief Convert built scenegraph and objects to Assimp scenegraph.
+/// \date 2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+
+#include "AMFImporter.hpp"
+
+// Header files, Assimp.
+#include <assimp/SceneCombiner.h>
+#include <assimp/StandardShapes.h>
+#include <assimp/StringUtils.h>
+
+// Header files, stdlib.
+#include <iterator>
+
+namespace Assimp {
+
+aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*pY*/, const float /*pZ*/) const {
+    aiColor4D tcol;
+
+    // Check if stored data are supported.
+    if (!Composition.empty()) {
+        throw DeadlyImportError("IME. GetColor for composition");
+    } else if (Color->Composed) {
+        throw DeadlyImportError("IME. GetColor, composed color");
+    } else {
+        tcol = Color->Color;
+    }
+
+    // Check if default color must be used
+    if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) {
+        tcol.r = 0.5f;
+        tcol.g = 0.5f;
+        tcol.b = 0.5f;
+        tcol.a = 1;
+    }
+
+    return tcol;
+}
+
+void AMFImporter::PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh &pNodeElement, std::vector<aiVector3D> &pVertexCoordinateArray,
+        std::vector<CAMFImporter_NodeElement_Color *> &pVertexColorArray) const {
+    CAMFImporter_NodeElement_Vertices *vn = nullptr;
+    size_t col_idx;
+
+    // All data stored in "vertices", search for it.
+    for (CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Vertices) vn = (CAMFImporter_NodeElement_Vertices *)ne_child;
+    }
+
+    // If "vertices" not found then no work for us.
+    if (vn == nullptr) return;
+
+    pVertexCoordinateArray.reserve(vn->Child.size()); // all coordinates stored as child and we need to reserve space for future push_back's.
+    pVertexColorArray.resize(vn->Child.size()); // colors count equal vertices count.
+    col_idx = 0;
+    // Inside vertices collect all data and place to arrays
+    for (CAMFImporter_NodeElement *vn_child : vn->Child) {
+        // vertices, colors
+        if (vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex) {
+            // by default clear color for current vertex
+            pVertexColorArray[col_idx] = nullptr;
+
+            for (CAMFImporter_NodeElement *vtx : vn_child->Child) {
+                if (vtx->Type == CAMFImporter_NodeElement::ENET_Coordinates) {
+                    pVertexCoordinateArray.push_back(((CAMFImporter_NodeElement_Coordinates *)vtx)->Coordinate);
+
+                    continue;
+                }
+
+                if (vtx->Type == CAMFImporter_NodeElement::ENET_Color) {
+                    pVertexColorArray[col_idx] = (CAMFImporter_NodeElement_Color *)vtx;
+
+                    continue;
+                }
+            } // for(CAMFImporter_NodeElement* vtx: vn_child->Child)
+
+            col_idx++;
+        } // if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
+    } // for(CAMFImporter_NodeElement* vn_child: vn->Child)
+}
+
+size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &pID_R, const std::string &pID_G, const std::string &pID_B,
+        const std::string &pID_A) {
+    size_t TextureConverted_Index;
+    std::string TextureConverted_ID;
+
+    // check input data
+    if (pID_R.empty() && pID_G.empty() && pID_B.empty() && pID_A.empty())
+        throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined.");
+
+    // Create ID
+    TextureConverted_ID = pID_R + "_" + pID_G + "_" + pID_B + "_" + pID_A;
+    // Check if texture specified by set of IDs is converted already.
+    TextureConverted_Index = 0;
+    for (const SPP_Texture &tex_convd : mTexture_Converted) {
+        if (tex_convd.ID == TextureConverted_ID) {
+            return TextureConverted_Index;
+        } else {
+            ++TextureConverted_Index;
+        }
+    }
+
+    //
+    // Converted texture not found, create it.
+    //
+    CAMFImporter_NodeElement_Texture *src_texture[4]{ nullptr };
+    std::vector<CAMFImporter_NodeElement_Texture *> src_texture_4check;
+    SPP_Texture converted_texture;
+
+    { // find all specified source textures
+        CAMFImporter_NodeElement *t_tex;
+
+        // R
+        if (!pID_R.empty()) {
+            if (!Find_NodeElement(pID_R, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R);
+
+            src_texture[0] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[0] = nullptr;
+        }
+
+        // G
+        if (!pID_G.empty()) {
+            if (!Find_NodeElement(pID_G, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_G);
+
+            src_texture[1] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[1] = nullptr;
+        }
+
+        // B
+        if (!pID_B.empty()) {
+            if (!Find_NodeElement(pID_B, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_B);
+
+            src_texture[2] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[2] = nullptr;
+        }
+
+        // A
+        if (!pID_A.empty()) {
+            if (!Find_NodeElement(pID_A, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_A);
+
+            src_texture[3] = (CAMFImporter_NodeElement_Texture *)t_tex;
+            src_texture_4check.push_back((CAMFImporter_NodeElement_Texture *)t_tex);
+        } else {
+            src_texture[3] = nullptr;
+        }
+    } // END: find all specified source textures
+
+    // check that all textures has same size
+    if (src_texture_4check.size() > 1) {
+        for (size_t i = 0, i_e = (src_texture_4check.size() - 1); i < i_e; i++) {
+            if ((src_texture_4check[i]->Width != src_texture_4check[i + 1]->Width) || (src_texture_4check[i]->Height != src_texture_4check[i + 1]->Height) ||
+                    (src_texture_4check[i]->Depth != src_texture_4check[i + 1]->Depth)) {
+                throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size.");
+            }
+        }
+    } // if(src_texture_4check.size() > 1)
+
+    // set texture attributes
+    converted_texture.Width = src_texture_4check[0]->Width;
+    converted_texture.Height = src_texture_4check[0]->Height;
+    converted_texture.Depth = src_texture_4check[0]->Depth;
+    // if one of source texture is tiled then converted texture is tiled too.
+    converted_texture.Tiled = false;
+    for (uint8_t i = 0; i < src_texture_4check.size(); i++)
+        converted_texture.Tiled |= src_texture_4check[i]->Tiled;
+
+    // Create format hint.
+    strcpy(converted_texture.FormatHint, "rgba0000"); // copy initial string.
+    if (!pID_R.empty()) converted_texture.FormatHint[4] = '8';
+    if (!pID_G.empty()) converted_texture.FormatHint[5] = '8';
+    if (!pID_B.empty()) converted_texture.FormatHint[6] = '8';
+    if (!pID_A.empty()) converted_texture.FormatHint[7] = '8';
+
+    //
+    // Сopy data of textures.
+    //
+    size_t tex_size = 0;
+    size_t step = 0;
+    size_t off_g = 0;
+    size_t off_b = 0;
+
+    // Calculate size of the target array and rule how data will be copied.
+    if (!pID_R.empty() && nullptr != src_texture[0]) {
+        tex_size += src_texture[0]->Data.size();
+        step++, off_g++, off_b++;
+    }
+    if (!pID_G.empty() && nullptr != src_texture[1]) {
+        tex_size += src_texture[1]->Data.size();
+        step++, off_b++;
+    }
+    if (!pID_B.empty() && nullptr != src_texture[2]) {
+        tex_size += src_texture[2]->Data.size();
+        step++;
+    }
+    if (!pID_A.empty() && nullptr != src_texture[3]) {
+        tex_size += src_texture[3]->Data.size();
+        step++;
+    }
+
+    // Create target array.
+    converted_texture.Data = new uint8_t[tex_size];
+    // And copy data
+    auto CopyTextureData = [&](const std::string &pID, const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void {
+        if (!pID.empty()) {
+            for (size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++) {
+                CAMFImporter_NodeElement_Texture *tex = src_texture[pSrcTexNum];
+                ai_assert(tex);
+                converted_texture.Data[idx_target] = tex->Data.at(idx_src);
+            }
+        }
+    }; // auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
+
+    CopyTextureData(pID_R, 0, step, 0);
+    CopyTextureData(pID_G, off_g, step, 1);
+    CopyTextureData(pID_B, off_b, step, 2);
+    CopyTextureData(pID_A, step - 1, step, 3);
+
+    // Store new converted texture ID
+    converted_texture.ID = TextureConverted_ID;
+    // Store new converted texture
+    mTexture_Converted.push_back(converted_texture);
+
+    return TextureConverted_Index;
+}
+
+void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace> &pInputList, std::list<std::list<SComplexFace>> &pOutputList_Separated) {
+    auto texmap_is_equal = [](const CAMFImporter_NodeElement_TexMap *pTexMap1, const CAMFImporter_NodeElement_TexMap *pTexMap2) -> bool {
+        if ((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true;
+        if (pTexMap1 == nullptr) return false;
+        if (pTexMap2 == nullptr) return false;
+
+        if (pTexMap1->TextureID_R != pTexMap2->TextureID_R) return false;
+        if (pTexMap1->TextureID_G != pTexMap2->TextureID_G) return false;
+        if (pTexMap1->TextureID_B != pTexMap2->TextureID_B) return false;
+        if (pTexMap1->TextureID_A != pTexMap2->TextureID_A) return false;
+
+        return true;
+    };
+
+    pOutputList_Separated.clear();
+    if (pInputList.empty()) return;
+
+    do {
+        SComplexFace face_start = pInputList.front();
+        std::list<SComplexFace> face_list_cur;
+
+        for (std::list<SComplexFace>::iterator it = pInputList.begin(), it_end = pInputList.end(); it != it_end;) {
+            if (texmap_is_equal(face_start.TexMap, it->TexMap)) {
+                auto it_old = it;
+
+                ++it;
+                face_list_cur.push_back(*it_old);
+                pInputList.erase(it_old);
+            } else {
+                ++it;
+            }
+        }
+
+        if (!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur);
+
+    } while (!pInputList.empty());
+}
+
+void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata *> &metadataList, aiNode &sceneNode) const {
+    if (!metadataList.empty()) {
+        if (sceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
+
+        // copy collected metadata to output node.
+        sceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metadataList.size()));
+        size_t meta_idx(0);
+
+        for (const CAMFImporter_NodeElement_Metadata &metadata : metadataList) {
+            sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata.Type, aiString(metadata.Value));
+        }
+    } // if(!metadataList.empty())
+}
+
+void AMFImporter::Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object &pNodeElement, std::list<aiMesh *> &pMeshList, aiNode **pSceneNode) {
+    CAMFImporter_NodeElement_Color *object_color = nullptr;
+
+    // create new aiNode and set name as <object> has.
+    *pSceneNode = new aiNode;
+    (*pSceneNode)->mName = pNodeElement.ID;
+    // read mesh and color
+    for (const CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
+        std::vector<aiVector3D> vertex_arr;
+        std::vector<CAMFImporter_NodeElement_Color *> color_arr;
+
+        // color for object
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Color) object_color = (CAMFImporter_NodeElement_Color *)ne_child;
+
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Mesh) {
+            // Create arrays from children of mesh: vertices.
+            PostprocessHelper_CreateMeshDataArray(*((CAMFImporter_NodeElement_Mesh *)ne_child), vertex_arr, color_arr);
+            // Use this arrays as a source when creating every aiMesh
+            Postprocess_BuildMeshSet(*((CAMFImporter_NodeElement_Mesh *)ne_child), vertex_arr, color_arr, object_color, pMeshList, **pSceneNode);
+        }
+    } // for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
+}
+
+void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray,
+        const std::vector<CAMFImporter_NodeElement_Color *> &pVertexColorArray,
+        const CAMFImporter_NodeElement_Color *pObjectColor, std::list<aiMesh *> &pMeshList, aiNode &pSceneNode) {
+    std::list<unsigned int> mesh_idx;
+
+    // all data stored in "volume", search for it.
+    for (const CAMFImporter_NodeElement *ne_child : pNodeElement.Child) {
+        const CAMFImporter_NodeElement_Color *ne_volume_color = nullptr;
+        const SPP_Material *cur_mat = nullptr;
+
+        if (ne_child->Type == CAMFImporter_NodeElement::ENET_Volume) {
+            /******************* Get faces *******************/
+            const CAMFImporter_NodeElement_Volume *ne_volume = reinterpret_cast<const CAMFImporter_NodeElement_Volume *>(ne_child);
+
+            std::list<SComplexFace> complex_faces_list; // List of the faces of the volume.
+            std::list<std::list<SComplexFace>> complex_faces_toplist; // List of the face list for every mesh.
+
+            // check if volume use material
+            if (!ne_volume->MaterialID.empty()) {
+                if (!Find_ConvertedMaterial(ne_volume->MaterialID, &cur_mat)) Throw_ID_NotFound(ne_volume->MaterialID);
+            }
+
+            // inside "volume" collect all data and place to arrays or create new objects
+            for (const CAMFImporter_NodeElement *ne_volume_child : ne_volume->Child) {
+                // color for volume
+                if (ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Color) {
+                    ne_volume_color = reinterpret_cast<const CAMFImporter_NodeElement_Color *>(ne_volume_child);
+                } else if (ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Triangle) // triangles, triangles colors
+                {
+                    const CAMFImporter_NodeElement_Triangle &tri_al = *reinterpret_cast<const CAMFImporter_NodeElement_Triangle *>(ne_volume_child);
+
+                    SComplexFace complex_face;
+
+                    // initialize pointers
+                    complex_face.Color = nullptr;
+                    complex_face.TexMap = nullptr;
+                    // get data from triangle children: color, texture coordinates.
+                    if (tri_al.Child.size()) {
+                        for (const CAMFImporter_NodeElement *ne_triangle_child : tri_al.Child) {
+                            if (ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_Color)
+                                complex_face.Color = reinterpret_cast<const CAMFImporter_NodeElement_Color *>(ne_triangle_child);
+                            else if (ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_TexMap)
+                                complex_face.TexMap = reinterpret_cast<const CAMFImporter_NodeElement_TexMap *>(ne_triangle_child);
+                        }
+                    } // if(tri_al.Child.size())
+
+                    // create new face and store it.
+                    complex_face.Face.mNumIndices = 3;
+                    complex_face.Face.mIndices = new unsigned int[3];
+                    complex_face.Face.mIndices[0] = static_cast<unsigned int>(tri_al.V[0]);
+                    complex_face.Face.mIndices[1] = static_cast<unsigned int>(tri_al.V[1]);
+                    complex_face.Face.mIndices[2] = static_cast<unsigned int>(tri_al.V[2]);
+                    complex_faces_list.push_back(complex_face);
+                }
+            } // for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
+
+            /**** Split faces list: one list per mesh ****/
+            PostprocessHelper_SplitFacesByTextureID(complex_faces_list, complex_faces_toplist);
+
+            /***** Create mesh for every faces list ******/
+            for (std::list<SComplexFace> &face_list_cur : complex_faces_toplist) {
+                auto VertexIndex_GetMinimal = [](const std::list<SComplexFace> &pFaceList, const size_t *pBiggerThan) -> size_t {
+                    size_t rv = 0;
+
+                    if (pBiggerThan != nullptr) {
+                        bool found = false;
+
+                        for (const SComplexFace &face : pFaceList) {
+                            for (size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++) {
+                                if (face.Face.mIndices[idx_vert] > *pBiggerThan) {
+                                    rv = face.Face.mIndices[idx_vert];
+                                    found = true;
+
+                                    break;
+                                }
+                            }
+
+                            if (found) break;
+                        }
+
+                        if (!found) return *pBiggerThan;
+                    } else {
+                        rv = pFaceList.front().Face.mIndices[0];
+                    } // if(pBiggerThan != nullptr) else
+
+                    for (const SComplexFace &face : pFaceList) {
+                        for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) {
+                            if (face.Face.mIndices[vi] < rv) {
+                                if (pBiggerThan != nullptr) {
+                                    if (face.Face.mIndices[vi] > *pBiggerThan) rv = face.Face.mIndices[vi];
+                                } else {
+                                    rv = face.Face.mIndices[vi];
+                                }
+                            }
+                        }
+                    } // for(const SComplexFace& face: pFaceList)
+
+                    return rv;
+                }; // auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
+
+                auto VertexIndex_Replace = [](std::list<SComplexFace> &pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void {
+                    for (const SComplexFace &face : pFaceList) {
+                        for (size_t vi = 0; vi < face.Face.mNumIndices; vi++) {
+                            if (face.Face.mIndices[vi] == pIdx_From) face.Face.mIndices[vi] = static_cast<unsigned int>(pIdx_To);
+                        }
+                    }
+                }; // auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
+
+                auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D {
+                    // Color priorities(In descending order):
+                    // 1. triangle color;
+                    // 2. vertex color;
+                    // 3. volume color;
+                    // 4. object color;
+                    // 5. material;
+                    // 6. default - invisible coat.
+                    //
+                    // Fill vertices colors in color priority list above that's points from 1 to 6.
+                    if ((pIdx < pVertexColorArray.size()) && (pVertexColorArray[pIdx] != nullptr)) // check for vertex color
+                    {
+                        if (pVertexColorArray[pIdx]->Composed)
+                            throw DeadlyImportError("IME: vertex color composed");
+                        else
+                            return pVertexColorArray[pIdx]->Color;
+                    } else if (ne_volume_color != nullptr) // check for volume color
+                    {
+                        if (ne_volume_color->Composed)
+                            throw DeadlyImportError("IME: volume color composed");
+                        else
+                            return ne_volume_color->Color;
+                    } else if (pObjectColor != nullptr) // check for object color
+                    {
+                        if (pObjectColor->Composed)
+                            throw DeadlyImportError("IME: object color composed");
+                        else
+                            return pObjectColor->Color;
+                    } else if (cur_mat != nullptr) // check for material
+                    {
+                        return cur_mat->GetColor(pVertexCoordinateArray.at(pIdx).x, pVertexCoordinateArray.at(pIdx).y, pVertexCoordinateArray.at(pIdx).z);
+                    } else // set default color.
+                    {
+                        return { 0, 0, 0, 0 };
+                    } // if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else
+                }; // auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
+
+                aiMesh *tmesh = new aiMesh;
+
+                tmesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // Only triangles is supported by AMF.
+                //
+                // set geometry and colors (vertices)
+                //
+                // copy faces/triangles
+                tmesh->mNumFaces = static_cast<unsigned int>(face_list_cur.size());
+                tmesh->mFaces = new aiFace[tmesh->mNumFaces];
+
+                // Create vertices list and optimize indices. Optimisation mean following.In AMF all volumes use one big list of vertices. And one volume
+                // can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10.
+                // Do you need all this thousands of garbage? Of course no. So, optimisation step transformate sparse indices set to continuous.
+                size_t VertexCount_Max = tmesh->mNumFaces * 3; // 3 - triangles.
+                std::vector<aiVector3D> vert_arr, texcoord_arr;
+                std::vector<aiColor4D> col_arr;
+
+                vert_arr.reserve(VertexCount_Max * 2); // "* 2" - see below TODO.
+                col_arr.reserve(VertexCount_Max * 2);
+
+                { // fill arrays
+                    size_t vert_idx_from, vert_idx_to;
+
+                    // first iteration.
+                    vert_idx_to = 0;
+                    vert_idx_from = VertexIndex_GetMinimal(face_list_cur, nullptr);
+                    vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
+                    col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
+                    if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
+
+                    // rest iterations
+                    do {
+                        vert_idx_from = VertexIndex_GetMinimal(face_list_cur, &vert_idx_to);
+                        if (vert_idx_from == vert_idx_to) break; // all indices are transferred,
+
+                        vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
+                        col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
+                        vert_idx_to++;
+                        if (vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
+
+                    } while (true);
+                } // fill arrays. END.
+
+                //
+                // check if triangle colors are used and create additional faces if needed.
+                //
+                for (const SComplexFace &face_cur : face_list_cur) {
+                    if (face_cur.Color != nullptr) {
+                        aiColor4D face_color;
+                        size_t vert_idx_new = vert_arr.size();
+
+                        if (face_cur.Color->Composed)
+                            throw DeadlyImportError("IME: face color composed");
+                        else
+                            face_color = face_cur.Color->Color;
+
+                        for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) {
+                            vert_arr.push_back(vert_arr.at(face_cur.Face.mIndices[idx_ind]));
+                            col_arr.push_back(face_color);
+                            face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(vert_idx_new++);
+                        }
+                    } // if(face_cur.Color != nullptr)
+                } // for(const SComplexFace& face_cur: face_list_cur)
+
+                //
+                // if texture is used then copy texture coordinates too.
+                //
+                if (face_list_cur.front().TexMap != nullptr) {
+                    size_t idx_vert_new = vert_arr.size();
+                    ///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for
+                    /// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about
+                    /// optimisation.
+                    bool *idx_vert_used;
+
+                    idx_vert_used = new bool[VertexCount_Max * 2];
+                    for (size_t i = 0, i_e = VertexCount_Max * 2; i < i_e; i++)
+                        idx_vert_used[i] = false;
+
+                    // This ID's will be used when set materials ID in scene.
+                    tmesh->mMaterialIndex = static_cast<unsigned int>(PostprocessHelper_GetTextureID_Or_Create(face_list_cur.front().TexMap->TextureID_R,
+                            face_list_cur.front().TexMap->TextureID_G,
+                            face_list_cur.front().TexMap->TextureID_B,
+                            face_list_cur.front().TexMap->TextureID_A));
+                    texcoord_arr.resize(VertexCount_Max * 2);
+                    for (const SComplexFace &face_cur : face_list_cur) {
+                        for (size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++) {
+                            const size_t idx_vert = face_cur.Face.mIndices[idx_ind];
+
+                            if (!idx_vert_used[idx_vert]) {
+                                texcoord_arr.at(idx_vert) = face_cur.TexMap->TextureCoordinate[idx_ind];
+                                idx_vert_used[idx_vert] = true;
+                            } else if (texcoord_arr.at(idx_vert) != face_cur.TexMap->TextureCoordinate[idx_ind]) {
+                                // in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture
+                                // coordinates.
+                                vert_arr.push_back(vert_arr.at(idx_vert));
+                                col_arr.push_back(col_arr.at(idx_vert));
+                                texcoord_arr.at(idx_vert_new) = face_cur.TexMap->TextureCoordinate[idx_ind];
+                                face_cur.Face.mIndices[idx_ind] = static_cast<unsigned int>(idx_vert_new++);
+                            }
+                        } // for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
+                    } // for(const SComplexFace& face_cur: face_list_cur)
+
+                    delete[] idx_vert_used;
+                    // shrink array
+                    texcoord_arr.resize(idx_vert_new);
+                } // if(face_list_cur.front().TexMap != nullptr)
+
+                //
+                // copy collected data to mesh
+                //
+                tmesh->mNumVertices = static_cast<unsigned int>(vert_arr.size());
+                tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
+                tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
+
+                memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
+                memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D));
+                if (texcoord_arr.size() > 0) {
+                    tmesh->mTextureCoords[0] = new aiVector3D[tmesh->mNumVertices];
+                    memcpy(tmesh->mTextureCoords[0], texcoord_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
+                    tmesh->mNumUVComponents[0] = 2; // U and V stored in "x", "y" of aiVector3D.
+                }
+
+                size_t idx_face = 0;
+                for (const SComplexFace &face_cur : face_list_cur)
+                    tmesh->mFaces[idx_face++] = face_cur.Face;
+
+                // store new aiMesh
+                mesh_idx.push_back(static_cast<unsigned int>(pMeshList.size()));
+                pMeshList.push_back(tmesh);
+            } // for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
+        } // if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
+    } // for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
+
+    // if meshes was created then assign new indices with current aiNode
+    if (!mesh_idx.empty()) {
+        std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
+
+        pSceneNode.mNumMeshes = static_cast<unsigned int>(mesh_idx.size());
+        pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
+        for (size_t i = 0; i < pSceneNode.mNumMeshes; i++)
+            pSceneNode.mMeshes[i] = *mit++;
+    } // if(mesh_idx.size() > 0)
+}
+
+void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material &pMaterial) {
+    SPP_Material new_mat;
+
+    new_mat.ID = pMaterial.ID;
+    for (const CAMFImporter_NodeElement *mat_child : pMaterial.Child) {
+        if (mat_child->Type == CAMFImporter_NodeElement::ENET_Color) {
+            new_mat.Color = (CAMFImporter_NodeElement_Color *)mat_child;
+        } else if (mat_child->Type == CAMFImporter_NodeElement::ENET_Metadata) {
+            new_mat.Metadata.push_back((CAMFImporter_NodeElement_Metadata *)mat_child);
+        }
+    } // for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
+
+    // place converted material to special list
+    mMaterial_Converted.push_back(new_mat);
+}
+
+void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation &pConstellation, std::list<aiNode *> &pNodeList) const {
+    aiNode *con_node;
+    std::list<aiNode *> ch_node;
+
+    // We will build next hierarchy:
+    // aiNode as parent (<constellation>) for set of nodes as a children
+    //  |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
+    //  ...
+    //  \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
+    con_node = new aiNode;
+    con_node->mName = pConstellation.ID;
+    // Walk through children and search for instances of another objects, constellations.
+    for (const CAMFImporter_NodeElement *ne : pConstellation.Child) {
+        aiMatrix4x4 tmat;
+        aiNode *t_node;
+        aiNode *found_node;
+
+        if (ne->Type == CAMFImporter_NodeElement::ENET_Metadata) continue;
+        if (ne->Type != CAMFImporter_NodeElement::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
+
+        // create alias for conveniance
+        CAMFImporter_NodeElement_Instance &als = *((CAMFImporter_NodeElement_Instance *)ne);
+        // find referenced object
+        if (!Find_ConvertedNode(als.ObjectID, pNodeList, &found_node)) Throw_ID_NotFound(als.ObjectID);
+
+        // create node for applying transformation
+        t_node = new aiNode;
+        t_node->mParent = con_node;
+        // apply transformation
+        aiMatrix4x4::Translation(als.Delta, tmat), t_node->mTransformation *= tmat;
+        aiMatrix4x4::RotationX(als.Rotation.x, tmat), t_node->mTransformation *= tmat;
+        aiMatrix4x4::RotationY(als.Rotation.y, tmat), t_node->mTransformation *= tmat;
+        aiMatrix4x4::RotationZ(als.Rotation.z, tmat), t_node->mTransformation *= tmat;
+        // create array for one child node
+        t_node->mNumChildren = 1;
+        t_node->mChildren = new aiNode *[t_node->mNumChildren];
+        SceneCombiner::Copy(&t_node->mChildren[0], found_node);
+        t_node->mChildren[0]->mParent = t_node;
+        ch_node.push_back(t_node);
+    } // for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
+
+    // copy found aiNode's as children
+    if (ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>.");
+
+    size_t ch_idx = 0;
+
+    con_node->mNumChildren = static_cast<unsigned int>(ch_node.size());
+    con_node->mChildren = new aiNode *[con_node->mNumChildren];
+    for (aiNode *node : ch_node)
+        con_node->mChildren[ch_idx++] = node;
+
+    // and place "root" of <constellation> node to node list
+    pNodeList.push_back(con_node);
+}
+
+void AMFImporter::Postprocess_BuildScene(aiScene *pScene) {
+    std::list<aiNode *> node_list;
+    std::list<aiMesh *> mesh_list;
+    std::list<CAMFImporter_NodeElement_Metadata *> meta_list;
+
+    //
+    // Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
+    // For building aiScene we are must to do few steps:
+    // at first creating root node for aiScene.
+    pScene->mRootNode = new aiNode;
+    pScene->mRootNode->mParent = nullptr;
+    pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
+    // search for root(<amf>) element
+    CAMFImporter_NodeElement *root_el = nullptr;
+
+    for (CAMFImporter_NodeElement *ne : mNodeElement_List) {
+        if (ne->Type != CAMFImporter_NodeElement::ENET_Root) continue;
+
+        root_el = ne;
+
+        break;
+    } // for(const CAMFImporter_NodeElement* ne: mNodeElement_List)
+
+    // Check if root element are found.
+    if (root_el == nullptr) throw DeadlyImportError("Root(<amf>) element not found.");
+
+    // after that walk through children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>,
+    // <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read
+    // at any moment.
+    //
+    // 1. <material>
+    // 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet
+    for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Material) Postprocess_BuildMaterial(*((CAMFImporter_NodeElement_Material *)root_child));
+    }
+
+    // After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
+    //
+    // 3. <object>
+    for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Object) {
+            aiNode *tnode = nullptr;
+
+            // for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance>
+            Postprocess_BuildNodeAndObject(*((CAMFImporter_NodeElement_Object *)root_child), mesh_list, &tnode);
+            if (tnode != nullptr) node_list.push_back(tnode);
+        }
+    } // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
+
+    // And finally read rest of nodes.
+    //
+    for (const CAMFImporter_NodeElement *root_child : root_el->Child) {
+        // 4. <constellation>
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Constellation) {
+            // <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's.
+            Postprocess_BuildConstellation(*((CAMFImporter_NodeElement_Constellation *)root_child), node_list);
+        }
+
+        // 5, <metadata>
+        if (root_child->Type == CAMFImporter_NodeElement::ENET_Metadata) meta_list.push_back((CAMFImporter_NodeElement_Metadata *)root_child);
+    } // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
+
+    // at now we can add collected metadata to root node
+    Postprocess_AddMetadata(meta_list, *pScene->mRootNode);
+    //
+    // Check constellation children
+    //
+    // As said in specification:
+    // "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing."
+    // What that means? For example: if some object is used in constellation then you must show only constellation but not original object.
+    // And at this step we are checking that relations.
+nl_clean_loop:
+
+    if (node_list.size() > 1) {
+        // walk through all nodes
+        for (std::list<aiNode *>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it) {
+            // and try to find them in another top nodes.
+            std::list<aiNode *>::const_iterator next_it = nl_it;
+
+            ++next_it;
+            for (; next_it != node_list.end(); ++next_it) {
+                if ((*next_it)->FindNode((*nl_it)->mName) != nullptr) {
+                    // if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
+                    node_list.erase(nl_it);
+
+                    goto nl_clean_loop;
+                }
+            } // for(; next_it != node_list.end(); next_it++)
+        } // for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
+    }
+
+    //
+    // move created objects to aiScene
+    //
+    //
+    // Nodes
+    if (!node_list.empty()) {
+        std::list<aiNode *>::const_iterator nl_it = node_list.begin();
+
+        pScene->mRootNode->mNumChildren = static_cast<unsigned int>(node_list.size());
+        pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
+        for (size_t i = 0; i < pScene->mRootNode->mNumChildren; i++) {
+            // Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have
+            // mRootNode only as parent.
+            (*nl_it)->mParent = pScene->mRootNode;
+            pScene->mRootNode->mChildren[i] = *nl_it++;
+        }
+    } // if(node_list.size() > 0)
+
+    //
+    // Meshes
+    if (!mesh_list.empty()) {
+        std::list<aiMesh *>::const_iterator ml_it = mesh_list.begin();
+
+        pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
+        pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
+        for (size_t i = 0; i < pScene->mNumMeshes; i++)
+            pScene->mMeshes[i] = *ml_it++;
+    } // if(mesh_list.size() > 0)
+
+    //
+    // Textures
+    pScene->mNumTextures = static_cast<unsigned int>(mTexture_Converted.size());
+    if (pScene->mNumTextures > 0) {
+        size_t idx;
+
+        idx = 0;
+        pScene->mTextures = new aiTexture *[pScene->mNumTextures];
+        for (const SPP_Texture &tex_convd : mTexture_Converted) {
+            pScene->mTextures[idx] = new aiTexture;
+            pScene->mTextures[idx]->mWidth = static_cast<unsigned int>(tex_convd.Width);
+            pScene->mTextures[idx]->mHeight = static_cast<unsigned int>(tex_convd.Height);
+            pScene->mTextures[idx]->pcData = (aiTexel *)tex_convd.Data;
+            // texture format description.
+            strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint);
+            idx++;
+        } // for(const SPP_Texture& tex_convd: mTexture_Converted)
+
+        // Create materials for embedded textures.
+        idx = 0;
+        pScene->mNumMaterials = static_cast<unsigned int>(mTexture_Converted.size());
+        pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
+        for (const SPP_Texture &tex_convd : mTexture_Converted) {
+            const aiString texture_id(AI_EMBEDDED_TEXNAME_PREFIX + to_string(idx));
+            const int mode = aiTextureOp_Multiply;
+            const int repeat = tex_convd.Tiled ? 1 : 0;
+
+            pScene->mMaterials[idx] = new aiMaterial;
+            pScene->mMaterials[idx]->AddProperty(&texture_id, AI_MATKEY_TEXTURE_DIFFUSE(0));
+            pScene->mMaterials[idx]->AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
+            pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+            pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+            idx++;
+        }
+    } // if(pScene->mNumTextures > 0)
+} // END: after that walk through children of root and collect data
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 271 - 285
code/AssetLib/ASE/ASELoader.cpp


+ 0 - 0
code/ASE/ASELoader.h → code/AssetLib/ASE/ASELoader.h


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 242 - 371
code/AssetLib/ASE/ASEParser.cpp


+ 1 - 1
code/ASE/ASEParser.h → code/AssetLib/ASE/ASEParser.h

@@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/qnan.h>
 
 // ASE is quite similar to 3ds. We can reuse some structures
-#include "3DS/3DSLoader.h"
+#include "AssetLib/3DS/3DSLoader.h"
 
 namespace Assimp    {
 namespace ASE   {

+ 0 - 1
code/Assbin/AssbinExporter.cpp → code/AssetLib/Assbin/AssbinExporter.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,

+ 0 - 0
code/Assbin/AssbinExporter.h → code/AssetLib/Assbin/AssbinExporter.h


+ 29 - 30
code/Assbin/AssbinFileWriter.cpp → code/AssetLib/Assbin/AssbinFileWriter.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -54,16 +53,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/IOStream.hpp>
 
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#    include <zlib.h>
+#include <zlib.h>
 #else
-#    include "../contrib/zlib/zlib.h"
+#include "../contrib/zlib/zlib.h"
 #endif
 
 #include <time.h>
 
 #ifdef _WIN32
-#    pragma warning(push)
-#    pragma warning(disable : 4706)
+#pragma warning(push)
+#pragma warning(disable : 4706)
 #endif // _WIN32
 
 namespace Assimp {
@@ -269,7 +268,7 @@ private:
 
 public:
     AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) :
-            buffer(NULL),
+            buffer(nullptr),
             magic(magic),
             container(container),
             cur_size(0),
@@ -362,32 +361,32 @@ protected:
             Write<uint16_t>(&chunk, (uint16_t)type);
 
             switch (type) {
-                case AI_BOOL:
-                    Write<bool>(&chunk, *((bool *)value));
-                    break;
-                case AI_INT32:
-                    Write<int32_t>(&chunk, *((int32_t *)value));
-                    break;
-                case AI_UINT64:
-                    Write<uint64_t>(&chunk, *((uint64_t *)value));
-                    break;
-                case AI_FLOAT:
-                    Write<float>(&chunk, *((float *)value));
-                    break;
-                case AI_DOUBLE:
-                    Write<double>(&chunk, *((double *)value));
-                    break;
-                case AI_AISTRING:
-                    Write<aiString>(&chunk, *((aiString *)value));
-                    break;
-                case AI_AIVECTOR3D:
-                    Write<aiVector3D>(&chunk, *((aiVector3D *)value));
-                    break;
+            case AI_BOOL:
+                Write<bool>(&chunk, *((bool *)value));
+                break;
+            case AI_INT32:
+                Write<int32_t>(&chunk, *((int32_t *)value));
+                break;
+            case AI_UINT64:
+                Write<uint64_t>(&chunk, *((uint64_t *)value));
+                break;
+            case AI_FLOAT:
+                Write<float>(&chunk, *((float *)value));
+                break;
+            case AI_DOUBLE:
+                Write<double>(&chunk, *((double *)value));
+                break;
+            case AI_AISTRING:
+                Write<aiString>(&chunk, *((aiString *)value));
+                break;
+            case AI_AIVECTOR3D:
+                Write<aiVector3D>(&chunk, *((aiVector3D *)value));
+                break;
 #ifdef SWIG
                 case FORCE_32BIT:
 #endif // SWIG
-                default:
-                    break;
+            default:
+                break;
             }
         }
     }
@@ -827,7 +826,7 @@ void DumpSceneToAssbin(
     fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
 }
 #ifdef _WIN32
-#    pragma warning(pop)
+#pragma warning(pop)
 #endif // _WIN32
 
 } // end of namespace Assimp

+ 6 - 7
code/Assbin/AssbinFileWriter.h → code/AssetLib/Assbin/AssbinFileWriter.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2020, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -54,12 +53,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 
 void ASSIMP_API DumpSceneToAssbin(
-    const char* pFile,
-    const char* cmd,
-    IOSystem* pIOSystem,
-    const aiScene* pScene,
-    bool shortened,
-    bool compressed);
+        const char *pFile,
+        const char *cmd,
+        IOSystem *pIOSystem,
+        const aiScene *pScene,
+        bool shortened,
+        bool compressed);
 
 }
 

+ 25 - 25
code/Assbin/AssbinLoader.cpp → code/AssetLib/Assbin/AssbinLoader.cpp

@@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 
 // internal headers
-#include "Assbin/AssbinLoader.h"
+#include "AssetLib/Assbin/AssbinLoader.h"
 #include "Common/assbin_chunks.h"
 #include <assimp/MemoryIOWrapper.h>
 #include <assimp/anim.h>
@@ -253,32 +253,32 @@ void AssbinImporter::ReadBinaryNode(IOStream *stream, aiNode **onode, aiNode *pa
             void *data = nullptr;
 
             switch (node->mMetaData->mValues[i].mType) {
-                case AI_BOOL:
-                    data = new bool(Read<bool>(stream));
-                    break;
-                case AI_INT32:
-                    data = new int32_t(Read<int32_t>(stream));
-                    break;
-                case AI_UINT64:
-                    data = new uint64_t(Read<uint64_t>(stream));
-                    break;
-                case AI_FLOAT:
-                    data = new ai_real(Read<ai_real>(stream));
-                    break;
-                case AI_DOUBLE:
-                    data = new double(Read<double>(stream));
-                    break;
-                case AI_AISTRING:
-                    data = new aiString(Read<aiString>(stream));
-                    break;
-                case AI_AIVECTOR3D:
-                    data = new aiVector3D(Read<aiVector3D>(stream));
-                    break;
+            case AI_BOOL:
+                data = new bool(Read<bool>(stream));
+                break;
+            case AI_INT32:
+                data = new int32_t(Read<int32_t>(stream));
+                break;
+            case AI_UINT64:
+                data = new uint64_t(Read<uint64_t>(stream));
+                break;
+            case AI_FLOAT:
+                data = new ai_real(Read<ai_real>(stream));
+                break;
+            case AI_DOUBLE:
+                data = new double(Read<double>(stream));
+                break;
+            case AI_AISTRING:
+                data = new aiString(Read<aiString>(stream));
+                break;
+            case AI_AIVECTOR3D:
+                data = new aiVector3D(Read<aiVector3D>(stream));
+                break;
 #ifndef SWIG
-                case FORCE_32BIT:
+            case FORCE_32BIT:
 #endif // SWIG
-                default:
-                    break;
+            default:
+                break;
             }
 
             node->mMetaData->mValues[i].mData = data;

+ 0 - 0
code/Assbin/AssbinLoader.h → code/AssetLib/Assbin/AssbinLoader.h


+ 0 - 0
code/Assjson/cencode.c → code/AssetLib/Assjson/cencode.c


+ 0 - 0
code/Assjson/cencode.h → code/AssetLib/Assjson/cencode.h


+ 83 - 94
code/Assjson/json_exporter.cpp → code/AssetLib/Assjson/json_exporter.cpp

@@ -9,30 +9,31 @@ Licensed under a 3-clause BSD license. See the LICENSE file for more information
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
 
-#include <assimp/Importer.hpp>
+#include <assimp/scene.h>
 #include <assimp/Exporter.hpp>
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
-#include <assimp/scene.h>
+#include <assimp/Importer.hpp>
+#include <assimp/Exceptional.h>
 
-#include <sstream>
-#include <limits>
 #include <cassert>
+#include <limits>
 #include <memory>
+#include <sstream>
 
 #define CURRENT_FORMAT_VERSION 100
 
-// grab scoped_ptr from assimp to avoid a dependency on boost. 
+// grab scoped_ptr from assimp to avoid a dependency on boost.
 //#include <assimp/../../code/BoostWorkaround/boost/scoped_ptr.hpp>
 
 #include "mesh_splitter.h"
 
 extern "C" {
-    #include "cencode.h"
+#include "cencode.h"
 }
 namespace Assimp {
 
-void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
+void ExportAssimp2Json(const char *, Assimp::IOSystem *, const aiScene *, const Assimp::ExportProperties *);
 
 // small utility class to simplify serializing the aiScene to Json
 class JSONWriter {
@@ -42,10 +43,8 @@ public:
         Flag_WriteSpecialFloats = 0x2,
     };
 
-    JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u)
-    : out(out)
-    , first()
-    , flags(flags) {
+    JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) :
+            out(out), first(), flags(flags) {
         // make sure that all formatting happens using the standard, C locale and not the user's current locale
         buff.imbue(std::locale("C"));
     }
@@ -68,30 +67,30 @@ public:
         indent.erase(indent.end() - 1);
     }
 
-    void Key(const std::string& name) {
+    void Key(const std::string &name) {
         AddIndentation();
         Delimit();
         buff << '\"' + name + "\": ";
     }
 
-    template<typename Literal>
-    void Element(const Literal& name) {
+    template <typename Literal>
+    void Element(const Literal &name) {
         AddIndentation();
         Delimit();
 
         LiteralToString(buff, name) << '\n';
     }
 
-    template<typename Literal>
-    void SimpleValue(const Literal& s) {
+    template <typename Literal>
+    void SimpleValue(const Literal &s) {
         LiteralToString(buff, s) << '\n';
     }
 
-    void SimpleValue(const void* buffer, size_t len) {
+    void SimpleValue(const void *buffer, size_t len) {
         base64_encodestate s;
         base64_init_encodestate(&s);
 
-        char* const cur_out = new char[std::max(len * 2, static_cast<size_t>(16u))];
+        char *const cur_out = new char[std::max(len * 2, static_cast<size_t>(16u))];
         const int n = base64_encode_block(reinterpret_cast<const char *>(buffer), static_cast<int>(len), cur_out, &s);
         cur_out[n + base64_encode_blockend(cur_out + n, &s)] = '\0';
 
@@ -156,21 +155,20 @@ public:
     void Delimit() {
         if (!first) {
             buff << ',';
-        }
-        else {
+        } else {
             buff << ' ';
             first = false;
         }
     }
 
 private:
-    template<typename Literal>
-    std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) {
+    template <typename Literal>
+    std::stringstream &LiteralToString(std::stringstream &stream, const Literal &s) {
         stream << s;
         return stream;
     }
 
-    std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) {
+    std::stringstream &LiteralToString(std::stringstream &stream, const aiString &s) {
         std::string t;
 
         // escape backslashes and single quotes, both would render the JSON invalid if left as is
@@ -189,10 +187,10 @@ private:
         return stream;
     }
 
-    std::stringstream& LiteralToString(std::stringstream& stream, float f) {
+    std::stringstream &LiteralToString(std::stringstream &stream, float f) {
         if (!std::numeric_limits<float>::is_iec559) {
             // on a non IEEE-754 platform, we make no assumptions about the representation or existence
-            // of special floating-point numbers. 
+            // of special floating-point numbers.
             stream << f;
             return stream;
         }
@@ -228,7 +226,7 @@ private:
     }
 
 private:
-    Assimp::IOStream& out;
+    Assimp::IOStream &out;
     std::string indent, newline;
     std::stringstream buff;
     bool first;
@@ -236,7 +234,7 @@ private:
     unsigned int flags;
 };
 
-void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiVector3D &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     out.Element(ai.x);
     out.Element(ai.y);
@@ -244,7 +242,7 @@ void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiQuaternion &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     out.Element(ai.w);
     out.Element(ai.x);
@@ -253,7 +251,7 @@ void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiColor3D &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     out.Element(ai.r);
     out.Element(ai.g);
@@ -261,7 +259,7 @@ void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiMatrix4x4 &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     for (unsigned int x = 0; x < 4; ++x) {
         for (unsigned int y = 0; y < 4; ++y) {
@@ -271,7 +269,7 @@ void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiBone &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -292,7 +290,7 @@ void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiFace &ai, bool is_elem = true) {
     out.StartArray(is_elem);
     for (unsigned int i = 0; i < ai.mNumIndices; ++i) {
         out.Element(ai.mIndices[i]);
@@ -300,7 +298,7 @@ void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
     out.EndArray();
 }
 
-void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiMesh &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -411,7 +409,7 @@ void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiNode &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -441,13 +439,13 @@ void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiMaterial &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("properties");
     out.StartArray();
     for (unsigned int i = 0; i < ai.mNumProperties; ++i) {
-        const aiMaterialProperty* const prop = ai.mProperties[i];
+        const aiMaterialProperty *const prop = ai.mProperties[i];
         out.StartObj(true);
         out.Key("key");
         out.SimpleValue(prop->mKey);
@@ -461,46 +459,41 @@ void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
 
         out.Key("value");
         switch (prop->mType) {
-            case aiPTI_Float:
-                if (prop->mDataLength / sizeof(float) > 1) {
-                    out.StartArray();
-                    for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(float); ++ii) {
-                        out.Element(reinterpret_cast<float*>(prop->mData)[ii]);
-                    }
-                    out.EndArray();
-                }
-                else {
-                    out.SimpleValue(*reinterpret_cast<float*>(prop->mData));
-                }
-                break;
-
-            case aiPTI_Integer:
-                if (prop->mDataLength / sizeof(int) > 1) {
-                    out.StartArray();
-                    for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(int); ++ii) {
-                        out.Element(reinterpret_cast<int*>(prop->mData)[ii]);
-                    }
-                    out.EndArray();
-                } else {
-                    out.SimpleValue(*reinterpret_cast<int*>(prop->mData));
+        case aiPTI_Float:
+            if (prop->mDataLength / sizeof(float) > 1) {
+                out.StartArray();
+                for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(float); ++ii) {
+                    out.Element(reinterpret_cast<float *>(prop->mData)[ii]);
                 }
-                break;
+                out.EndArray();
+            } else {
+                out.SimpleValue(*reinterpret_cast<float *>(prop->mData));
+            }
+            break;
 
-            case aiPTI_String:
-                {
-                    aiString s;
-                    aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
-                    out.SimpleValue(s);
-                }
-                break;
-            case aiPTI_Buffer:
-                {
-                    // binary data is written as series of hex-encoded octets
-                    out.SimpleValue(prop->mData, prop->mDataLength);
+        case aiPTI_Integer:
+            if (prop->mDataLength / sizeof(int) > 1) {
+                out.StartArray();
+                for (unsigned int ii = 0; ii < prop->mDataLength / sizeof(int); ++ii) {
+                    out.Element(reinterpret_cast<int *>(prop->mData)[ii]);
                 }
-                break;
-            default:
-                assert(false);
+                out.EndArray();
+            } else {
+                out.SimpleValue(*reinterpret_cast<int *>(prop->mData));
+            }
+            break;
+
+        case aiPTI_String: {
+            aiString s;
+            aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
+            out.SimpleValue(s);
+        } break;
+        case aiPTI_Buffer: {
+            // binary data is written as series of hex-encoded octets
+            out.SimpleValue(prop->mData, prop->mDataLength);
+        } break;
+        default:
+            assert(false);
         }
 
         out.EndObj();
@@ -510,7 +503,7 @@ void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiTexture &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("width");
@@ -525,13 +518,12 @@ void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
     out.Key("data");
     if (!ai.mHeight) {
         out.SimpleValue(ai.pcData, ai.mWidth);
-    }
-    else {
+    } else {
         out.StartArray();
         for (unsigned int y = 0; y < ai.mHeight; ++y) {
             out.StartArray(true);
             for (unsigned int x = 0; x < ai.mWidth; ++x) {
-                const aiTexel& tx = ai.pcData[y*ai.mWidth + x];
+                const aiTexel &tx = ai.pcData[y * ai.mWidth + x];
                 out.StartArray(true);
                 out.Element(static_cast<unsigned int>(tx.r));
                 out.Element(static_cast<unsigned int>(tx.g));
@@ -547,7 +539,7 @@ void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiLight &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -585,7 +577,6 @@ void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
     if (ai.mType != aiLightSource_POINT) {
         out.Key("direction");
         Write(out, ai.mDirection, false);
-
     }
 
     if (ai.mType != aiLightSource_DIRECTIONAL) {
@@ -596,7 +587,7 @@ void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiNodeAnim &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -612,7 +603,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
         out.Key("positionkeys");
         out.StartArray();
         for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) {
-            const aiVectorKey& pos = ai.mPositionKeys[n];
+            const aiVectorKey &pos = ai.mPositionKeys[n];
             out.StartArray(true);
             out.Element(pos.mTime);
             Write(out, pos.mValue);
@@ -625,7 +616,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
         out.Key("rotationkeys");
         out.StartArray();
         for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) {
-            const aiQuatKey& rot = ai.mRotationKeys[n];
+            const aiQuatKey &rot = ai.mRotationKeys[n];
             out.StartArray(true);
             out.Element(rot.mTime);
             Write(out, rot.mValue);
@@ -638,7 +629,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
         out.Key("scalingkeys");
         out.StartArray();
         for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) {
-            const aiVectorKey& scl = ai.mScalingKeys[n];
+            const aiVectorKey &scl = ai.mScalingKeys[n];
             out.StartArray(true);
             out.Element(scl.mTime);
             Write(out, scl.mValue);
@@ -649,7 +640,7 @@ void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiAnimation &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -670,7 +661,7 @@ void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
+void Write(JSONWriter &out, const aiCamera &ai, bool is_elem = true) {
     out.StartObj(is_elem);
 
     out.Key("name");
@@ -697,7 +688,7 @@ void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
     out.EndObj();
 }
 
-void WriteFormatInfo(JSONWriter& out) {
+void WriteFormatInfo(JSONWriter &out) {
     out.StartObj();
     out.Key("format");
     out.SimpleValue("\"assimp2json\"");
@@ -706,7 +697,7 @@ void WriteFormatInfo(JSONWriter& out) {
     out.EndObj();
 }
 
-void Write(JSONWriter& out, const aiScene& ai) {
+void Write(JSONWriter &out, const aiScene &ai) {
     out.StartObj();
 
     out.Key("__metadata__");
@@ -774,15 +765,14 @@ void Write(JSONWriter& out, const aiScene& ai) {
     out.EndObj();
 }
 
-
-void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) {
+void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) {
     std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
     if (!str) {
-        //throw Assimp::DeadlyExportError("could not open output file");
+        throw DeadlyExportError("could not open output file");
     }
 
     // get a copy of the scene so we can modify it
-    aiScene* scenecopy_tmp;
+    aiScene *scenecopy_tmp;
     aiCopyScene(scene, &scenecopy_tmp);
 
     try {
@@ -795,15 +785,14 @@ void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* sc
         JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
         Write(s, *scenecopy_tmp);
 
-    }
-    catch (...) {
+    } catch (...) {
         aiFreeScene(scenecopy_tmp);
         throw;
     }
     aiFreeScene(scenecopy_tmp);
 }
 
-}
+} // namespace Assimp
 
 #endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 0 - 0
code/Assjson/mesh_splitter.cpp → code/AssetLib/Assjson/mesh_splitter.cpp


+ 0 - 0
code/Assjson/mesh_splitter.h → code/AssetLib/Assjson/mesh_splitter.h


+ 0 - 0
code/Assxml/AssxmlExporter.cpp → code/AssetLib/Assxml/AssxmlExporter.cpp


+ 0 - 0
code/Assxml/AssxmlExporter.h → code/AssetLib/Assxml/AssxmlExporter.h


+ 659 - 0
code/AssetLib/Assxml/AssxmlFileWriter.cpp

@@ -0,0 +1,659 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file AssxmlFileWriter.cpp
+ *  @brief Implementation of Assxml file writer.
+ */
+
+#include "AssxmlFileWriter.h"
+
+#include "PostProcessing/ProcessHelper.h"
+
+#include <assimp/version.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <stdarg.h>
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#include <zlib.h>
+#else
+#include <contrib/zlib/zlib.h>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <memory>
+
+using namespace Assimp;
+
+namespace Assimp {
+
+namespace AssxmlFileWriter {
+
+// -----------------------------------------------------------------------------------
+static int ioprintf(IOStream *io, const char *format, ...) {
+    using namespace std;
+    if (nullptr == io) {
+        return -1;
+    }
+
+    static const int Size = 4096;
+    char sz[Size];
+    ::memset(sz, '\0', Size);
+    va_list va;
+    va_start(va, format);
+    const unsigned int nSize = vsnprintf(sz, Size - 1, format, va);
+    ai_assert(nSize < Size);
+    va_end(va);
+
+    io->Write(sz, sizeof(char), nSize);
+
+    return nSize;
+}
+
+// -----------------------------------------------------------------------------------
+// Convert a name to standard XML format
+static void ConvertName(aiString &out, const aiString &in) {
+    out.length = 0;
+    for (unsigned int i = 0; i < in.length; ++i) {
+        switch (in.data[i]) {
+        case '<':
+            out.Append("&lt;");
+            break;
+        case '>':
+            out.Append("&gt;");
+            break;
+        case '&':
+            out.Append("&amp;");
+            break;
+        case '\"':
+            out.Append("&quot;");
+            break;
+        case '\'':
+            out.Append("&apos;");
+            break;
+        default:
+            out.data[out.length++] = in.data[i];
+        }
+    }
+    out.data[out.length] = 0;
+}
+
+// -----------------------------------------------------------------------------------
+// Write a single node as text dump
+static void WriteNode(const aiNode *node, IOStream *io, unsigned int depth) {
+    char prefix[512];
+    for (unsigned int i = 0; i < depth; ++i)
+        prefix[i] = '\t';
+    prefix[depth] = '\0';
+
+    const aiMatrix4x4 &m = node->mTransformation;
+
+    aiString name;
+    ConvertName(name, node->mName);
+    ioprintf(io, "%s<Node name=\"%s\"> \n"
+                 "%s\t<Matrix4> \n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                 "%s\t</Matrix4> \n",
+            prefix, name.data, prefix,
+            prefix, m.a1, m.a2, m.a3, m.a4,
+            prefix, m.b1, m.b2, m.b3, m.b4,
+            prefix, m.c1, m.c2, m.c3, m.c4,
+            prefix, m.d1, m.d2, m.d3, m.d4, prefix);
+
+    if (node->mNumMeshes) {
+        ioprintf(io, "%s\t<MeshRefs num=\"%u\">\n%s\t",
+                prefix, node->mNumMeshes, prefix);
+
+        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+            ioprintf(io, "%u ", node->mMeshes[i]);
+        }
+        ioprintf(io, "\n%s\t</MeshRefs>\n", prefix);
+    }
+
+    if (node->mNumChildren) {
+        ioprintf(io, "%s\t<NodeList num=\"%u\">\n",
+                prefix, node->mNumChildren);
+
+        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+            WriteNode(node->mChildren[i], io, depth + 2);
+        }
+        ioprintf(io, "%s\t</NodeList>\n", prefix);
+    }
+    ioprintf(io, "%s</Node>\n", prefix);
+}
+
+// -----------------------------------------------------------------------------------
+// Some chuncks of text will need to be encoded for XML
+// http://stackoverflow.com/questions/5665231/most-efficient-way-to-escape-xml-html-in-c-string#5665377
+static std::string encodeXML(const std::string &data) {
+    std::string buffer;
+    buffer.reserve(data.size());
+    for (size_t pos = 0; pos != data.size(); ++pos) {
+        switch (data[pos]) {
+        case '&': buffer.append("&amp;"); break;
+        case '\"': buffer.append("&quot;"); break;
+        case '\'': buffer.append("&apos;"); break;
+        case '<': buffer.append("&lt;"); break;
+        case '>': buffer.append("&gt;"); break;
+        default: buffer.append(&data[pos], 1); break;
+        }
+    }
+    return buffer;
+}
+
+// -----------------------------------------------------------------------------------
+// Write a text model dump
+static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene, IOStream *io, bool shortened) {
+    time_t tt = ::time(NULL);
+#if _WIN32
+    tm *p = gmtime(&tt);
+#else
+    struct tm now;
+    tm *p = gmtime_r(&tt, &now);
+#endif
+    ai_assert(nullptr != p);
+
+    std::string c = cmd;
+    std::string::size_type s;
+
+    // https://sourceforge.net/tracker/?func=detail&aid=3167364&group_id=226462&atid=1067632
+    // -- not allowed in XML comments
+    while ((s = c.find("--")) != std::string::npos) {
+        c[s] = '?';
+    }
+
+    // write header
+    std::string header(
+            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+            "<ASSIMP format_id=\"1\">\n\n"
+            "<!-- XML Model dump produced by assimp dump\n"
+            "  Library version: %u.%u.%u\n"
+            "  Source: %s\n"
+            "  Command line: %s\n"
+            "  %s\n"
+            "-->"
+            " \n\n"
+            "<Scene flags=\"%u\" postprocessing=\"%u\">\n");
+
+    const unsigned int majorVersion(aiGetVersionMajor());
+    const unsigned int minorVersion(aiGetVersionMinor());
+    const unsigned int rev(aiGetVersionRevision());
+    const char *curtime(asctime(p));
+    ioprintf(io, header.c_str(), majorVersion, minorVersion, rev, pFile, c.c_str(), curtime, scene->mFlags, 0u);
+
+    // write the node graph
+    WriteNode(scene->mRootNode, io, 0);
+
+#if 0
+    // write cameras
+    for (unsigned int i = 0; i < scene->mNumCameras;++i) {
+        aiCamera* cam  = scene->mCameras[i];
+        ConvertName(name,cam->mName);
+
+        // camera header
+        ioprintf(io,"\t<Camera parent=\"%s\">\n"
+            "\t\t<Vector3 name=\"up\"        > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"lookat\"    > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"pos\"       > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Float   name=\"fov\"       > %f </Float>\n"
+            "\t\t<Float   name=\"aspect\"    > %f </Float>\n"
+            "\t\t<Float   name=\"near_clip\" > %f </Float>\n"
+            "\t\t<Float   name=\"far_clip\"  > %f </Float>\n"
+            "\t</Camera>\n",
+            name.data,
+            cam->mUp.x,cam->mUp.y,cam->mUp.z,
+            cam->mLookAt.x,cam->mLookAt.y,cam->mLookAt.z,
+            cam->mPosition.x,cam->mPosition.y,cam->mPosition.z,
+            cam->mHorizontalFOV,cam->mAspect,cam->mClipPlaneNear,cam->mClipPlaneFar,i);
+    }
+
+    // write lights
+    for (unsigned int i = 0; i < scene->mNumLights;++i) {
+        aiLight* l  = scene->mLights[i];
+        ConvertName(name,l->mName);
+
+        // light header
+        ioprintf(io,"\t<Light parent=\"%s\"> type=\"%s\"\n"
+            "\t\t<Vector3 name=\"diffuse\"   > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"specular\"  > %0 8f %0 8f %0 8f </Vector3>\n"
+            "\t\t<Vector3 name=\"ambient\"   > %0 8f %0 8f %0 8f </Vector3>\n",
+            name.data,
+            (l->mType == aiLightSource_DIRECTIONAL ? "directional" :
+            (l->mType == aiLightSource_POINT ? "point" : "spot" )),
+            l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b,
+            l->mColorSpecular.r,l->mColorSpecular.g,l->mColorSpecular.b,
+            l->mColorAmbient.r, l->mColorAmbient.g, l->mColorAmbient.b);
+
+        if (l->mType != aiLightSource_DIRECTIONAL) {
+            ioprintf(io,
+                "\t\t<Vector3 name=\"pos\"       > %0 8f %0 8f %0 8f </Vector3>\n"
+                "\t\t<Float   name=\"atten_cst\" > %f </Float>\n"
+                "\t\t<Float   name=\"atten_lin\" > %f </Float>\n"
+                "\t\t<Float   name=\"atten_sqr\" > %f </Float>\n",
+                l->mPosition.x,l->mPosition.y,l->mPosition.z,
+                l->mAttenuationConstant,l->mAttenuationLinear,l->mAttenuationQuadratic);
+        }
+
+        if (l->mType != aiLightSource_POINT) {
+            ioprintf(io,
+                "\t\t<Vector3 name=\"lookat\"    > %0 8f %0 8f %0 8f </Vector3>\n",
+                l->mDirection.x,l->mDirection.y,l->mDirection.z);
+        }
+
+        if (l->mType == aiLightSource_SPOT) {
+            ioprintf(io,
+                "\t\t<Float   name=\"cone_out\" > %f </Float>\n"
+                "\t\t<Float   name=\"cone_inn\" > %f </Float>\n",
+                l->mAngleOuterCone,l->mAngleInnerCone);
+        }
+        ioprintf(io,"\t</Light>\n");
+    }
+#endif
+    aiString name;
+
+    // write textures
+    if (scene->mNumTextures) {
+        ioprintf(io, "<TextureList num=\"%u\">\n", scene->mNumTextures);
+        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
+            aiTexture *tex = scene->mTextures[i];
+            bool compressed = (tex->mHeight == 0);
+
+            // mesh header
+            ioprintf(io, "\t<Texture width=\"%u\" height=\"%u\" compressed=\"%s\"> \n",
+                    (compressed ? -1 : tex->mWidth), (compressed ? -1 : tex->mHeight),
+                    (compressed ? "true" : "false"));
+
+            if (compressed) {
+                ioprintf(io, "\t\t<Data length=\"%u\"> \n", tex->mWidth);
+
+                if (!shortened) {
+                    for (unsigned int n = 0; n < tex->mWidth; ++n) {
+                        ioprintf(io, "\t\t\t%2x", reinterpret_cast<uint8_t *>(tex->pcData)[n]);
+                        if (n && !(n % 50)) {
+                            ioprintf(io, "\n");
+                        }
+                    }
+                }
+            } else if (!shortened) {
+                ioprintf(io, "\t\t<Data length=\"%u\"> \n", tex->mWidth * tex->mHeight * 4);
+
+                // const unsigned int width = (unsigned int)std::log10((double)std::max(tex->mHeight,tex->mWidth))+1;
+                for (unsigned int y = 0; y < tex->mHeight; ++y) {
+                    for (unsigned int x = 0; x < tex->mWidth; ++x) {
+                        aiTexel *tx = tex->pcData + y * tex->mWidth + x;
+                        unsigned int r = tx->r, g = tx->g, b = tx->b, a = tx->a;
+                        ioprintf(io, "\t\t\t%2x %2x %2x %2x", r, g, b, a);
+
+                        // group by four for readability
+                        if (0 == (x + y * tex->mWidth) % 4) {
+                            ioprintf(io, "\n");
+                        }
+                    }
+                }
+            }
+            ioprintf(io, "\t\t</Data>\n\t</Texture>\n");
+        }
+        ioprintf(io, "</TextureList>\n");
+    }
+
+    // write materials
+    if (scene->mNumMaterials) {
+        ioprintf(io, "<MaterialList num=\"%u\">\n", scene->mNumMaterials);
+        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+            const aiMaterial *mat = scene->mMaterials[i];
+
+            ioprintf(io, "\t<Material>\n");
+            ioprintf(io, "\t\t<MatPropertyList  num=\"%u\">\n", mat->mNumProperties);
+            for (unsigned int n = 0; n < mat->mNumProperties; ++n) {
+
+                const aiMaterialProperty *prop = mat->mProperties[n];
+                const char *sz = "";
+                if (prop->mType == aiPTI_Float) {
+                    sz = "float";
+                } else if (prop->mType == aiPTI_Integer) {
+                    sz = "integer";
+                } else if (prop->mType == aiPTI_String) {
+                    sz = "string";
+                } else if (prop->mType == aiPTI_Buffer) {
+                    sz = "binary_buffer";
+                }
+
+                ioprintf(io, "\t\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%u\"",
+                        prop->mKey.data, sz,
+                        ::TextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex);
+
+                if (prop->mType == aiPTI_Float) {
+                    ioprintf(io, " size=\"%i\">\n\t\t\t\t",
+                            static_cast<int>(prop->mDataLength / sizeof(float)));
+
+                    for (unsigned int pp = 0; pp < prop->mDataLength / sizeof(float); ++pp) {
+                        ioprintf(io, "%f ", *((float *)(prop->mData + pp * sizeof(float))));
+                    }
+                } else if (prop->mType == aiPTI_Integer) {
+                    ioprintf(io, " size=\"%i\">\n\t\t\t\t",
+                            static_cast<int>(prop->mDataLength / sizeof(int)));
+
+                    for (unsigned int pp = 0; pp < prop->mDataLength / sizeof(int); ++pp) {
+                        ioprintf(io, "%i ", *((int *)(prop->mData + pp * sizeof(int))));
+                    }
+                } else if (prop->mType == aiPTI_Buffer) {
+                    ioprintf(io, " size=\"%i\">\n\t\t\t\t",
+                            static_cast<int>(prop->mDataLength));
+
+                    for (unsigned int pp = 0; pp < prop->mDataLength; ++pp) {
+                        ioprintf(io, "%2x ", prop->mData[pp]);
+                        if (pp && 0 == pp % 30) {
+                            ioprintf(io, "\n\t\t\t\t");
+                        }
+                    }
+                } else if (prop->mType == aiPTI_String) {
+                    ioprintf(io, ">\n\t\t\t\t\"%s\"", encodeXML(prop->mData + 4).c_str() /* skip length */);
+                }
+                ioprintf(io, "\n\t\t\t</MatProperty>\n");
+            }
+            ioprintf(io, "\t\t</MatPropertyList>\n");
+            ioprintf(io, "\t</Material>\n");
+        }
+        ioprintf(io, "</MaterialList>\n");
+    }
+
+    // write animations
+    if (scene->mNumAnimations) {
+        ioprintf(io, "<AnimationList num=\"%u\">\n", scene->mNumAnimations);
+        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
+            aiAnimation *anim = scene->mAnimations[i];
+
+            // anim header
+            ConvertName(name, anim->mName);
+            ioprintf(io, "\t<Animation name=\"%s\" duration=\"%e\" tick_cnt=\"%e\">\n",
+                    name.data, anim->mDuration, anim->mTicksPerSecond);
+
+            // write bone animation channels
+            if (anim->mNumChannels) {
+                ioprintf(io, "\t\t<NodeAnimList num=\"%u\">\n", anim->mNumChannels);
+                for (unsigned int n = 0; n < anim->mNumChannels; ++n) {
+                    aiNodeAnim *nd = anim->mChannels[n];
+
+                    // node anim header
+                    ConvertName(name, nd->mNodeName);
+                    ioprintf(io, "\t\t\t<NodeAnim node=\"%s\">\n", name.data);
+
+                    if (!shortened) {
+                        // write position keys
+                        if (nd->mNumPositionKeys) {
+                            ioprintf(io, "\t\t\t\t<PositionKeyList num=\"%u\">\n", nd->mNumPositionKeys);
+                            for (unsigned int a = 0; a < nd->mNumPositionKeys; ++a) {
+                                aiVectorKey *vc = nd->mPositionKeys + a;
+                                ioprintf(io, "\t\t\t\t\t<PositionKey time=\"%e\">\n"
+                                             "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</PositionKey>\n",
+                                        vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z);
+                            }
+                            ioprintf(io, "\t\t\t\t</PositionKeyList>\n");
+                        }
+
+                        // write scaling keys
+                        if (nd->mNumScalingKeys) {
+                            ioprintf(io, "\t\t\t\t<ScalingKeyList num=\"%u\">\n", nd->mNumScalingKeys);
+                            for (unsigned int a = 0; a < nd->mNumScalingKeys; ++a) {
+                                aiVectorKey *vc = nd->mScalingKeys + a;
+                                ioprintf(io, "\t\t\t\t\t<ScalingKey time=\"%e\">\n"
+                                             "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</ScalingKey>\n",
+                                        vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z);
+                            }
+                            ioprintf(io, "\t\t\t\t</ScalingKeyList>\n");
+                        }
+
+                        // write rotation keys
+                        if (nd->mNumRotationKeys) {
+                            ioprintf(io, "\t\t\t\t<RotationKeyList num=\"%u\">\n", nd->mNumRotationKeys);
+                            for (unsigned int a = 0; a < nd->mNumRotationKeys; ++a) {
+                                aiQuatKey *vc = nd->mRotationKeys + a;
+                                ioprintf(io, "\t\t\t\t\t<RotationKey time=\"%e\">\n"
+                                             "\t\t\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t\t\t</RotationKey>\n",
+                                        vc->mTime, vc->mValue.x, vc->mValue.y, vc->mValue.z, vc->mValue.w);
+                            }
+                            ioprintf(io, "\t\t\t\t</RotationKeyList>\n");
+                        }
+                    }
+                    ioprintf(io, "\t\t\t</NodeAnim>\n");
+                }
+                ioprintf(io, "\t\t</NodeAnimList>\n");
+            }
+            ioprintf(io, "\t</Animation>\n");
+        }
+        ioprintf(io, "</AnimationList>\n");
+    }
+
+    // write meshes
+    if (scene->mNumMeshes) {
+        ioprintf(io, "<MeshList num=\"%u\">\n", scene->mNumMeshes);
+        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+            aiMesh *mesh = scene->mMeshes[i];
+            // const unsigned int width = (unsigned int)std::log10((double)mesh->mNumVertices)+1;
+
+            // mesh header
+            ioprintf(io, "\t<Mesh types=\"%s %s %s %s\" material_index=\"%u\">\n",
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_POINT ? "points" : ""),
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_LINE ? "lines" : ""),
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE ? "triangles" : ""),
+                    (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON ? "polygons" : ""),
+                    mesh->mMaterialIndex);
+
+            // bones
+            if (mesh->mNumBones) {
+                ioprintf(io, "\t\t<BoneList num=\"%u\">\n", mesh->mNumBones);
+
+                for (unsigned int n = 0; n < mesh->mNumBones; ++n) {
+                    aiBone *bone = mesh->mBones[n];
+
+                    ConvertName(name, bone->mName);
+                    // bone header
+                    ioprintf(io, "\t\t\t<Bone name=\"%s\">\n"
+                                 "\t\t\t\t<Matrix4> \n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+                                 "\t\t\t\t</Matrix4> \n",
+                            name.data,
+                            bone->mOffsetMatrix.a1, bone->mOffsetMatrix.a2, bone->mOffsetMatrix.a3, bone->mOffsetMatrix.a4,
+                            bone->mOffsetMatrix.b1, bone->mOffsetMatrix.b2, bone->mOffsetMatrix.b3, bone->mOffsetMatrix.b4,
+                            bone->mOffsetMatrix.c1, bone->mOffsetMatrix.c2, bone->mOffsetMatrix.c3, bone->mOffsetMatrix.c4,
+                            bone->mOffsetMatrix.d1, bone->mOffsetMatrix.d2, bone->mOffsetMatrix.d3, bone->mOffsetMatrix.d4);
+
+                    if (!shortened && bone->mNumWeights) {
+                        ioprintf(io, "\t\t\t\t<WeightList num=\"%u\">\n", bone->mNumWeights);
+
+                        // bone weights
+                        for (unsigned int a = 0; a < bone->mNumWeights; ++a) {
+                            aiVertexWeight *wght = bone->mWeights + a;
+
+                            ioprintf(io, "\t\t\t\t\t<Weight index=\"%u\">\n\t\t\t\t\t\t%f\n\t\t\t\t\t</Weight>\n",
+                                    wght->mVertexId, wght->mWeight);
+                        }
+                        ioprintf(io, "\t\t\t\t</WeightList>\n");
+                    }
+                    ioprintf(io, "\t\t\t</Bone>\n");
+                }
+                ioprintf(io, "\t\t</BoneList>\n");
+            }
+
+            // faces
+            if (!shortened && mesh->mNumFaces) {
+                ioprintf(io, "\t\t<FaceList num=\"%u\">\n", mesh->mNumFaces);
+                for (unsigned int n = 0; n < mesh->mNumFaces; ++n) {
+                    aiFace &f = mesh->mFaces[n];
+                    ioprintf(io, "\t\t\t<Face num=\"%u\">\n"
+                                 "\t\t\t\t",
+                            f.mNumIndices);
+
+                    for (unsigned int j = 0; j < f.mNumIndices; ++j)
+                        ioprintf(io, "%u ", f.mIndices[j]);
+
+                    ioprintf(io, "\n\t\t\t</Face>\n");
+                }
+                ioprintf(io, "\t\t</FaceList>\n");
+            }
+
+            // vertex positions
+            if (mesh->HasPositions()) {
+                ioprintf(io, "\t\t<Positions num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mVertices[n].x,
+                                mesh->mVertices[n].y,
+                                mesh->mVertices[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Positions>\n");
+            }
+
+            // vertex normals
+            if (mesh->HasNormals()) {
+                ioprintf(io, "\t\t<Normals num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mNormals[n].x,
+                                mesh->mNormals[n].y,
+                                mesh->mNormals[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Normals>\n");
+            }
+
+            // vertex tangents and bitangents
+            if (mesh->HasTangentsAndBitangents()) {
+                ioprintf(io, "\t\t<Tangents num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mTangents[n].x,
+                                mesh->mTangents[n].y,
+                                mesh->mTangents[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Tangents>\n");
+
+                ioprintf(io, "\t\t<Bitangents num=\"%u\" set=\"0\" num_components=\"3\"> \n", mesh->mNumVertices);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                mesh->mBitangents[n].x,
+                                mesh->mBitangents[n].y,
+                                mesh->mBitangents[n].z);
+                    }
+                }
+                ioprintf(io, "\t\t</Bitangents>\n");
+            }
+
+            // texture coordinates
+            for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+                if (!mesh->mTextureCoords[a])
+                    break;
+
+                ioprintf(io, "\t\t<TextureCoords num=\"%u\" set=\"%u\" num_components=\"%u\"> \n", mesh->mNumVertices,
+                        a, mesh->mNumUVComponents[a]);
+
+                if (!shortened) {
+                    if (mesh->mNumUVComponents[a] == 3) {
+                        for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                            ioprintf(io, "\t\t%0 8f %0 8f %0 8f\n",
+                                    mesh->mTextureCoords[a][n].x,
+                                    mesh->mTextureCoords[a][n].y,
+                                    mesh->mTextureCoords[a][n].z);
+                        }
+                    } else {
+                        for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                            ioprintf(io, "\t\t%0 8f %0 8f\n",
+                                    mesh->mTextureCoords[a][n].x,
+                                    mesh->mTextureCoords[a][n].y);
+                        }
+                    }
+                }
+                ioprintf(io, "\t\t</TextureCoords>\n");
+            }
+
+            // vertex colors
+            for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+                if (!mesh->mColors[a])
+                    break;
+                ioprintf(io, "\t\t<Colors num=\"%u\" set=\"%u\" num_components=\"4\"> \n", mesh->mNumVertices, a);
+                if (!shortened) {
+                    for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+                        ioprintf(io, "\t\t%0 8f %0 8f %0 8f %0 8f\n",
+                                mesh->mColors[a][n].r,
+                                mesh->mColors[a][n].g,
+                                mesh->mColors[a][n].b,
+                                mesh->mColors[a][n].a);
+                    }
+                }
+                ioprintf(io, "\t\t</Colors>\n");
+            }
+            ioprintf(io, "\t</Mesh>\n");
+        }
+        ioprintf(io, "</MeshList>\n");
+    }
+    ioprintf(io, "</Scene>\n</ASSIMP>");
+}
+
+} // end of namespace AssxmlFileWriter
+
+void DumpSceneToAssxml(
+        const char *pFile, const char *cmd, IOSystem *pIOSystem,
+        const aiScene *pScene, bool shortened) {
+    std::unique_ptr<IOStream> file(pIOSystem->Open(pFile, "wt"));
+    if (!file.get()) {
+        throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n');
+    }
+
+    AssxmlFileWriter::WriteDump(pFile, cmd, pScene, file.get(), shortened);
+}
+
+} // end of namespace Assimp

+ 0 - 0
code/Assxml/AssxmlFileWriter.h → code/AssetLib/Assxml/AssxmlFileWriter.h


+ 744 - 0
code/AssetLib/B3D/B3DImporter.cpp

@@ -0,0 +1,744 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  B3DImporter.cpp
+ *  @brief Implementation of the b3d importer class
+ */
+
+#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
+
+// internal headers
+#include "AssetLib/B3D/B3DImporter.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+#include "PostProcessing/TextureTransform.h"
+
+#include <assimp/StringUtils.h>
+#include <assimp/anim.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <memory>
+
+using namespace Assimp;
+using namespace std;
+
+static const aiImporterDesc desc = {
+    "BlitzBasic 3D Importer",
+    "",
+    "",
+    "http://www.blitzbasic.com/",
+    aiImporterFlags_SupportBinaryFlavour,
+    0,
+    0,
+    0,
+    0,
+    "b3d"
+};
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4018)
+#endif
+
+//#define DEBUG_B3D
+
+template <typename T>
+void DeleteAllBarePointers(std::vector<T> &x) {
+    for (auto p : x) {
+        delete p;
+    }
+}
+
+B3DImporter::~B3DImporter() {
+}
+
+// ------------------------------------------------------------------------------------------------
+bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
+
+    size_t pos = pFile.find_last_of('.');
+    if (pos == string::npos) {
+        return false;
+    }
+
+    string ext = pFile.substr(pos + 1);
+    if (ext.size() != 3) {
+        return false;
+    }
+
+    return (ext[0] == 'b' || ext[0] == 'B') && (ext[1] == '3') && (ext[2] == 'd' || ext[2] == 'D');
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc *B3DImporter::GetInfo() const {
+    return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+
+    // Check whether we can read from the file
+    if (file.get() == nullptr) {
+        throw DeadlyImportError("Failed to open B3D file " + pFile + ".");
+    }
+
+    // check whether the .b3d file is large enough to contain
+    // at least one chunk.
+    size_t fileSize = file->FileSize();
+    if (fileSize < 8) {
+        throw DeadlyImportError("B3D File is too small.");
+    }
+
+    _pos = 0;
+    _buf.resize(fileSize);
+    file->Read(&_buf[0], 1, fileSize);
+    _stack.clear();
+
+    ReadBB3D(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+AI_WONT_RETURN void B3DImporter::Oops() {
+    throw DeadlyImportError("B3D Importer - INTERNAL ERROR");
+}
+
+// ------------------------------------------------------------------------------------------------
+AI_WONT_RETURN void B3DImporter::Fail(string str) {
+#ifdef DEBUG_B3D
+    ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
+#endif
+    throw DeadlyImportError("B3D Importer - error in B3D file data: " + str);
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadByte() {
+    if (_pos > _buf.size()) {
+        Fail("EOF");
+    }
+
+    return _buf[_pos++];
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadInt() {
+    if (_pos + 4 > _buf.size()) {
+        Fail("EOF");
+    }
+
+    int n;
+    memcpy(&n, &_buf[_pos], 4);
+    _pos += 4;
+
+    return n;
+}
+
+// ------------------------------------------------------------------------------------------------
+float B3DImporter::ReadFloat() {
+    if (_pos + 4 > _buf.size()) {
+        Fail("EOF");
+    }
+
+    float n;
+    memcpy(&n, &_buf[_pos], 4);
+    _pos += 4;
+
+    return n;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector2D B3DImporter::ReadVec2() {
+    float x = ReadFloat();
+    float y = ReadFloat();
+    return aiVector2D(x, y);
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector3D B3DImporter::ReadVec3() {
+    float x = ReadFloat();
+    float y = ReadFloat();
+    float z = ReadFloat();
+    return aiVector3D(x, y, z);
+}
+
+// ------------------------------------------------------------------------------------------------
+aiQuaternion B3DImporter::ReadQuat() {
+    // (aramis_acg) Fix to adapt the loader to changed quat orientation
+    float w = -ReadFloat();
+    float x = ReadFloat();
+    float y = ReadFloat();
+    float z = ReadFloat();
+    return aiQuaternion(w, x, y, z);
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadString() {
+    if (_pos > _buf.size()) {
+        Fail("EOF");
+    }
+    string str;
+    while (_pos < _buf.size()) {
+        char c = (char)ReadByte();
+        if (!c) {
+            return str;
+        }
+        str += c;
+    }
+    return string();
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadChunk() {
+    string tag;
+    for (int i = 0; i < 4; ++i) {
+        tag += char(ReadByte());
+    }
+#ifdef DEBUG_B3D
+    ASSIMP_LOG_DEBUG_F("ReadChunk: ", tag);
+#endif
+    unsigned sz = (unsigned)ReadInt();
+    _stack.push_back(_pos + sz);
+    return tag;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ExitChunk() {
+    _pos = _stack.back();
+    _stack.pop_back();
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t B3DImporter::ChunkSize() {
+    return _stack.back() - _pos;
+}
+// ------------------------------------------------------------------------------------------------
+
+template <class T>
+T *B3DImporter::to_array(const vector<T> &v) {
+    if (v.empty()) {
+        return 0;
+    }
+    T *p = new T[v.size()];
+    for (size_t i = 0; i < v.size(); ++i) {
+        p[i] = v[i];
+    }
+    return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+T **unique_to_array(vector<std::unique_ptr<T>> &v) {
+    if (v.empty()) {
+        return 0;
+    }
+    T **p = new T *[v.size()];
+    for (size_t i = 0; i < v.size(); ++i) {
+        p[i] = v[i].release();
+    }
+    return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTEXS() {
+    while (ChunkSize()) {
+        string name = ReadString();
+        /*int flags=*/ReadInt();
+        /*int blend=*/ReadInt();
+        /*aiVector2D pos=*/ReadVec2();
+        /*aiVector2D scale=*/ReadVec2();
+        /*float rot=*/ReadFloat();
+
+        _textures.push_back(name);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBRUS() {
+    int n_texs = ReadInt();
+    if (n_texs < 0 || n_texs > 8) {
+        Fail("Bad texture count");
+    }
+    while (ChunkSize()) {
+        string name = ReadString();
+        aiVector3D color = ReadVec3();
+        float alpha = ReadFloat();
+        float shiny = ReadFloat();
+        /*int blend=**/ ReadInt();
+        int fx = ReadInt();
+
+        std::unique_ptr<aiMaterial> mat(new aiMaterial);
+
+        // Name
+        aiString ainame(name);
+        mat->AddProperty(&ainame, AI_MATKEY_NAME);
+
+        // Diffuse color
+        mat->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE);
+
+        // Opacity
+        mat->AddProperty(&alpha, 1, AI_MATKEY_OPACITY);
+
+        // Specular color
+        aiColor3D speccolor(shiny, shiny, shiny);
+        mat->AddProperty(&speccolor, 1, AI_MATKEY_COLOR_SPECULAR);
+
+        // Specular power
+        float specpow = shiny * 128;
+        mat->AddProperty(&specpow, 1, AI_MATKEY_SHININESS);
+
+        // Double sided
+        if (fx & 0x10) {
+            int i = 1;
+            mat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
+        }
+
+        //Textures
+        for (int i = 0; i < n_texs; ++i) {
+            int texid = ReadInt();
+            if (texid < -1 || (texid >= 0 && texid >= static_cast<int>(_textures.size()))) {
+                Fail("Bad texture id");
+            }
+            if (i == 0 && texid >= 0) {
+                aiString texname(_textures[texid]);
+                mat->AddProperty(&texname, AI_MATKEY_TEXTURE_DIFFUSE(0));
+            }
+        }
+        _materials.emplace_back(std::move(mat));
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadVRTS() {
+    _vflags = ReadInt();
+    _tcsets = ReadInt();
+    _tcsize = ReadInt();
+    if (_tcsets < 0 || _tcsets > 4 || _tcsize < 0 || _tcsize > 4) {
+        Fail("Bad texcoord data");
+    }
+
+    int sz = 12 + (_vflags & 1 ? 12 : 0) + (_vflags & 2 ? 16 : 0) + (_tcsets * _tcsize * 4);
+    size_t n_verts = ChunkSize() / sz;
+
+    int v0 = static_cast<int>(_vertices.size());
+    _vertices.resize(v0 + n_verts);
+
+    for (unsigned int i = 0; i < n_verts; ++i) {
+        Vertex &v = _vertices[v0 + i];
+
+        memset(v.bones, 0, sizeof(v.bones));
+        memset(v.weights, 0, sizeof(v.weights));
+
+        v.vertex = ReadVec3();
+
+        if (_vflags & 1) {
+            v.normal = ReadVec3();
+        }
+
+        if (_vflags & 2) {
+            ReadQuat(); //skip v 4bytes...
+        }
+
+        for (int j = 0; j < _tcsets; ++j) {
+            float t[4] = { 0, 0, 0, 0 };
+            for (int k = 0; k < _tcsize; ++k) {
+                t[k] = ReadFloat();
+            }
+            t[1] = 1 - t[1];
+            if (!j) {
+                v.texcoords = aiVector3D(t[0], t[1], t[2]);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTRIS(int v0) {
+    int matid = ReadInt();
+    if (matid == -1) {
+        matid = 0;
+    } else if (matid < 0 || matid >= (int)_materials.size()) {
+#ifdef DEBUG_B3D
+        ASSIMP_LOG_ERROR_F("material id=", matid);
+#endif
+        Fail("Bad material id");
+    }
+
+    std::unique_ptr<aiMesh> mesh(new aiMesh);
+
+    mesh->mMaterialIndex = matid;
+    mesh->mNumFaces = 0;
+    mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+    size_t n_tris = ChunkSize() / 12;
+    aiFace *face = mesh->mFaces = new aiFace[n_tris];
+
+    for (unsigned int i = 0; i < n_tris; ++i) {
+        int i0 = ReadInt() + v0;
+        int i1 = ReadInt() + v0;
+        int i2 = ReadInt() + v0;
+        if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
+#ifdef DEBUG_B3D
+            ASSIMP_LOG_ERROR_F("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
+#endif
+            Fail("Bad triangle index");
+            continue;
+        }
+        face->mNumIndices = 3;
+        face->mIndices = new unsigned[3];
+        face->mIndices[0] = i0;
+        face->mIndices[1] = i1;
+        face->mIndices[2] = i2;
+        ++mesh->mNumFaces;
+        ++face;
+    }
+
+    _meshes.emplace_back(std::move(mesh));
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadMESH() {
+    /*int matid=*/ReadInt();
+
+    int v0 = static_cast<int>(_vertices.size());
+
+    while (ChunkSize()) {
+        string t = ReadChunk();
+        if (t == "VRTS") {
+            ReadVRTS();
+        } else if (t == "TRIS") {
+            ReadTRIS(v0);
+        }
+        ExitChunk();
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBONE(int id) {
+    while (ChunkSize()) {
+        int vertex = ReadInt();
+        float weight = ReadFloat();
+        if (vertex < 0 || vertex >= (int)_vertices.size()) {
+            Fail("Bad vertex index");
+        }
+
+        Vertex &v = _vertices[vertex];
+        for (int i = 0; i < 4; ++i) {
+            if (!v.weights[i]) {
+                v.bones[i] = static_cast<unsigned char>(id);
+                v.weights[i] = weight;
+                break;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadKEYS(aiNodeAnim *nodeAnim) {
+    vector<aiVectorKey> trans, scale;
+    vector<aiQuatKey> rot;
+    int flags = ReadInt();
+    while (ChunkSize()) {
+        int frame = ReadInt();
+        if (flags & 1) {
+            trans.push_back(aiVectorKey(frame, ReadVec3()));
+        }
+        if (flags & 2) {
+            scale.push_back(aiVectorKey(frame, ReadVec3()));
+        }
+        if (flags & 4) {
+            rot.push_back(aiQuatKey(frame, ReadQuat()));
+        }
+    }
+
+    if (flags & 1) {
+        nodeAnim->mNumPositionKeys = static_cast<unsigned int>(trans.size());
+        nodeAnim->mPositionKeys = to_array(trans);
+    }
+
+    if (flags & 2) {
+        nodeAnim->mNumScalingKeys = static_cast<unsigned int>(scale.size());
+        nodeAnim->mScalingKeys = to_array(scale);
+    }
+
+    if (flags & 4) {
+        nodeAnim->mNumRotationKeys = static_cast<unsigned int>(rot.size());
+        nodeAnim->mRotationKeys = to_array(rot);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadANIM() {
+    /*int flags=*/ReadInt();
+    int frames = ReadInt();
+    float fps = ReadFloat();
+
+    std::unique_ptr<aiAnimation> anim(new aiAnimation);
+
+    anim->mDuration = frames;
+    anim->mTicksPerSecond = fps;
+    _animations.emplace_back(std::move(anim));
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode *B3DImporter::ReadNODE(aiNode *parent) {
+
+    string name = ReadString();
+    aiVector3D t = ReadVec3();
+    aiVector3D s = ReadVec3();
+    aiQuaternion r = ReadQuat();
+
+    aiMatrix4x4 trans, scale, rot;
+
+    aiMatrix4x4::Translation(t, trans);
+    aiMatrix4x4::Scaling(s, scale);
+    rot = aiMatrix4x4(r.GetMatrix());
+
+    aiMatrix4x4 tform = trans * rot * scale;
+
+    int nodeid = static_cast<int>(_nodes.size());
+
+    aiNode *node = new aiNode(name);
+    _nodes.push_back(node);
+
+    node->mParent = parent;
+    node->mTransformation = tform;
+
+    std::unique_ptr<aiNodeAnim> nodeAnim;
+    vector<unsigned> meshes;
+    vector<aiNode *> children;
+
+    while (ChunkSize()) {
+        const string chunk = ReadChunk();
+        if (chunk == "MESH") {
+            unsigned int n = static_cast<unsigned int>(_meshes.size());
+            ReadMESH();
+            for (unsigned int i = n; i < static_cast<unsigned int>(_meshes.size()); ++i) {
+                meshes.push_back(i);
+            }
+        } else if (chunk == "BONE") {
+            ReadBONE(nodeid);
+        } else if (chunk == "ANIM") {
+            ReadANIM();
+        } else if (chunk == "KEYS") {
+            if (!nodeAnim) {
+                nodeAnim.reset(new aiNodeAnim);
+                nodeAnim->mNodeName = node->mName;
+            }
+            ReadKEYS(nodeAnim.get());
+        } else if (chunk == "NODE") {
+            aiNode *child = ReadNODE(node);
+            children.push_back(child);
+        }
+        ExitChunk();
+    }
+
+    if (nodeAnim) {
+        _nodeAnims.emplace_back(std::move(nodeAnim));
+    }
+
+    node->mNumMeshes = static_cast<unsigned int>(meshes.size());
+    node->mMeshes = to_array(meshes);
+
+    node->mNumChildren = static_cast<unsigned int>(children.size());
+    node->mChildren = to_array(children);
+
+    return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBB3D(aiScene *scene) {
+
+    _textures.clear();
+
+    _materials.clear();
+
+    _vertices.clear();
+
+    _meshes.clear();
+
+    DeleteAllBarePointers(_nodes);
+    _nodes.clear();
+
+    _nodeAnims.clear();
+
+    _animations.clear();
+
+    string t = ReadChunk();
+    if (t == "BB3D") {
+        int version = ReadInt();
+
+        if (!DefaultLogger::isNullLogger()) {
+            char dmp[128];
+            ai_snprintf(dmp, 128, "B3D file format version: %i", version);
+            ASSIMP_LOG_INFO(dmp);
+        }
+
+        while (ChunkSize()) {
+            const string chunk = ReadChunk();
+            if (chunk == "TEXS") {
+                ReadTEXS();
+            } else if (chunk == "BRUS") {
+                ReadBRUS();
+            } else if (chunk == "NODE") {
+                ReadNODE(0);
+            }
+            ExitChunk();
+        }
+    }
+    ExitChunk();
+
+    if (!_nodes.size()) {
+        Fail("No nodes");
+    }
+
+    if (!_meshes.size()) {
+        Fail("No meshes");
+    }
+
+    // Fix nodes/meshes/bones
+    for (size_t i = 0; i < _nodes.size(); ++i) {
+        aiNode *node = _nodes[i];
+
+        for (size_t j = 0; j < node->mNumMeshes; ++j) {
+            aiMesh *mesh = _meshes[node->mMeshes[j]].get();
+
+            int n_tris = mesh->mNumFaces;
+            int n_verts = mesh->mNumVertices = n_tris * 3;
+
+            aiVector3D *mv = mesh->mVertices = new aiVector3D[n_verts], *mn = 0, *mc = 0;
+            if (_vflags & 1) {
+                mn = mesh->mNormals = new aiVector3D[n_verts];
+            }
+            if (_tcsets) {
+                mc = mesh->mTextureCoords[0] = new aiVector3D[n_verts];
+            }
+
+            aiFace *face = mesh->mFaces;
+
+            vector<vector<aiVertexWeight>> vweights(_nodes.size());
+
+            for (int vertIdx = 0; vertIdx < n_verts; vertIdx += 3) {
+                for (int faceIndex = 0; faceIndex < 3; ++faceIndex) {
+                    Vertex &v = _vertices[face->mIndices[faceIndex]];
+
+                    *mv++ = v.vertex;
+                    if (mn) *mn++ = v.normal;
+                    if (mc) *mc++ = v.texcoords;
+
+                    face->mIndices[faceIndex] = vertIdx + faceIndex;
+
+                    for (int k = 0; k < 4; ++k) {
+                        if (!v.weights[k])
+                            break;
+
+                        int bone = v.bones[k];
+                        float weight = v.weights[k];
+
+                        vweights[bone].push_back(aiVertexWeight(vertIdx + faceIndex, weight));
+                    }
+                }
+                ++face;
+            }
+
+            vector<aiBone *> bones;
+            for (size_t weightIndx = 0; weightIndx < vweights.size(); ++weightIndx) {
+                vector<aiVertexWeight> &weights = vweights[weightIndx];
+                if (!weights.size()) {
+                    continue;
+                }
+
+                aiBone *bone = new aiBone;
+                bones.push_back(bone);
+
+                aiNode *bnode = _nodes[weightIndx];
+
+                bone->mName = bnode->mName;
+                bone->mNumWeights = static_cast<unsigned int>(weights.size());
+                bone->mWeights = to_array(weights);
+
+                aiMatrix4x4 mat = bnode->mTransformation;
+                while (bnode->mParent) {
+                    bnode = bnode->mParent;
+                    mat = bnode->mTransformation * mat;
+                }
+                bone->mOffsetMatrix = mat.Inverse();
+            }
+            mesh->mNumBones = static_cast<unsigned int>(bones.size());
+            mesh->mBones = to_array(bones);
+        }
+    }
+
+    //nodes
+    scene->mRootNode = _nodes[0];
+    _nodes.clear(); // node ownership now belongs to scene
+
+    //material
+    if (!_materials.size()) {
+        _materials.emplace_back(std::unique_ptr<aiMaterial>(new aiMaterial));
+    }
+    scene->mNumMaterials = static_cast<unsigned int>(_materials.size());
+    scene->mMaterials = unique_to_array(_materials);
+
+    //meshes
+    scene->mNumMeshes = static_cast<unsigned int>(_meshes.size());
+    scene->mMeshes = unique_to_array(_meshes);
+
+    //animations
+    if (_animations.size() == 1 && _nodeAnims.size()) {
+
+        aiAnimation *anim = _animations.back().get();
+        anim->mNumChannels = static_cast<unsigned int>(_nodeAnims.size());
+        anim->mChannels = unique_to_array(_nodeAnims);
+
+        scene->mNumAnimations = static_cast<unsigned int>(_animations.size());
+        scene->mAnimations = unique_to_array(_animations);
+    }
+
+    // convert to RH
+    MakeLeftHandedProcess makeleft;
+    makeleft.Execute(scene);
+
+    FlipWindingOrderProcess flip;
+    flip.Execute(scene);
+}
+
+#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER

+ 0 - 0
code/B3D/B3DImporter.h → code/AssetLib/B3D/B3DImporter.h


+ 543 - 0
code/AssetLib/BVH/BVHLoader.cpp

@@ -0,0 +1,543 @@
+/** Implementation of the BVH loader */
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
+
+#include "BVHLoader.h"
+#include <assimp/SkeletonMeshBuilder.h>
+#include <assimp/TinyFormatter.h>
+#include <assimp/fast_atof.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/Importer.hpp>
+#include <map>
+#include <memory>
+
+using namespace Assimp;
+using namespace Assimp::Formatter;
+
+static const aiImporterDesc desc = {
+    "BVH Importer (MoCap)",
+    "",
+    "",
+    "",
+    aiImporterFlags_SupportTextFlavour,
+    0,
+    0,
+    0,
+    0,
+    "bvh"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BVHLoader::BVHLoader() :
+        mLine(),
+        mAnimTickDuration(),
+        mAnimNumFrames(),
+        noSkeletonMesh() {}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BVHLoader::~BVHLoader() {}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const {
+    // check file extension
+    const std::string extension = GetExtension(pFile);
+
+    if (extension == "bvh")
+        return true;
+
+    if ((!extension.length() || cs) && pIOHandler) {
+        const char *tokens[] = { "HIERARCHY" };
+        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+    }
+    return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BVHLoader::SetupProperties(const Importer *pImp) {
+    noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc *BVHLoader::GetInfo() const {
+    return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void BVHLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    mFileName = pFile;
+
+    // read file into memory
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
+    if (file.get() == nullptr) {
+        throw DeadlyImportError("Failed to open file " + pFile + ".");
+    }
+
+    size_t fileSize = file->FileSize();
+    if (fileSize == 0) {
+        throw DeadlyImportError("File is too small.");
+    }
+
+    mBuffer.resize(fileSize);
+    file->Read(&mBuffer.front(), 1, fileSize);
+
+    // start reading
+    mReader = mBuffer.begin();
+    mLine = 1;
+    ReadStructure(pScene);
+
+    if (!noSkeletonMesh) {
+        // build a dummy mesh for the skeleton so that we see something at least
+        SkeletonMeshBuilder meshBuilder(pScene);
+    }
+
+    // construct an animation from all the motion data we read
+    CreateAnimation(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the file
+void BVHLoader::ReadStructure(aiScene *pScene) {
+    // first comes hierarchy
+    std::string header = GetNextToken();
+    if (header != "HIERARCHY")
+        ThrowException("Expected header string \"HIERARCHY\".");
+    ReadHierarchy(pScene);
+
+    // then comes the motion data
+    std::string motion = GetNextToken();
+    if (motion != "MOTION")
+        ThrowException("Expected beginning of motion data \"MOTION\".");
+    ReadMotion(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the hierarchy
+void BVHLoader::ReadHierarchy(aiScene *pScene) {
+    std::string root = GetNextToken();
+    if (root != "ROOT")
+        ThrowException("Expected root node \"ROOT\".");
+
+    // Go read the hierarchy from here
+    pScene->mRootNode = ReadNode();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node and recursively its childs and returns the created node;
+aiNode *BVHLoader::ReadNode() {
+    // first token is name
+    std::string nodeName = GetNextToken();
+    if (nodeName.empty() || nodeName == "{")
+        ThrowException(format() << "Expected node name, but found \"" << nodeName << "\".");
+
+    // then an opening brace should follow
+    std::string openBrace = GetNextToken();
+    if (openBrace != "{")
+        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
+
+    // Create a node
+    aiNode *node = new aiNode(nodeName);
+    std::vector<aiNode *> childNodes;
+
+    // and create an bone entry for it
+    mNodes.push_back(Node(node));
+    Node &internNode = mNodes.back();
+
+    // now read the node's contents
+    std::string siteToken;
+    while (1) {
+        std::string token = GetNextToken();
+
+        // node offset to parent node
+        if (token == "OFFSET")
+            ReadNodeOffset(node);
+        else if (token == "CHANNELS")
+            ReadNodeChannels(internNode);
+        else if (token == "JOINT") {
+            // child node follows
+            aiNode *child = ReadNode();
+            child->mParent = node;
+            childNodes.push_back(child);
+        } else if (token == "End") {
+            // The real symbol is "End Site". Second part comes in a separate token
+            siteToken.clear();
+            siteToken = GetNextToken();
+            if (siteToken != "Site")
+                ThrowException(format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\".");
+
+            aiNode *child = ReadEndSite(nodeName);
+            child->mParent = node;
+            childNodes.push_back(child);
+        } else if (token == "}") {
+            // we're done with that part of the hierarchy
+            break;
+        } else {
+            // everything else is a parse error
+            ThrowException(format() << "Unknown keyword \"" << token << "\".");
+        }
+    }
+
+    // add the child nodes if there are any
+    if (childNodes.size() > 0) {
+        node->mNumChildren = static_cast<unsigned int>(childNodes.size());
+        node->mChildren = new aiNode *[node->mNumChildren];
+        std::copy(childNodes.begin(), childNodes.end(), node->mChildren);
+    }
+
+    // and return the sub-hierarchy we built here
+    return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an end node and returns the created node.
+aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
+    // check opening brace
+    std::string openBrace = GetNextToken();
+    if (openBrace != "{")
+        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
+
+    // Create a node
+    aiNode *node = new aiNode("EndSite_" + pParentName);
+
+    // now read the node's contents. Only possible entry is "OFFSET"
+    std::string token;
+    while (1) {
+        token.clear();
+        token = GetNextToken();
+
+        // end node's offset
+        if (token == "OFFSET") {
+            ReadNodeOffset(node);
+        } else if (token == "}") {
+            // we're done with the end node
+            break;
+        } else {
+            // everything else is a parse error
+            ThrowException(format() << "Unknown keyword \"" << token << "\".");
+        }
+    }
+
+    // and return the sub-hierarchy we built here
+    return node;
+}
+// ------------------------------------------------------------------------------------------------
+// Reads a node offset for the given node
+void BVHLoader::ReadNodeOffset(aiNode *pNode) {
+    // Offset consists of three floats to read
+    aiVector3D offset;
+    offset.x = GetNextTokenAsFloat();
+    offset.y = GetNextTokenAsFloat();
+    offset.z = GetNextTokenAsFloat();
+
+    // build a transformation matrix from it
+    pNode->mTransformation = aiMatrix4x4(1.0f, 0.0f, 0.0f, offset.x,
+            0.0f, 1.0f, 0.0f, offset.y,
+            0.0f, 0.0f, 1.0f, offset.z,
+            0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation channels for the given node
+void BVHLoader::ReadNodeChannels(BVHLoader::Node &pNode) {
+    // number of channels. Use the float reader because we're lazy
+    float numChannelsFloat = GetNextTokenAsFloat();
+    unsigned int numChannels = (unsigned int)numChannelsFloat;
+
+    for (unsigned int a = 0; a < numChannels; a++) {
+        std::string channelToken = GetNextToken();
+
+        if (channelToken == "Xposition")
+            pNode.mChannels.push_back(Channel_PositionX);
+        else if (channelToken == "Yposition")
+            pNode.mChannels.push_back(Channel_PositionY);
+        else if (channelToken == "Zposition")
+            pNode.mChannels.push_back(Channel_PositionZ);
+        else if (channelToken == "Xrotation")
+            pNode.mChannels.push_back(Channel_RotationX);
+        else if (channelToken == "Yrotation")
+            pNode.mChannels.push_back(Channel_RotationY);
+        else if (channelToken == "Zrotation")
+            pNode.mChannels.push_back(Channel_RotationZ);
+        else
+            ThrowException(format() << "Invalid channel specifier \"" << channelToken << "\".");
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the motion data
+void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
+    // Read number of frames
+    std::string tokenFrames = GetNextToken();
+    if (tokenFrames != "Frames:")
+        ThrowException(format() << "Expected frame count \"Frames:\", but found \"" << tokenFrames << "\".");
+
+    float numFramesFloat = GetNextTokenAsFloat();
+    mAnimNumFrames = (unsigned int)numFramesFloat;
+
+    // Read frame duration
+    std::string tokenDuration1 = GetNextToken();
+    std::string tokenDuration2 = GetNextToken();
+    if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
+        ThrowException(format() << "Expected frame duration \"Frame Time:\", but found \"" << tokenDuration1 << " " << tokenDuration2 << "\".");
+
+    mAnimTickDuration = GetNextTokenAsFloat();
+
+    // resize value vectors for each node
+    for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+        it->mChannelValues.reserve(it->mChannels.size() * mAnimNumFrames);
+
+    // now read all the data and store it in the corresponding node's value vector
+    for (unsigned int frame = 0; frame < mAnimNumFrames; ++frame) {
+        // on each line read the values for all nodes
+        for (std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it) {
+            // get as many values as the node has channels
+            for (unsigned int c = 0; c < it->mChannels.size(); ++c)
+                it->mChannelValues.push_back(GetNextTokenAsFloat());
+        }
+
+        // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Retrieves the next token
+std::string BVHLoader::GetNextToken() {
+    // skip any preceding whitespace
+    while (mReader != mBuffer.end()) {
+        if (!isspace(*mReader))
+            break;
+
+        // count lines
+        if (*mReader == '\n')
+            mLine++;
+
+        ++mReader;
+    }
+
+    // collect all chars till the next whitespace. BVH is easy in respect to that.
+    std::string token;
+    while (mReader != mBuffer.end()) {
+        if (isspace(*mReader))
+            break;
+
+        token.push_back(*mReader);
+        ++mReader;
+
+        // little extra logic to make sure braces are counted correctly
+        if (token == "{" || token == "}")
+            break;
+    }
+
+    // empty token means end of file, which is just fine
+    return token;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the next token as a float
+float BVHLoader::GetNextTokenAsFloat() {
+    std::string token = GetNextToken();
+    if (token.empty())
+        ThrowException("Unexpected end of file while trying to read a float");
+
+    // check if the float is valid by testing if the atof() function consumed every char of the token
+    const char *ctoken = token.c_str();
+    float result = 0.0f;
+    ctoken = fast_atoreal_move<float>(ctoken, result);
+
+    if (ctoken != token.c_str() + token.length())
+        ThrowException(format() << "Expected a floating point number, but found \"" << token << "\".");
+
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+AI_WONT_RETURN void BVHLoader::ThrowException(const std::string &pError) {
+    throw DeadlyImportError(format() << mFileName << ":" << mLine << " - " << pError);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs an animation for the motion data and stores it in the given scene
+void BVHLoader::CreateAnimation(aiScene *pScene) {
+    // create the animation
+    pScene->mNumAnimations = 1;
+    pScene->mAnimations = new aiAnimation *[1];
+    aiAnimation *anim = new aiAnimation;
+    pScene->mAnimations[0] = anim;
+
+    // put down the basic parameters
+    anim->mName.Set("Motion");
+    anim->mTicksPerSecond = 1.0 / double(mAnimTickDuration);
+    anim->mDuration = double(mAnimNumFrames - 1);
+
+    // now generate the tracks for all nodes
+    anim->mNumChannels = static_cast<unsigned int>(mNodes.size());
+    anim->mChannels = new aiNodeAnim *[anim->mNumChannels];
+
+    // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown
+    for (unsigned int i = 0; i < anim->mNumChannels; ++i)
+        anim->mChannels[i] = NULL;
+
+    for (unsigned int a = 0; a < anim->mNumChannels; a++) {
+        const Node &node = mNodes[a];
+        const std::string nodeName = std::string(node.mNode->mName.data);
+        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) {
+            nodeAnim->mNumPositionKeys = mAnimNumFrames;
+            nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
+            aiVectorKey *poskey = nodeAnim->mPositionKeys;
+            for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
+                poskey->mTime = double(fr);
+
+                // Now compute all translations
+                for (BVHLoader::ChannelType channel = Channel_PositionX; channel <= Channel_PositionZ; 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 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;
+            }
+        } else {
+            // if no translation part is given, put a default sequence
+            aiVector3D nodePos(node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
+            nodeAnim->mNumPositionKeys = 1;
+            nodeAnim->mPositionKeys = new aiVectorKey[1];
+            nodeAnim->mPositionKeys[0].mTime = 0.0;
+            nodeAnim->mPositionKeys[0].mValue = nodePos;
+        }
+
+        // rotation part. Always present. First find value offsets
+        {
+
+            // Then create the number of rotation keys
+            nodeAnim->mNumRotationKeys = mAnimNumFrames;
+            nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
+            aiQuatKey *rotkey = nodeAnim->mRotationKeys;
+            for (unsigned int fr = 0; fr < mAnimNumFrames; ++fr) {
+                aiMatrix4x4 temp;
+                aiMatrix3x3 rotMatrix;
+                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);
+                rotkey->mValue = aiQuaternion(rotMatrix);
+                ++rotkey;
+            }
+        }
+
+        // scaling part. Always just a default track
+        {
+            nodeAnim->mNumScalingKeys = 1;
+            nodeAnim->mScalingKeys = new aiVectorKey[1];
+            nodeAnim->mScalingKeys[0].mTime = 0.0;
+            nodeAnim->mScalingKeys[0].mValue.Set(1.0f, 1.0f, 1.0f);
+        }
+    }
+}
+
+#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER

+ 22 - 29
code/BVH/BVHLoader.h → code/AssetLib/BVH/BVHLoader.h

@@ -53,8 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 struct aiNode;
 
-namespace Assimp
-{
+namespace Assimp {
 
 // --------------------------------------------------------------------------------
 /** Loader class to read Motion Capturing data from a .bvh file.
@@ -63,12 +62,10 @@ namespace Assimp
  * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh
  * inside the loader just to be able to see something.
 */
-class BVHLoader : public BaseImporter
-{
+class BVHLoader : public BaseImporter {
 
     /** Possible animation channels for which the motion data holds the values */
-    enum ChannelType
-    {
+    enum ChannelType {
         Channel_PositionX,
         Channel_PositionY,
         Channel_PositionZ,
@@ -78,61 +75,57 @@ class BVHLoader : public BaseImporter
     };
 
     /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */
-    struct Node
-    {
-        const aiNode* mNode;
+    struct Node {
+        const aiNode *mNode;
         std::vector<ChannelType> mChannels;
         std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
 
-        Node()
-        : mNode(nullptr)
-        { }
+        Node() :
+                mNode(nullptr) {}
 
-        explicit Node( const aiNode* pNode) : mNode( pNode) { }
+        explicit Node(const aiNode *pNode) :
+                mNode(pNode) {}
     };
 
 public:
-
     BVHLoader();
     ~BVHLoader();
 
 public:
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details. */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const;
 
-    void SetupProperties(const Importer* pImp);
-    const aiImporterDesc* GetInfo () const;
+    void SetupProperties(const Importer *pImp);
+    const aiImporterDesc *GetInfo() const;
 
 protected:
-
-
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      */
-    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
 
 protected:
     /** Reads the file */
-    void ReadStructure( aiScene* pScene);
+    void ReadStructure(aiScene *pScene);
 
     /** Reads the hierarchy */
-    void ReadHierarchy( aiScene* pScene);
+    void ReadHierarchy(aiScene *pScene);
 
     /** Reads a node and recursively its childs and returns the created node. */
-    aiNode* ReadNode();
+    aiNode *ReadNode();
 
     /** Reads an end node and returns the created node. */
-    aiNode* ReadEndSite( const std::string& pParentName);
+    aiNode *ReadEndSite(const std::string &pParentName);
 
     /** Reads a node offset for the given node */
-    void ReadNodeOffset( aiNode* pNode);
+    void ReadNodeOffset(aiNode *pNode);
 
     /** Reads the animation channels into the given node */
-    void ReadNodeChannels( BVHLoader::Node& pNode);
+    void ReadNodeChannels(BVHLoader::Node &pNode);
 
     /** Reads the motion data */
-    void ReadMotion( aiScene* pScene);
+    void ReadMotion(aiScene *pScene);
 
     /** Retrieves the next token */
     std::string GetNextToken();
@@ -141,10 +134,10 @@ protected:
     float GetNextTokenAsFloat();
 
     /** Aborts the file reading with an exception */
-    AI_WONT_RETURN void ThrowException( const std::string& pError) AI_WONT_RETURN_SUFFIX;
+    AI_WONT_RETURN void ThrowException(const std::string &pError) AI_WONT_RETURN_SUFFIX;
 
     /** Constructs an animation for the motion data and stores it in the given scene */
-    void CreateAnimation( aiScene* pScene);
+    void CreateAnimation(aiScene *pScene);
 
 protected:
     /** Filename, for a verbose error message */

+ 62 - 84
code/Blender/BlenderBMesh.cpp → code/AssetLib/Blender/BlenderBMesh.cpp

@@ -42,165 +42,143 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Conversion of Blender's new BMesh stuff
  */
 
-
 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
 
+#include "BlenderBMesh.h"
 #include "BlenderDNA.h"
 #include "BlenderScene.h"
-#include "BlenderBMesh.h"
 #include "BlenderTessellator.h"
 
-namespace Assimp
-{
-    template< > const char* LogFunctions< BlenderBMeshConverter >::Prefix()
-    {
-        static auto prefix = "BLEND_BMESH: ";
-        return prefix;
-    }
+namespace Assimp {
+template <>
+const char *LogFunctions<BlenderBMeshConverter>::Prefix() {
+    static auto prefix = "BLEND_BMESH: ";
+    return prefix;
 }
+} // namespace Assimp
 
 using namespace Assimp;
 using namespace Assimp::Blender;
 using namespace Assimp::Formatter;
 
 // ------------------------------------------------------------------------------------------------
-BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ):
-    BMesh( mesh ),
-    triMesh( NULL )
-{
+BlenderBMeshConverter::BlenderBMeshConverter(const Mesh *mesh) :
+        BMesh(mesh),
+        triMesh(nullptr) {
+    ai_assert(nullptr != mesh);
 }
 
 // ------------------------------------------------------------------------------------------------
-BlenderBMeshConverter::~BlenderBMeshConverter( )
-{
-    DestroyTriMesh( );
+BlenderBMeshConverter::~BlenderBMeshConverter() {
+    DestroyTriMesh();
 }
 
 // ------------------------------------------------------------------------------------------------
-bool BlenderBMeshConverter::ContainsBMesh( ) const
-{
+bool BlenderBMeshConverter::ContainsBMesh() const {
     // TODO - Should probably do some additional verification here
     return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
 }
 
 // ------------------------------------------------------------------------------------------------
-const Mesh* BlenderBMeshConverter::TriangulateBMesh( )
-{
-    AssertValidMesh( );
-    AssertValidSizes( );
-    PrepareTriMesh( );
-
-    for ( int i = 0; i < BMesh->totpoly; ++i )
-    {
-        const MPoly& poly = BMesh->mpoly[ i ];
-        ConvertPolyToFaces( poly );
+const Mesh *BlenderBMeshConverter::TriangulateBMesh() {
+    AssertValidMesh();
+    AssertValidSizes();
+    PrepareTriMesh();
+
+    for (int i = 0; i < BMesh->totpoly; ++i) {
+        const MPoly &poly = BMesh->mpoly[i];
+        ConvertPolyToFaces(poly);
     }
 
     return triMesh;
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AssertValidMesh( )
-{
-    if ( !ContainsBMesh( ) )
-    {
-        ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" );
+void BlenderBMeshConverter::AssertValidMesh() {
+    if (!ContainsBMesh()) {
+        ThrowException("BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first");
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AssertValidSizes( )
-{
-    if ( BMesh->totpoly != static_cast<int>( BMesh->mpoly.size( ) ) )
-    {
-        ThrowException( "BMesh poly array has incorrect size" );
+void BlenderBMeshConverter::AssertValidSizes() {
+    if (BMesh->totpoly != static_cast<int>(BMesh->mpoly.size())) {
+        ThrowException("BMesh poly array has incorrect size");
     }
-    if ( BMesh->totloop != static_cast<int>( BMesh->mloop.size( ) ) )
-    {
-        ThrowException( "BMesh loop array has incorrect size" );
+    if (BMesh->totloop != static_cast<int>(BMesh->mloop.size())) {
+        ThrowException("BMesh loop array has incorrect size");
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::PrepareTriMesh( )
-{
-    if ( triMesh )
-    {
-        DestroyTriMesh( );
+void BlenderBMeshConverter::PrepareTriMesh() {
+    if (triMesh) {
+        DestroyTriMesh();
     }
 
-    triMesh = new Mesh( *BMesh );
+    triMesh = new Mesh(*BMesh);
     triMesh->totface = 0;
-    triMesh->mface.clear( );
+    triMesh->mface.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::DestroyTriMesh( )
-{
+void BlenderBMeshConverter::DestroyTriMesh() {
     delete triMesh;
-    triMesh = NULL;
+    triMesh = nullptr;
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::ConvertPolyToFaces( const MPoly& poly )
-{
-    const MLoop* polyLoop = &BMesh->mloop[ poly.loopstart ];
+void BlenderBMeshConverter::ConvertPolyToFaces(const MPoly &poly) {
+    const MLoop *polyLoop = &BMesh->mloop[poly.loopstart];
 
-    if ( poly.totloop == 3 || poly.totloop == 4 )
-    {
-        AddFace( polyLoop[ 0 ].v, polyLoop[ 1 ].v, polyLoop[ 2 ].v, poly.totloop == 4 ? polyLoop[ 3 ].v : 0 );
+    if (poly.totloop == 3 || poly.totloop == 4) {
+        AddFace(polyLoop[0].v, polyLoop[1].v, polyLoop[2].v, poly.totloop == 4 ? polyLoop[3].v : 0);
 
         // UVs are optional, so only convert when present.
-        if ( BMesh->mloopuv.size() )
-        {
-            if ( (poly.loopstart + poly.totloop ) > static_cast<int>( BMesh->mloopuv.size() ) )
-            {
-                ThrowException( "BMesh uv loop array has incorrect size" );
+        if (BMesh->mloopuv.size()) {
+            if ((poly.loopstart + poly.totloop) > static_cast<int>(BMesh->mloopuv.size())) {
+                ThrowException("BMesh uv loop array has incorrect size");
             }
-            const MLoopUV* loopUV = &BMesh->mloopuv[ poly.loopstart ];
-            AddTFace( loopUV[ 0 ].uv, loopUV[ 1 ].uv, loopUV[ 2 ].uv, poly.totloop == 4 ? loopUV[ 3 ].uv : 0 );
+            const MLoopUV *loopUV = &BMesh->mloopuv[poly.loopstart];
+            AddTFace(loopUV[0].uv, loopUV[1].uv, loopUV[2].uv, poly.totloop == 4 ? loopUV[3].uv : 0);
         }
-    }
-    else if ( poly.totloop > 4 )
-    {
+    } else if (poly.totloop > 4) {
 #if ASSIMP_BLEND_WITH_GLU_TESSELLATE
-        BlenderTessellatorGL tessGL( *this );
-        tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+        BlenderTessellatorGL tessGL(*this);
+        tessGL.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
 #elif ASSIMP_BLEND_WITH_POLY_2_TRI
-        BlenderTessellatorP2T tessP2T( *this );
-        tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+        BlenderTessellatorP2T tessP2T(*this);
+        tessP2T.Tessellate(polyLoop, poly.totloop, triMesh->mvert);
 #endif
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
-{
+void BlenderBMeshConverter::AddFace(int v1, int v2, int v3, int v4) {
     MFace face;
     face.v1 = v1;
     face.v2 = v2;
     face.v3 = v3;
     face.v4 = v4;
+    face.flag = 0;
     // TODO - Work out how materials work
     face.mat_nr = 0;
-    triMesh->mface.push_back( face );
-    triMesh->totface = static_cast<int>(triMesh->mface.size( ));
+    triMesh->mface.push_back(face);
+    triMesh->totface = static_cast<int>(triMesh->mface.size());
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderBMeshConverter::AddTFace( const float* uv1, const float *uv2, const float *uv3, const float* uv4 )
-{
+void BlenderBMeshConverter::AddTFace(const float *uv1, const float *uv2, const float *uv3, const float *uv4) {
     MTFace mtface;
-    memcpy( &mtface.uv[ 0 ], uv1, sizeof(float) * 2 );
-    memcpy( &mtface.uv[ 1 ], uv2, sizeof(float) * 2 );
-    memcpy( &mtface.uv[ 2 ], uv3, sizeof(float) * 2 );
+    memcpy(&mtface.uv[0], uv1, sizeof(float) * 2);
+    memcpy(&mtface.uv[1], uv2, sizeof(float) * 2);
+    memcpy(&mtface.uv[2], uv3, sizeof(float) * 2);
 
-    if ( uv4 )
-    {
-        memcpy( &mtface.uv[ 3 ], uv4, sizeof(float) * 2 );
+    if (uv4) {
+        memcpy(&mtface.uv[3], uv4, sizeof(float) * 2);
     }
 
-    triMesh->mtface.push_back( mtface );
+    triMesh->mtface.push_back(mtface);
 }
 
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

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


+ 181 - 0
code/AssetLib/Blender/BlenderCustomData.cpp

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

+ 0 - 0
code/Blender/BlenderCustomData.h → code/AssetLib/Blender/BlenderCustomData.h


+ 74 - 89
code/Blender/BlenderDNA.cpp → code/AssetLib/Blender/BlenderDNA.cpp

@@ -45,25 +45,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *    serialized set of data structures.
  */
 
-
 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
 #include "BlenderDNA.h"
 #include <assimp/StreamReader.h>
-#include <assimp/fast_atof.h>
 #include <assimp/TinyFormatter.h>
+#include <assimp/fast_atof.h>
 
 using namespace Assimp;
 using namespace Assimp::Blender;
 using namespace Assimp::Formatter;
 
-static bool match4(StreamReaderAny& stream, const char* string) {
-    ai_assert( nullptr != string );
+static bool match4(StreamReaderAny &stream, const char *string) {
+    ai_assert(nullptr != string);
     char tmp[4];
-    tmp[ 0 ] = ( stream ).GetI1();
-    tmp[ 1 ] = ( stream ).GetI1();
-    tmp[ 2 ] = ( stream ).GetI1();
-    tmp[ 3 ] = ( stream ).GetI1();
-    return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]);
+    tmp[0] = (stream).GetI1();
+    tmp[1] = (stream).GetI1();
+    tmp[2] = (stream).GetI1();
+    tmp[3] = (stream).GetI1();
+    return (tmp[0] == string[0] && tmp[1] == string[1] && tmp[2] == string[2] && tmp[3] == string[3]);
 }
 
 struct Type {
@@ -72,75 +71,76 @@ struct Type {
 };
 
 // ------------------------------------------------------------------------------------------------
-void DNAParser::Parse ()
-{
-    StreamReaderAny& stream = *db.reader.get();
-    DNA& dna = db.dna;
+void DNAParser::Parse() {
+    StreamReaderAny &stream = *db.reader.get();
+    DNA &dna = db.dna;
 
-    if(!match4(stream,"SDNA")) {
+    if (!match4(stream, "SDNA")) {
         throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
     }
 
     // name dictionary
-    if(!match4(stream,"NAME")) {
+    if (!match4(stream, "NAME")) {
         throw DeadlyImportError("BlenderDNA: Expected NAME field");
     }
 
-    std::vector<std::string> names (stream.GetI4());
-    for(std::string& s : names) {
+    std::vector<std::string> names(stream.GetI4());
+    for (std::string &s : names) {
         while (char c = stream.GetI1()) {
             s += c;
         }
     }
 
     // type dictionary
-    for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
-    if(!match4(stream,"TYPE")) {
+    for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+        ;
+    if (!match4(stream, "TYPE")) {
         throw DeadlyImportError("BlenderDNA: Expected TYPE field");
     }
 
-    std::vector<Type> types (stream.GetI4());
-    for(Type& s : types) {
+    std::vector<Type> types(stream.GetI4());
+    for (Type &s : types) {
         while (char c = stream.GetI1()) {
             s.name += c;
         }
     }
 
     // type length dictionary
-    for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
-    if(!match4(stream,"TLEN")) {
+    for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+        ;
+    if (!match4(stream, "TLEN")) {
         throw DeadlyImportError("BlenderDNA: Expected TLEN field");
     }
 
-    for(Type& s : types) {
+    for (Type &s : types) {
         s.size = stream.GetI2();
     }
 
     // structures dictionary
-    for (;stream.GetCurrentPos() & 0x3; stream.GetI1());
-    if(!match4(stream,"STRC")) {
+    for (; stream.GetCurrentPos() & 0x3; stream.GetI1())
+        ;
+    if (!match4(stream, "STRC")) {
         throw DeadlyImportError("BlenderDNA: Expected STRC field");
     }
 
     size_t end = stream.GetI4(), fields = 0;
 
     dna.structures.reserve(end);
-    for(size_t i = 0; i != end; ++i) {
+    for (size_t i = 0; i != end; ++i) {
 
         uint16_t n = stream.GetI2();
         if (n >= types.size()) {
             throw DeadlyImportError((format(),
-                "BlenderDNA: Invalid type index in structure name" ,n,
-                " (there are only ", types.size(), " entries)"
-            ));
+                    "BlenderDNA: Invalid type index in structure name", n,
+                    " (there are only ", types.size(), " entries)"));
         }
 
         // maintain separate indexes
         dna.indices[types[n].name] = dna.structures.size();
 
         dna.structures.push_back(Structure());
-        Structure& s = dna.structures.back();
-        s.name  = types[n].name;
+        Structure &s = dna.structures.back();
+        s.name = types[n].name;
         //s.index = dna.structures.size()-1;
 
         n = stream.GetI2();
@@ -152,12 +152,11 @@ void DNAParser::Parse ()
             uint16_t j = stream.GetI2();
             if (j >= types.size()) {
                 throw DeadlyImportError((format(),
-                    "BlenderDNA: Invalid type index in structure field ", j,
-                    " (there are only ", types.size(), " entries)"
-                ));
+                        "BlenderDNA: Invalid type index in structure field ", j,
+                        " (there are only ", types.size(), " entries)"));
             }
             s.fields.push_back(Field());
-            Field& f = s.fields.back();
+            Field &f = s.fields.back();
             f.offset = offset;
 
             f.type = types[j].name;
@@ -166,9 +165,8 @@ void DNAParser::Parse ()
             j = stream.GetI2();
             if (j >= names.size()) {
                 throw DeadlyImportError((format(),
-                    "BlenderDNA: Invalid name index in structure field ", j,
-                    " (there are only ", names.size(), " entries)"
-                ));
+                        "BlenderDNA: Invalid name index in structure field ", j,
+                        " (there are only ", names.size(), " entries)"));
             }
 
             f.name = names[j];
@@ -191,26 +189,25 @@ void DNAParser::Parse ()
                 const std::string::size_type rb = f.name.find('[');
                 if (rb == std::string::npos) {
                     throw DeadlyImportError((format(),
-                        "BlenderDNA: Encountered invalid array declaration ",
-                        f.name
-                    ));
+                            "BlenderDNA: Encountered invalid array declaration ",
+                            f.name));
                 }
 
                 f.flags |= FieldFlag_Array;
-                DNA::ExtractArraySize(f.name,f.array_sizes);
-                f.name = f.name.substr(0,rb);
+                DNA::ExtractArraySize(f.name, f.array_sizes);
+                f.name = f.name.substr(0, rb);
 
                 f.size *= f.array_sizes[0] * f.array_sizes[1];
             }
 
             // maintain separate indexes
-            s.indices[f.name] = s.fields.size()-1;
+            s.indices[f.name] = s.fields.size() - 1;
             offset += f.size;
         }
         s.size = offset;
     }
 
-    ASSIMP_LOG_DEBUG_F( "BlenderDNA: Got ", dna.structures.size()," structures with totally ",fields," fields");
+    ASSIMP_LOG_DEBUG_F("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields");
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
     dna.DumpToFile();
@@ -220,13 +217,11 @@ void DNAParser::Parse ()
     dna.RegisterConverters();
 }
 
-
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
 
 #include <fstream>
 // ------------------------------------------------------------------------------------------------
-void DNA :: DumpToFile()
-{
+void DNA ::DumpToFile() {
     // we don't bother using the VFS here for this is only for debugging.
     // (and all your bases are belong to us).
 
@@ -235,12 +230,14 @@ void DNA :: DumpToFile()
         ASSIMP_LOG_ERROR("Could not dump dna to dna.txt");
         return;
     }
-    f << "Field format: type name offset size" << "\n";
-    f << "Structure format: name size" << "\n";
+    f << "Field format: type name offset size"
+      << "\n";
+    f << "Structure format: name size"
+      << "\n";
 
-    for(const Structure& s : structures) {
+    for (const Structure &s : structures) {
         f << s.name << " " << s.size << "\n\n";
-        for(const Field& ff : s.fields) {
+        for (const Field &ff : s.fields) {
             f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << "\n";
         }
         f << "\n";
@@ -252,11 +249,9 @@ void DNA :: DumpToFile()
 #endif
 
 // ------------------------------------------------------------------------------------------------
-/*static*/ void  DNA :: ExtractArraySize(
-    const std::string& out,
-    size_t array_sizes[2]
-)
-{
+/*static*/ void DNA ::ExtractArraySize(
+        const std::string &out,
+        size_t array_sizes[2]) {
     array_sizes[0] = array_sizes[1] = 1;
     std::string::size_type pos = out.find('[');
     if (pos++ == std::string::npos) {
@@ -264,7 +259,7 @@ void DNA :: DumpToFile()
     }
     array_sizes[0] = strtoul10(&out[pos]);
 
-    pos = out.find('[',pos);
+    pos = out.find('[', pos);
     if (pos++ == std::string::npos) {
         return;
     }
@@ -272,36 +267,32 @@ void DNA :: DumpToFile()
 }
 
 // ------------------------------------------------------------------------------------------------
-std::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure(
-    const Structure& structure,
-    const FileDatabase& db
-) const
-{
-    std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name);
+std::shared_ptr<ElemBase> DNA ::ConvertBlobToStructure(
+        const Structure &structure,
+        const FileDatabase &db) const {
+    std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
     if (it == converters.end()) {
-        return std::shared_ptr< ElemBase >();
+        return std::shared_ptr<ElemBase>();
     }
 
-    std::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))();
-    (structure.*((*it).second.second))(ret,db);
+    std::shared_ptr<ElemBase> ret = (structure.*((*it).second.first))();
+    (structure.*((*it).second.second))(ret, db);
 
     return ret;
 }
 
 // ------------------------------------------------------------------------------------------------
-DNA::FactoryPair DNA :: GetBlobToStructureConverter(
-    const Structure& structure,
-    const FileDatabase& /*db*/
-) const
-{
-    std::map<std::string,  FactoryPair>::const_iterator it = converters.find(structure.name);
+DNA::FactoryPair DNA ::GetBlobToStructureConverter(
+        const Structure &structure,
+        const FileDatabase & /*db*/
+) const {
+    std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
     return it == converters.end() ? FactoryPair() : (*it).second;
 }
 
 // basing on http://www.blender.org/development/architecture/notes-on-sdna/
 // ------------------------------------------------------------------------------------------------
-void DNA :: AddPrimitiveStructures()
-{
+void DNA ::AddPrimitiveStructures() {
     // NOTE: these are just dummies. Their presence enforces
     // Structure::Convert<target_type> to be called on these
     // empty structures. These converters are special
@@ -311,30 +302,27 @@ void DNA :: AddPrimitiveStructures()
     // in question.
 
     indices["int"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "int";
     structures.back().size = 4;
 
     indices["short"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "short";
     structures.back().size = 2;
 
-
     indices["char"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "char";
     structures.back().size = 1;
 
-
     indices["float"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "float";
     structures.back().size = 4;
 
-
     indices["double"] = structures.size();
-    structures.push_back( Structure() );
+    structures.push_back(Structure());
     structures.back().name = "double";
     structures.back().size = 8;
 
@@ -342,8 +330,7 @@ void DNA :: AddPrimitiveStructures()
 }
 
 // ------------------------------------------------------------------------------------------------
-void SectionParser :: Next()
-{
+void SectionParser ::Next() {
     stream.SetCurrentPos(current.start + current.size);
 
     const char tmp[] = {
@@ -352,7 +339,7 @@ void SectionParser :: Next()
         (const char)stream.GetI1(),
         (const char)stream.GetI1()
     };
-    current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1);
+    current.id = std::string(tmp, tmp[3] ? 4 : tmp[2] ? 3 : tmp[1] ? 2 : 1);
 
     current.size = stream.GetI4();
     current.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
@@ -370,6 +357,4 @@ void SectionParser :: Next()
 #endif
 }
 
-
-
 #endif

+ 162 - 205
code/Blender/BlenderDNA.h → code/AssetLib/Blender/BlenderDNA.h

@@ -49,26 +49,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/BaseImporter.h>
 #include <assimp/StreamReader.h>
-#include <assimp/DefaultLogger.hpp>
 #include <stdint.h>
-#include <memory>
+#include <assimp/DefaultLogger.hpp>
 #include <map>
+#include <memory>
 
 // enable verbose log output. really verbose, so be careful.
 #ifdef ASSIMP_BUILD_DEBUG
-#   define ASSIMP_BUILD_BLENDER_DEBUG
+#define ASSIMP_BUILD_BLENDER_DEBUG
 #endif
 
 // #define ASSIMP_BUILD_BLENDER_NO_STATS
 
-namespace Assimp    {
+namespace Assimp {
 
-template <bool,bool> class StreamReader;
-typedef StreamReader<true,true> StreamReaderAny;
+template <bool, bool>
+class StreamReader;
+typedef StreamReader<true, true> StreamReaderAny;
 
 namespace Blender {
 
-class  FileDatabase;
+class FileDatabase;
 struct FileBlockHead;
 
 template <template <typename> class TOUT>
@@ -82,8 +83,8 @@ class ObjectCache;
  *  ancestry. */
 // -------------------------------------------------------------------------------
 struct Error : DeadlyImportError {
-    Error (const std::string& s)
-    : DeadlyImportError(s) {
+    Error(const std::string &s) :
+            DeadlyImportError(s) {
         // empty
     }
 };
@@ -93,9 +94,8 @@ struct Error : DeadlyImportError {
  *  descendents. It serves as base class for all data structure fields. */
 // -------------------------------------------------------------------------------
 struct ElemBase {
-    ElemBase()
-    : dna_type(nullptr)
-    {
+    ElemBase() :
+            dna_type(nullptr) {
         // empty
     }
 
@@ -110,7 +110,7 @@ struct ElemBase {
      * data type is not static, i.e. a std::shared_ptr<ElemBase>
      * in the scene description would have its type resolved
      * at runtime, so this member is always set. */
-    const char* dna_type;
+    const char *dna_type;
 };
 
 // -------------------------------------------------------------------------------
@@ -120,8 +120,8 @@ struct ElemBase {
  *  they used to point to.*/
 // -------------------------------------------------------------------------------
 struct Pointer {
-    Pointer()
-    : val() {
+    Pointer() :
+            val() {
         // empty
     }
     uint64_t val;
@@ -131,8 +131,8 @@ struct Pointer {
 /** Represents a generic offset within a BLEND file */
 // -------------------------------------------------------------------------------
 struct FileOffset {
-    FileOffset()
-    : val() {
+    FileOffset() :
+            val() {
         // empty
     }
     uint64_t val;
@@ -154,7 +154,7 @@ public:
         resize(0);
     }
 
-    operator bool () const {
+    operator bool() const {
         return !empty();
     }
 };
@@ -164,7 +164,7 @@ public:
 // -------------------------------------------------------------------------------
 enum FieldFlags {
     FieldFlag_Pointer = 0x1,
-    FieldFlag_Array   = 0x2
+    FieldFlag_Array = 0x2
 };
 
 // -------------------------------------------------------------------------------
@@ -200,7 +200,7 @@ enum ErrorPolicy {
 };
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
-#   define ErrorPolicy_Igno ErrorPolicy_Warn
+#define ErrorPolicy_Igno ErrorPolicy_Warn
 #endif
 
 // -------------------------------------------------------------------------------
@@ -212,47 +212,42 @@ enum ErrorPolicy {
  *  meaningful contents. */
 // -------------------------------------------------------------------------------
 class Structure {
-    template <template <typename> class> friend class ObjectCache;
+    template <template <typename> class>
+    friend class ObjectCache;
 
 public:
-    Structure()
-    : cache_idx(static_cast<size_t>(-1) ){
+    Structure() :
+            cache_idx(static_cast<size_t>(-1)) {
         // empty
     }
 
-public:
-
     // publicly accessible members
     std::string name;
-    vector< Field > fields;
+    vector<Field> fields;
     std::map<std::string, size_t> indices;
 
     size_t size;
 
-public:
-
     // --------------------------------------------------------
     /** Access a field of the structure by its canonical name. The pointer version
      *  returns NULL on failure while the reference version raises an import error. */
-    inline const Field& operator [] (const std::string& ss) const;
-    inline const Field* Get (const std::string& ss) const;
+    inline const Field &operator[](const std::string &ss) const;
+    inline const Field *Get(const std::string &ss) const;
 
     // --------------------------------------------------------
     /** Access a field of the structure by its index */
-    inline const Field& operator [] (const size_t i) const;
+    inline const Field &operator[](const size_t i) const;
 
     // --------------------------------------------------------
-    inline bool operator== (const Structure& other) const {
+    inline bool operator==(const Structure &other) const {
         return name == other.name; // name is meant to be an unique identifier
     }
 
     // --------------------------------------------------------
-    inline bool operator!= (const Structure& other) const {
+    inline bool operator!=(const Structure &other) const {
         return name != other.name;
     }
 
-public:
-
     // --------------------------------------------------------
     /** Try to read an instance of the structure from the stream
      *  and attempt to convert to `T`. This is done by
@@ -260,54 +255,54 @@ public:
      *  a compiler complain is the result.
      *  @param dest Destination value to be written
      *  @param db File database, including input stream. */
-    template <typename T> void Convert (T& dest, const FileDatabase& db) const;
+    template <typename T>
+    void Convert(T &dest, const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // generic converter
     template <typename T>
-    void Convert(std::shared_ptr<ElemBase> in,const FileDatabase& db) const;
+    void Convert(std::shared_ptr<ElemBase> in, const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // generic allocator
-    template <typename T> std::shared_ptr<ElemBase> Allocate() const;
-
-
+    template <typename T>
+    std::shared_ptr<ElemBase> Allocate() const;
 
     // --------------------------------------------------------
     // field parsing for 1d arrays
     template <int error_policy, typename T, size_t M>
-    void ReadFieldArray(T (& out)[M], const char* name,
-        const FileDatabase& db) const;
+    void ReadFieldArray(T (&out)[M], const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // field parsing for 2d arrays
     template <int error_policy, typename T, size_t M, size_t N>
-    void ReadFieldArray2(T (& out)[M][N], const char* name,
-        const FileDatabase& db) const;
+    void ReadFieldArray2(T (&out)[M][N], const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // field parsing for pointer or dynamic array types
     // (std::shared_ptr)
     // The return value indicates whether the data was already cached.
     template <int error_policy, template <typename> class TOUT, typename T>
-    bool ReadFieldPtr(TOUT<T>& out, const char* name,
-        const FileDatabase& db,
-        bool non_recursive = false) const;
+    bool ReadFieldPtr(TOUT<T> &out, const char *name,
+            const FileDatabase &db,
+            bool non_recursive = false) const;
 
     // --------------------------------------------------------
     // field parsing for static arrays of pointer or dynamic
     // array types (std::shared_ptr[])
     // The return value indicates whether the data was already cached.
     template <int error_policy, template <typename> class TOUT, typename T, size_t N>
-    bool ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
-        const FileDatabase& db) const;
+    bool ReadFieldPtr(TOUT<T> (&out)[N], const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     // field parsing for `normal` values
     // The return value indicates whether the data was already cached.
     template <int error_policy, typename T>
-    void ReadField(T& out, const char* name,
-        const FileDatabase& db) const;
+    void ReadField(T &out, const char *name,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     /**
@@ -318,7 +313,7 @@ public:
     *   @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;
+    bool ReadFieldPtrVector(vector<TOUT<T>> &out, const char *name, const FileDatabase &db) const;
 
     /**
     *   @brief  parses raw customdata
@@ -329,40 +324,40 @@ public:
     *   @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;
+    bool ReadCustomDataPtr(std::shared_ptr<ElemBase> &out, int cdtype, const char *name, const FileDatabase &db) const;
 
 private:
-
     // --------------------------------------------------------
     template <template <typename> class TOUT, typename T>
-    bool ResolvePointer(TOUT<T>& out, const Pointer & ptrval,
-        const FileDatabase& db, const Field& f,
-        bool non_recursive = false) const;
+    bool ResolvePointer(TOUT<T> &out, const Pointer &ptrval,
+            const FileDatabase &db, const Field &f,
+            bool non_recursive = false) const;
 
     // --------------------------------------------------------
     template <template <typename> class TOUT, typename T>
-    bool ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
-        const FileDatabase& db, const Field& f, bool) const;
+    bool ResolvePointer(vector<TOUT<T>> &out, const Pointer &ptrval,
+            const FileDatabase &db, const Field &f, bool) const;
 
     // --------------------------------------------------------
-    bool ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval,
-        const FileDatabase& db, const Field& f, bool) const;
+    bool ResolvePointer(std::shared_ptr<FileOffset> &out, const Pointer &ptrval,
+            const FileDatabase &db, const Field &f, bool) const;
 
     // --------------------------------------------------------
-    inline const FileBlockHead* LocateFileBlockForAddress(
-        const Pointer & ptrval,
-        const FileDatabase& db) const;
+    inline const FileBlockHead *LocateFileBlockForAddress(
+            const Pointer &ptrval,
+            const FileDatabase &db) const;
 
 private:
-
     // ------------------------------------------------------------------------------
-    template <typename T> T* _allocate(std::shared_ptr<T>& out, size_t& s) const {
+    template <typename T>
+    T *_allocate(std::shared_ptr<T> &out, size_t &s) const {
         out = std::shared_ptr<T>(new T());
         s = 1;
         return out.get();
     }
 
-    template <typename T> T* _allocate(vector<T>& out, size_t& s) const {
+    template <typename T>
+    T *_allocate(vector<T> &out, size_t &s) const {
         out.resize(s);
         return s ? &out.front() : NULL;
     }
@@ -372,14 +367,14 @@ private:
     struct _defaultInitializer {
 
         template <typename T, unsigned int N>
-        void operator ()(T (& out)[N], const char* = NULL) {
+        void operator()(T (&out)[N], const char * = NULL) {
             for (unsigned int i = 0; i < N; ++i) {
                 out[i] = T();
             }
         }
 
         template <typename T, unsigned int N, unsigned int M>
-        void operator ()(T (& out)[N][M], const char* = NULL) {
+        void operator()(T (&out)[N][M], const char * = NULL) {
             for (unsigned int i = 0; i < N; ++i) {
                 for (unsigned int j = 0; j < M; ++j) {
                     out[i][j] = T();
@@ -388,21 +383,21 @@ private:
         }
 
         template <typename T>
-        void operator ()(T& out, const char* = NULL) {
+        void operator()(T &out, const char * = NULL) {
             out = T();
         }
     };
 
 private:
-
     mutable size_t cache_idx;
 };
 
 // --------------------------------------------------------
-template <>  struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
+template <>
+struct Structure ::_defaultInitializer<ErrorPolicy_Warn> {
 
     template <typename T>
-    void operator ()(T& out, const char* reason = "<add reason>") {
+    void operator()(T &out, const char *reason = "<add reason>") {
         ASSIMP_LOG_WARN(reason);
 
         // ... and let the show go on
@@ -410,10 +405,11 @@ template <>  struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
     }
 };
 
-template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
+template <>
+struct Structure ::_defaultInitializer<ErrorPolicy_Fail> {
 
     template <typename T>
-    void operator ()(T& /*out*/,const char* = "") {
+    void operator()(T & /*out*/, const char * = "") {
         // obviously, it is crucial that _DefaultInitializer is used
         // only from within a catch clause.
         throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
@@ -421,13 +417,12 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
 };
 
 // -------------------------------------------------------------------------------------------------------
-template <> inline bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shared_ptr<ElemBase>& out,
-    const Pointer & ptrval,
-    const FileDatabase& db,
-    const Field& f,
-    bool
-    ) const;
-
+template <>
+inline bool Structure ::ResolvePointer<std::shared_ptr, ElemBase>(std::shared_ptr<ElemBase> &out,
+        const Pointer &ptrval,
+        const FileDatabase &db,
+        const Field &f,
+        bool) const;
 
 // -------------------------------------------------------------------------------
 /** Represents the full data structure information for a single BLEND file.
@@ -435,40 +430,34 @@ template <> inline bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(st
  *  #DNAParser does the reading and represents currently the only place where
  *  DNA is altered.*/
 // -------------------------------------------------------------------------------
-class DNA
-{
+class DNA {
 public:
-
-    typedef void (Structure::*ConvertProcPtr) (
-        std::shared_ptr<ElemBase> in,
-        const FileDatabase&
-    ) const;
+    typedef void (Structure::*ConvertProcPtr)(
+            std::shared_ptr<ElemBase> in,
+            const FileDatabase &) const;
 
     typedef std::shared_ptr<ElemBase> (
-        Structure::*AllocProcPtr) () const;
+            Structure::*AllocProcPtr)() const;
 
-    typedef std::pair< AllocProcPtr, ConvertProcPtr > FactoryPair;
+    typedef std::pair<AllocProcPtr, ConvertProcPtr> FactoryPair;
 
 public:
-
-    std::map<std::string, FactoryPair > converters;
-    vector<Structure > structures;
+    std::map<std::string, FactoryPair> converters;
+    vector<Structure> structures;
     std::map<std::string, size_t> indices;
 
 public:
-
     // --------------------------------------------------------
     /** Access a structure by its canonical name, the pointer version returns NULL on failure
       * while the reference version raises an error. */
-    inline const Structure& operator [] (const std::string& ss) const;
-    inline const Structure* Get (const std::string& ss) const;
+    inline const Structure &operator[](const std::string &ss) const;
+    inline const Structure *Get(const std::string &ss) const;
 
     // --------------------------------------------------------
     /** Access a structure by its index */
-    inline const Structure& operator [] (const size_t i) const;
+    inline const Structure &operator[](const size_t i) const;
 
 public:
-
     // --------------------------------------------------------
     /** Add structure definitions for all the primitive types,
      *  i.e. integer, short, char, float */
@@ -483,7 +472,6 @@ public:
      *  known at compile time (consier Object::data).*/
     void RegisterConverters();
 
-
     // --------------------------------------------------------
     /** Take an input blob from the stream, interpret it according to
      *  a its structure name and convert it to the intermediate
@@ -491,10 +479,9 @@ public:
      *  @param structure Destination structure definition
      *  @param db File database.
      *  @return A null pointer if no appropriate converter is available.*/
-    std::shared_ptr< ElemBase > ConvertBlobToStructure(
-        const Structure& structure,
-        const FileDatabase& db
-        ) const;
+    std::shared_ptr<ElemBase> ConvertBlobToStructure(
+            const Structure &structure,
+            const FileDatabase &db) const;
 
     // --------------------------------------------------------
     /** Find a suitable conversion function for a given Structure.
@@ -505,10 +492,8 @@ public:
      *  @param db File database.
      *  @return A null pointer in .first if no appropriate converter is available.*/
     FactoryPair GetBlobToStructureConverter(
-        const Structure& structure,
-        const FileDatabase& db
-        ) const;
-
+            const Structure &structure,
+            const FileDatabase &db) const;
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
     // --------------------------------------------------------
@@ -527,25 +512,29 @@ public:
      *  @throw DeadlyImportError if more than 2 dimensions are
      *    encountered. */
     static void ExtractArraySize(
-        const std::string& out,
-        size_t array_sizes[2]
-    );
+            const std::string &out,
+            size_t array_sizes[2]);
 };
 
 // special converters for primitive types
-template <> inline void Structure :: Convert<int>       (int& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<short>     (short& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<char>      (char& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<float>     (float& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<double>    (double& dest,const FileDatabase& db) const;
-template <> inline void Structure :: Convert<Pointer>   (Pointer& dest,const FileDatabase& db) const;
+template <>
+inline void Structure ::Convert<int>(int &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<short>(short &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<char>(char &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<float>(float &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<double>(double &dest, const FileDatabase &db) const;
+template <>
+inline void Structure ::Convert<Pointer>(Pointer &dest, const FileDatabase &db) const;
 
 // -------------------------------------------------------------------------------
 /** Describes a master file block header. Each master file sections holds n
  *  elements of a certain SDNA structure (or otherwise unspecified data). */
 // -------------------------------------------------------------------------------
-struct FileBlockHead
-{
+struct FileBlockHead {
     // points right after the header of the file block
     StreamReaderAny::pos start;
 
@@ -561,66 +550,55 @@ struct FileBlockHead
     // number of structure instances to follow
     size_t num;
 
-
-
     // file blocks are sorted by address to quickly locate specific memory addresses
-    bool operator < (const FileBlockHead& o) const {
+    bool operator<(const FileBlockHead &o) const {
         return address.val < o.address.val;
     }
 
     // for std::upper_bound
-    operator const Pointer& () const {
+    operator const Pointer &() const {
         return address;
     }
 };
 
 // for std::upper_bound
-inline bool operator< (const Pointer& a, const Pointer& b) {
+inline bool operator<(const Pointer &a, const Pointer &b) {
     return a.val < b.val;
 }
 
 // -------------------------------------------------------------------------------
 /** Utility to read all master file blocks in turn. */
 // -------------------------------------------------------------------------------
-class SectionParser
-{
+class SectionParser {
 public:
-
     // --------------------------------------------------------
     /** @param stream Inout stream, must point to the
      *  first section in the file. Call Next() once
      *  to have it read.
      *  @param ptr64 Pointer size in file is 64 bits? */
-    SectionParser(StreamReaderAny& stream,bool ptr64)
-        : stream(stream)
-        , ptr64(ptr64)
-    {
+    SectionParser(StreamReaderAny &stream, bool ptr64) :
+            stream(stream), ptr64(ptr64) {
         current.size = current.start = 0;
     }
 
 public:
-
     // --------------------------------------------------------
-    const FileBlockHead& GetCurrent() const {
+    const FileBlockHead &GetCurrent() const {
         return current;
     }
 
-
 public:
-
     // --------------------------------------------------------
     /** Advance to the next section.
      *  @throw DeadlyImportError if the last chunk was passed. */
     void Next();
 
 public:
-
     FileBlockHead current;
-    StreamReaderAny& stream;
+    StreamReaderAny &stream;
     bool ptr64;
 };
 
-
 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
 // -------------------------------------------------------------------------------
 /** Import statistics, i.e. number of file blocks read*/
@@ -628,17 +606,13 @@ public:
 class Statistics {
 
 public:
-
-    Statistics ()
-        : fields_read       ()
-        , pointers_resolved ()
-        , cache_hits        ()
-//      , blocks_read       ()
-        , cached_objects    ()
-    {}
+    Statistics() :
+            fields_read(), pointers_resolved(), cache_hits()
+            //      , blocks_read       ()
+            ,
+            cached_objects() {}
 
 public:
-
     /** total number of fields we read */
     unsigned int fields_read;
 
@@ -662,17 +636,13 @@ public:
  *  avoids circular references and avoids object duplication. */
 // -------------------------------------------------------------------------------
 template <template <typename> class TOUT>
-class ObjectCache
-{
+class ObjectCache {
 public:
-
-    typedef std::map< Pointer, TOUT<ElemBase> > StructureCache;
+    typedef std::map<Pointer, TOUT<ElemBase>> StructureCache;
 
 public:
-
-    ObjectCache(const FileDatabase& db)
-        : db(db)
-    {
+    ObjectCache(const FileDatabase &db) :
+            db(db) {
         // currently there are only ~400 structure records per blend file.
         // we read only a small part of them and don't cache objects
         // which we don't need, so this should suffice.
@@ -680,17 +650,17 @@ public:
     }
 
 public:
-
     // --------------------------------------------------------
     /** Check whether a specific item is in the cache.
      *  @param s Data type of the item
      *  @param out Output pointer. Unchanged if the
      *   cache doesn't know the item yet.
      *  @param ptr Item address to look for. */
-    template <typename T> void get (
-        const Structure& s,
-        TOUT<T>& out,
-        const Pointer& ptr) const;
+    template <typename T>
+    void get(
+            const Structure &s,
+            TOUT<T> &out,
+            const Pointer &ptr) const;
 
     // --------------------------------------------------------
     /** Add an item to the cache after the item has
@@ -700,47 +670,44 @@ public:
      *  @param s Data type of the item
      *  @param out Item to insert into the cache
      *  @param ptr address (cache key) of the item. */
-    template <typename T> void set
-        (const Structure& s,
-        const TOUT<T>& out,
-        const Pointer& ptr);
+    template <typename T>
+    void set(const Structure &s,
+            const TOUT<T> &out,
+            const Pointer &ptr);
 
 private:
-
     mutable vector<StructureCache> caches;
-    const FileDatabase& db;
+    const FileDatabase &db;
 };
 
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
-template <> class ObjectCache<Blender::vector>
-{
+template <>
+class ObjectCache<Blender::vector> {
 public:
+    ObjectCache(const FileDatabase &) {}
 
-    ObjectCache(const FileDatabase&) {}
-
-    template <typename T> void get(const Structure&, vector<T>&, const Pointer&) {}
-    template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
+    template <typename T>
+    void get(const Structure &, vector<T> &, const Pointer &) {}
+    template <typename T>
+    void set(const Structure &, const vector<T> &, const Pointer &) {}
 };
 
 #ifdef _MSC_VER
-#   pragma warning(disable:4355)
+#pragma warning(disable : 4355)
 #endif
 
 // -------------------------------------------------------------------------------
 /** Memory representation of a full BLEND file and all its dependencies. The
  *  output aiScene is constructed from an instance of this data structure. */
 // -------------------------------------------------------------------------------
-class FileDatabase
-{
-    template <template <typename> class TOUT> friend class ObjectCache;
+class FileDatabase {
+    template <template <typename> class TOUT>
+    friend class ObjectCache;
 
 public:
-    FileDatabase()
-        : _cacheArrays(*this)
-        , _cache(*this)
-        , next_cache_idx()
-    {}
+    FileDatabase() :
+            _cacheArrays(*this), _cache(*this), next_cache_idx() {}
 
 public:
     // publicly accessible fields
@@ -748,12 +715,11 @@ public:
     bool little;
 
     DNA dna;
-    std::shared_ptr< StreamReaderAny > reader;
-    vector< FileBlockHead > entries;
+    std::shared_ptr<StreamReaderAny> reader;
+    vector<FileBlockHead> entries;
 
 public:
-
-    Statistics& stats() const {
+    Statistics &stats() const {
         return _stats;
     }
 
@@ -762,18 +728,16 @@ public:
     // arrays of objects are never cached because we can't easily
     // ensure their proper destruction.
     template <typename T>
-    ObjectCache<std::shared_ptr>& cache(std::shared_ptr<T>& /*in*/) const {
+    ObjectCache<std::shared_ptr> &cache(std::shared_ptr<T> & /*in*/) const {
         return _cache;
     }
 
     template <typename T>
-    ObjectCache<vector>& cache(vector<T>& /*in*/) const {
+    ObjectCache<vector> &cache(vector<T> & /*in*/) const {
         return _cacheArrays;
     }
 
 private:
-
-
 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
     mutable Statistics _stats;
 #endif
@@ -785,24 +749,20 @@ private:
 };
 
 #ifdef _MSC_VER
-#   pragma warning(default:4355)
+#pragma warning(default : 4355)
 #endif
 
 // -------------------------------------------------------------------------------
 /** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
 // -------------------------------------------------------------------------------
-class DNAParser
-{
+class DNAParser {
 
 public:
-
     /** Bind the parser to a empty DNA and an input stream */
-    DNAParser(FileDatabase& db)
-        : db(db)
-    {}
+    DNAParser(FileDatabase &db) :
+            db(db) {}
 
 public:
-
     // --------------------------------------------------------
     /** Locate the DNA in the file and parse it. The input
      *  stream is expected to point to the beginning of the DN1
@@ -811,18 +771,16 @@ public:
      *  @throw DeadlyImportError if the DNA cannot be read.
      *  @note The position of the stream pointer is undefined
      *    afterwards.*/
-    void Parse ();
+    void Parse();
 
 public:
-
     /** Obtain a reference to the extracted DNA information */
-    const Blender::DNA& GetDNA() const {
+    const Blender::DNA &GetDNA() const {
         return db.dna;
     }
 
 private:
-
-    FileDatabase& db;
+    FileDatabase &db;
 };
 
 /**
@@ -835,9 +793,8 @@ private:
 */
 bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db);
 
-
-    } // end Blend
-} // end Assimp
+} // namespace Blender
+} // namespace Assimp
 
 #include "BlenderDNA.inl"
 

+ 1 - 1
code/Blender/BlenderDNA.inl → code/AssetLib/Blender/BlenderDNA.inl

@@ -795,7 +795,7 @@ const Structure& DNA :: operator [] (const std::string& ss) const
 const Structure* DNA :: Get (const std::string& ss) const
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
-    return it == indices.end() ? NULL : &structures[(*it).second];
+    return it == indices.end() ? nullptr : &structures[(*it).second];
 }
 
 //--------------------------------------------------------------------------------

+ 0 - 0
code/Blender/BlenderIntermediate.h → code/AssetLib/Blender/BlenderIntermediate.h


+ 0 - 0
code/Blender/BlenderLoader.cpp → code/AssetLib/Blender/BlenderLoader.cpp


+ 0 - 0
code/Blender/BlenderLoader.h → code/AssetLib/Blender/BlenderLoader.h


+ 15 - 15
code/Blender/BlenderModifier.cpp → code/AssetLib/Blender/BlenderModifier.cpp

@@ -68,7 +68,7 @@ static const fpCreateModifier creators[] = {
     &god<BlenderModifier_Mirror>,
     &god<BlenderModifier_Subdivision>,
 
-    NULL // sentinel
+    nullptr // sentinel
 };
 
 // ------------------------------------------------------------------------------------------------
@@ -127,7 +127,7 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_d
                 modifier->DoIt(out, conv_data, *static_cast<const ElemBase *>(cur), in, orig_object);
                 cnt++;
 
-                curgod = NULL;
+                curgod = nullptr;
                 break;
             }
         }
@@ -229,7 +229,7 @@ void BlenderModifier_Mirror ::DoIt(aiNode &out, ConversionData &conv_data, const
 
         // Only reverse the winding order if an odd number of axes were mirrored.
         if (xs * ys * zs < 0) {
-            for (unsigned int j = 0; j < mesh->mNumFaces; ++j ) {
+            for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {
                 aiFace &face = mesh->mFaces[j];
                 for (unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
                     std::swap(face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
@@ -267,18 +267,18 @@ void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data,
 
     Subdivider::Algorithm algo;
     switch (mir.subdivType) {
-        case SubsurfModifierData::TYPE_CatmullClarke:
-            algo = Subdivider::CATMULL_CLARKE;
-            break;
-
-        case SubsurfModifierData::TYPE_Simple:
-            ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke");
-            algo = Subdivider::CATMULL_CLARKE;
-            break;
-
-        default:
-            ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType);
-            return;
+    case SubsurfModifierData::TYPE_CatmullClarke:
+        algo = Subdivider::CATMULL_CLARKE;
+        break;
+
+    case SubsurfModifierData::TYPE_Simple:
+        ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke");
+        algo = Subdivider::CATMULL_CLARKE;
+        break;
+
+    default:
+        ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType);
+        return;
     };
 
     std::unique_ptr<Subdivider> subd(Subdivider::Create(algo));

+ 0 - 0
code/Blender/BlenderModifier.h → code/AssetLib/Blender/BlenderModifier.h


+ 838 - 0
code/AssetLib/Blender/BlenderScene.cpp

@@ -0,0 +1,838 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderScene.cpp
+ *  @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderScene.h"
+#include "BlenderCustomData.h"
+#include "BlenderDNA.h"
+#include "BlenderSceneGen.h"
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Object>(
+        Object &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Object::Type>(temp);
+    ReadFieldArray2<ErrorPolicy_Warn>(dest.obmat, "obmat", db);
+    ReadFieldArray2<ErrorPolicy_Warn>(dest.parentinv, "parentinv", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.parsubstr, "parsubstr", db);
+    {
+        std::shared_ptr<Object> parent;
+        ReadFieldPtr<ErrorPolicy_Warn>(parent, "*parent", db);
+        dest.parent = parent.get();
+    }
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.track, "*track", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy, "*proxy", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_from, "*proxy_from", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_group, "*proxy_group", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.dup_group, "*dup_group", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.data, "*data", db);
+    ReadField<ErrorPolicy_Igno>(dest.modifiers, "modifiers", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Group>(
+        Group &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Igno>(dest.layer, "layer", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.gobject, "*gobject", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTex>(
+        MTex &dest,
+        const FileDatabase &db) const {
+
+    int temp_short = 0;
+    ReadField<ErrorPolicy_Igno>(temp_short, "mapto", db);
+    dest.mapto = static_cast<Assimp::Blender::MTex::MapType>(temp_short);
+    int temp = 0;
+    ReadField<ErrorPolicy_Igno>(temp, "blendtype", db);
+    dest.blendtype = static_cast<Assimp::Blender::MTex::BlendType>(temp);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.object, "*object", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.tex, "*tex", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.uvname, "uvname", db);
+    ReadField<ErrorPolicy_Igno>(temp, "projx", db);
+    dest.projx = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(temp, "projy", db);
+    dest.projy = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(temp, "projz", db);
+    dest.projz = static_cast<Assimp::Blender::MTex::Projection>(temp);
+    ReadField<ErrorPolicy_Igno>(dest.mapping, "mapping", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.ofs, "ofs", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.size, "size", db);
+    ReadField<ErrorPolicy_Igno>(dest.rot, "rot", db);
+    ReadField<ErrorPolicy_Igno>(dest.texflag, "texflag", db);
+    ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
+    ReadField<ErrorPolicy_Igno>(dest.pmapto, "pmapto", db);
+    ReadField<ErrorPolicy_Igno>(dest.pmaptoneg, "pmaptoneg", db);
+    ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
+    ReadField<ErrorPolicy_Igno>(dest.colspecfac, "colspecfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirrfac, "mirrfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.alphafac, "alphafac", db);
+    ReadField<ErrorPolicy_Igno>(dest.difffac, "difffac", db);
+    ReadField<ErrorPolicy_Igno>(dest.specfac, "specfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.emitfac, "emitfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.hardfac, "hardfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.norfac, "norfac", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<TFace>(
+        TFace &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
+    ReadFieldArray<ErrorPolicy_Fail>(dest.col, "col", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+    ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<SubsurfModifierData>(
+        SubsurfModifierData &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
+    ReadField<ErrorPolicy_Warn>(dest.subdivType, "subdivType", db);
+    ReadField<ErrorPolicy_Fail>(dest.levels, "levels", db);
+    ReadField<ErrorPolicy_Igno>(dest.renderLevels, "renderLevels", db);
+    ReadField<ErrorPolicy_Igno>(dest.flags, "flags", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MFace>(
+        MFace &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
+    ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
+    ReadField<ErrorPolicy_Fail>(dest.v3, "v3", db);
+    ReadField<ErrorPolicy_Fail>(dest.v4, "v4", db);
+    ReadField<ErrorPolicy_Fail>(dest.mat_nr, "mat_nr", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Lamp>(
+        Lamp &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Lamp::Type>(temp);
+    ReadField<ErrorPolicy_Igno>(dest.flags, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.colormodel, "colormodel", db);
+    ReadField<ErrorPolicy_Igno>(dest.totex, "totex", db);
+    ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Warn>(dest.k, "k", db);
+    ReadField<ErrorPolicy_Igno>(dest.energy, "energy", db);
+    ReadField<ErrorPolicy_Warn>(dest.dist, "dist", db);
+    ReadField<ErrorPolicy_Igno>(dest.spotsize, "spotsize", db);
+    ReadField<ErrorPolicy_Igno>(dest.spotblend, "spotblend", db);
+    ReadField<ErrorPolicy_Warn>(dest.constant_coefficient, "coeff_const", db);
+    ReadField<ErrorPolicy_Warn>(dest.linear_coefficient, "coeff_lin", db);
+    ReadField<ErrorPolicy_Warn>(dest.quadratic_coefficient, "coeff_quad", db);
+    ReadField<ErrorPolicy_Igno>(dest.att1, "att1", db);
+    ReadField<ErrorPolicy_Igno>(dest.att2, "att2", db);
+    ReadField<ErrorPolicy_Igno>(temp, "falloff_type", db);
+    dest.falloff_type = static_cast<Assimp::Blender::Lamp::FalloffType>(temp);
+    ReadField<ErrorPolicy_Igno>(dest.sun_brightness, "sun_brightness", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_size, "area_size", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_sizey, "area_sizey", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_sizez, "area_sizez", db);
+    ReadField<ErrorPolicy_Igno>(dest.area_shape, "area_shape", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MDeformWeight>(
+        MDeformWeight &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.def_nr, "def_nr", db);
+    ReadField<ErrorPolicy_Fail>(dest.weight, "weight", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<PackedFile>(
+        PackedFile &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Warn>(dest.size, "size", db);
+    ReadField<ErrorPolicy_Warn>(dest.seek, "seek", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.data, "*data", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Base>(
+        Base &dest,
+        const FileDatabase &db) const {
+    // note: as per https://github.com/assimp/assimp/issues/128,
+    // reading the Object linked list recursively is prone to stack overflow.
+    // This structure converter is therefore an hand-written exception that
+    // does it iteratively.
+
+    const int initial_pos = db.reader->GetCurrentPos();
+
+    std::pair<Base *, int> todo = std::make_pair(&dest, initial_pos);
+    for (;;) {
+
+        Base &cur_dest = *todo.first;
+        db.reader->SetCurrentPos(todo.second);
+
+        // we know that this is a double-linked, circular list which we never
+        // traverse backwards, so don't bother resolving the back links.
+        cur_dest.prev = nullptr;
+
+        ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.object, "*object", db);
+
+        // the return value of ReadFieldPtr indicates whether the object
+        // was already cached. In this case, we don't need to resolve
+        // it again.
+        if (!ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.next, "*next", db, true) && cur_dest.next) {
+            todo = std::make_pair(&*cur_dest.next, db.reader->GetCurrentPos());
+            continue;
+        }
+        break;
+    }
+
+    db.reader->SetCurrentPos(initial_pos + size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTFace>(
+        MTFace &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray2<ErrorPolicy_Fail>(dest.uv, "uv", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+    ReadField<ErrorPolicy_Igno>(dest.unwrap, "unwrap", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Material>(
+        Material &dest,
+        const FileDatabase &db) const {
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Warn>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Warn>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Warn>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Warn>(dest.specr, "specr", db);
+    ReadField<ErrorPolicy_Warn>(dest.specg, "specg", db);
+    ReadField<ErrorPolicy_Warn>(dest.specb, "specb", db);
+    ReadField<ErrorPolicy_Igno>(dest.har, "har", db);
+    ReadField<ErrorPolicy_Warn>(dest.ambr, "ambr", db);
+    ReadField<ErrorPolicy_Warn>(dest.ambg, "ambg", db);
+    ReadField<ErrorPolicy_Warn>(dest.ambb, "ambb", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirr, "mirr", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirg, "mirg", db);
+    ReadField<ErrorPolicy_Igno>(dest.mirb, "mirb", db);
+    ReadField<ErrorPolicy_Warn>(dest.emit, "emit", db);
+    ReadField<ErrorPolicy_Igno>(dest.ray_mirror, "ray_mirror", db);
+    ReadField<ErrorPolicy_Warn>(dest.alpha, "alpha", db);
+    ReadField<ErrorPolicy_Igno>(dest.ref, "ref", db);
+    ReadField<ErrorPolicy_Igno>(dest.translucency, "translucency", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.roughness, "roughness", db);
+    ReadField<ErrorPolicy_Igno>(dest.darkness, "darkness", db);
+    ReadField<ErrorPolicy_Igno>(dest.refrac, "refrac", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.group, "*group", db);
+    ReadField<ErrorPolicy_Warn>(dest.diff_shader, "diff_shader", db);
+    ReadField<ErrorPolicy_Warn>(dest.spec_shader, "spec_shader", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mtex, "*mtex", db);
+
+    ReadField<ErrorPolicy_Igno>(dest.amb, "amb", db);
+    ReadField<ErrorPolicy_Igno>(dest.ang, "ang", db);
+    ReadField<ErrorPolicy_Igno>(dest.spectra, "spectra", db);
+    ReadField<ErrorPolicy_Igno>(dest.spec, "spec", db);
+    ReadField<ErrorPolicy_Igno>(dest.zoffs, "zoffs", db);
+    ReadField<ErrorPolicy_Igno>(dest.add, "add", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_mir, "fresnel_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_mir_i, "fresnel_mir_i", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_tra, "fresnel_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.fresnel_tra_i, "fresnel_tra_i", db);
+    ReadField<ErrorPolicy_Igno>(dest.filter, "filter", db);
+    ReadField<ErrorPolicy_Igno>(dest.tx_limit, "tx_limit", db);
+    ReadField<ErrorPolicy_Igno>(dest.tx_falloff, "tx_falloff", db);
+    ReadField<ErrorPolicy_Igno>(dest.gloss_mir, "gloss_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.gloss_tra, "gloss_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_mir, "adapt_thresh_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.adapt_thresh_tra, "adapt_thresh_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.aniso_gloss_mir, "aniso_gloss_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.dist_mir, "dist_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.hasize, "hasize", db);
+    ReadField<ErrorPolicy_Igno>(dest.flaresize, "flaresize", db);
+    ReadField<ErrorPolicy_Igno>(dest.subsize, "subsize", db);
+    ReadField<ErrorPolicy_Igno>(dest.flareboost, "flareboost", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_sta, "strand_sta", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_end, "strand_end", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_ease, "strand_ease", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_surfnor, "strand_surfnor", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_min, "strand_min", db);
+    ReadField<ErrorPolicy_Igno>(dest.strand_widthfade, "strand_widthfade", db);
+    ReadField<ErrorPolicy_Igno>(dest.sbias, "sbias", db);
+    ReadField<ErrorPolicy_Igno>(dest.lbias, "lbias", db);
+    ReadField<ErrorPolicy_Igno>(dest.shad_alpha, "shad_alpha", db);
+    ReadField<ErrorPolicy_Igno>(dest.param, "param", db);
+    ReadField<ErrorPolicy_Igno>(dest.rms, "rms", db);
+    ReadField<ErrorPolicy_Igno>(dest.rampfac_col, "rampfac_col", db);
+    ReadField<ErrorPolicy_Igno>(dest.rampfac_spec, "rampfac_spec", db);
+    ReadField<ErrorPolicy_Igno>(dest.friction, "friction", db);
+    ReadField<ErrorPolicy_Igno>(dest.fh, "fh", db);
+    ReadField<ErrorPolicy_Igno>(dest.reflect, "reflect", db);
+    ReadField<ErrorPolicy_Igno>(dest.fhdist, "fhdist", db);
+    ReadField<ErrorPolicy_Igno>(dest.xyfrict, "xyfrict", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_radius, "sss_radius", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_col, "sss_col", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_error, "sss_error", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_scale, "sss_scale", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_ior, "sss_ior", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_colfac, "sss_colfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_texfac, "sss_texfac", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_front, "sss_front", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_back, "sss_back", db);
+
+    ReadField<ErrorPolicy_Igno>(dest.material_type, "material_type", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.ray_depth, "ray_depth", db);
+    ReadField<ErrorPolicy_Igno>(dest.ray_depth_tra, "ray_depth_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.samp_gloss_mir, "samp_gloss_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.samp_gloss_tra, "samp_gloss_tra", db);
+    ReadField<ErrorPolicy_Igno>(dest.fadeto_mir, "fadeto_mir", db);
+    ReadField<ErrorPolicy_Igno>(dest.shade_flag, "shade_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.flarec, "flarec", db);
+    ReadField<ErrorPolicy_Igno>(dest.starc, "starc", db);
+    ReadField<ErrorPolicy_Igno>(dest.linec, "linec", db);
+    ReadField<ErrorPolicy_Igno>(dest.ringc, "ringc", db);
+    ReadField<ErrorPolicy_Igno>(dest.pr_lamp, "pr_lamp", db);
+    ReadField<ErrorPolicy_Igno>(dest.pr_texture, "pr_texture", db);
+    ReadField<ErrorPolicy_Igno>(dest.ml_flag, "ml_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.diff_shader, "diff_shader", db);
+    ReadField<ErrorPolicy_Igno>(dest.spec_shader, "spec_shader", db);
+    ReadField<ErrorPolicy_Igno>(dest.texco, "texco", db);
+    ReadField<ErrorPolicy_Igno>(dest.mapto, "mapto", db);
+    ReadField<ErrorPolicy_Igno>(dest.ramp_show, "ramp_show", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad3, "pad3", db);
+    ReadField<ErrorPolicy_Igno>(dest.dynamode, "dynamode", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad2, "pad2", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_flag, "sss_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.sss_preset, "sss_preset", db);
+    ReadField<ErrorPolicy_Igno>(dest.shadowonly_flag, "shadowonly_flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.index, "index", db);
+    ReadField<ErrorPolicy_Igno>(dest.vcol_alpha, "vcol_alpha", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad4, "pad4", db);
+
+    ReadField<ErrorPolicy_Igno>(dest.seed1, "seed1", db);
+    ReadField<ErrorPolicy_Igno>(dest.seed2, "seed2", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MTexPoly>(
+        MTexPoly &dest,
+        const FileDatabase &db) const {
+
+    {
+        std::shared_ptr<Image> tpage;
+        ReadFieldPtr<ErrorPolicy_Igno>(tpage, "*tpage", db);
+        dest.tpage = tpage.get();
+    }
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.transp, "transp", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadField<ErrorPolicy_Igno>(dest.tile, "tile", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Mesh>(
+        Mesh &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Fail>(dest.totface, "totface", db);
+    ReadField<ErrorPolicy_Fail>(dest.totedge, "totedge", db);
+    ReadField<ErrorPolicy_Fail>(dest.totvert, "totvert", db);
+    ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
+    ReadField<ErrorPolicy_Igno>(dest.totpoly, "totpoly", db);
+    ReadField<ErrorPolicy_Igno>(dest.subdiv, "subdiv", db);
+    ReadField<ErrorPolicy_Igno>(dest.subdivr, "subdivr", db);
+    ReadField<ErrorPolicy_Igno>(dest.subsurftype, "subsurftype", db);
+    ReadField<ErrorPolicy_Igno>(dest.smoothresh, "smoothresh", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.mface, "*mface", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mtface, "*mtface", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.tface, "*tface", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.mvert, "*mvert", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.medge, "*medge", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloop, "*mloop", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopuv, "*mloopuv", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopcol, "*mloopcol", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mpoly, "*mpoly", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mtpoly, "*mtpoly", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.dvert, "*dvert", db);
+    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);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MDeformVert>(
+        MDeformVert &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.dw, "*dw", db);
+    ReadField<ErrorPolicy_Igno>(dest.totweight, "totweight", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<World>(
+        World &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoopCol>(
+        MLoopCol &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Igno>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Igno>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Igno>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Igno>(dest.a, "a", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MVert>(
+        MVert &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray<ErrorPolicy_Fail>(dest.co, "co", db);
+    ReadFieldArray<ErrorPolicy_Fail>(dest.no, "no", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    //ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
+    ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MEdge>(
+        MEdge &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.v1, "v1", db);
+    ReadField<ErrorPolicy_Fail>(dest.v2, "v2", db);
+    ReadField<ErrorPolicy_Igno>(dest.crease, "crease", db);
+    ReadField<ErrorPolicy_Igno>(dest.bweight, "bweight", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoopUV>(
+        MLoopUV &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray<ErrorPolicy_Igno>(dest.uv, "uv", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<GroupObject>(
+        GroupObject &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.prev, "*prev", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.ob, "*ob", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ListBase>(
+        ListBase &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.first, "*first", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.last, "*last", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MLoop>(
+        MLoop &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Igno>(dest.v, "v", db);
+    ReadField<ErrorPolicy_Igno>(dest.e, "e", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ModifierData>(
+        ModifierData &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.next, "*next", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.prev, "*prev", db);
+    ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
+    ReadField<ErrorPolicy_Igno>(dest.mode, "mode", db);
+    ReadFieldArray<ErrorPolicy_Igno>(dest.name, "name", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<ID>(
+        ID &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MCol>(
+        MCol &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.r, "r", db);
+    ReadField<ErrorPolicy_Fail>(dest.g, "g", db);
+    ReadField<ErrorPolicy_Fail>(dest.b, "b", db);
+    ReadField<ErrorPolicy_Fail>(dest.a, "a", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MPoly>(
+        MPoly &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Igno>(dest.loopstart, "loopstart", db);
+    ReadField<ErrorPolicy_Igno>(dest.totloop, "totloop", db);
+    ReadField<ErrorPolicy_Igno>(dest.mat_nr, "mat_nr", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Scene>(
+        Scene &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.camera, "*camera", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.world, "*world", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.basact, "*basact", db);
+    ReadField<ErrorPolicy_Igno>(dest.base, "base", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Library>(
+        Library &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadFieldArray<ErrorPolicy_Fail>(dest.filename, "filename", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.parent, "*parent", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Tex>(
+        Tex &dest,
+        const FileDatabase &db) const {
+    short temp_short = 0;
+    ReadField<ErrorPolicy_Igno>(temp_short, "imaflag", db);
+    dest.imaflag = static_cast<Assimp::Blender::Tex::ImageFlags>(temp_short);
+    int temp = 0;
+    ReadField<ErrorPolicy_Fail>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Tex::Type>(temp);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.ima, "*ima", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Camera>(
+        Camera &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    int temp = 0;
+    ReadField<ErrorPolicy_Warn>(temp, "type", db);
+    dest.type = static_cast<Assimp::Blender::Camera::Type>(temp);
+    ReadField<ErrorPolicy_Warn>(temp, "flag", db);
+    dest.flag = static_cast<Assimp::Blender::Camera::Type>(temp);
+    ReadField<ErrorPolicy_Warn>(dest.lens, "lens", db);
+    ReadField<ErrorPolicy_Warn>(dest.sensor_x, "sensor_x", db);
+    ReadField<ErrorPolicy_Igno>(dest.clipsta, "clipsta", db);
+    ReadField<ErrorPolicy_Igno>(dest.clipend, "clipend", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<MirrorModifierData>(
+        MirrorModifierData &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.modifier, "modifier", db);
+    ReadField<ErrorPolicy_Igno>(dest.axis, "axis", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.tolerance, "tolerance", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mirror_ob, "*mirror_ob", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure ::Convert<Image>(
+        Image &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadFieldArray<ErrorPolicy_Warn>(dest.name, "name", db);
+    ReadField<ErrorPolicy_Igno>(dest.ok, "ok", db);
+    ReadField<ErrorPolicy_Igno>(dest.flag, "flag", db);
+    ReadField<ErrorPolicy_Igno>(dest.source, "source", db);
+    ReadField<ErrorPolicy_Igno>(dest.type, "type", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad, "pad", db);
+    ReadField<ErrorPolicy_Igno>(dest.pad1, "pad1", db);
+    ReadField<ErrorPolicy_Igno>(dest.lastframe, "lastframe", db);
+    ReadField<ErrorPolicy_Igno>(dest.tpageflag, "tpageflag", db);
+    ReadField<ErrorPolicy_Igno>(dest.totbind, "totbind", db);
+    ReadField<ErrorPolicy_Igno>(dest.xrep, "xrep", db);
+    ReadField<ErrorPolicy_Igno>(dest.yrep, "yrep", db);
+    ReadField<ErrorPolicy_Igno>(dest.twsta, "twsta", db);
+    ReadField<ErrorPolicy_Igno>(dest.twend, "twend", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.packedfile, "*packedfile", db);
+    ReadField<ErrorPolicy_Igno>(dest.lastupdate, "lastupdate", db);
+    ReadField<ErrorPolicy_Igno>(dest.lastused, "lastused", db);
+    ReadField<ErrorPolicy_Igno>(dest.animspeed, "animspeed", db);
+    ReadField<ErrorPolicy_Igno>(dest.gen_x, "gen_x", db);
+    ReadField<ErrorPolicy_Igno>(dest.gen_y, "gen_y", db);
+    ReadField<ErrorPolicy_Igno>(dest.gen_type, "gen_type", db);
+
+    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() {
+
+    converters["Object"] = DNA::FactoryPair(&Structure::Allocate<Object>, &Structure::Convert<Object>);
+    converters["Group"] = DNA::FactoryPair(&Structure::Allocate<Group>, &Structure::Convert<Group>);
+    converters["MTex"] = DNA::FactoryPair(&Structure::Allocate<MTex>, &Structure::Convert<MTex>);
+    converters["TFace"] = DNA::FactoryPair(&Structure::Allocate<TFace>, &Structure::Convert<TFace>);
+    converters["SubsurfModifierData"] = DNA::FactoryPair(&Structure::Allocate<SubsurfModifierData>, &Structure::Convert<SubsurfModifierData>);
+    converters["MFace"] = DNA::FactoryPair(&Structure::Allocate<MFace>, &Structure::Convert<MFace>);
+    converters["Lamp"] = DNA::FactoryPair(&Structure::Allocate<Lamp>, &Structure::Convert<Lamp>);
+    converters["MDeformWeight"] = DNA::FactoryPair(&Structure::Allocate<MDeformWeight>, &Structure::Convert<MDeformWeight>);
+    converters["PackedFile"] = DNA::FactoryPair(&Structure::Allocate<PackedFile>, &Structure::Convert<PackedFile>);
+    converters["Base"] = DNA::FactoryPair(&Structure::Allocate<Base>, &Structure::Convert<Base>);
+    converters["MTFace"] = DNA::FactoryPair(&Structure::Allocate<MTFace>, &Structure::Convert<MTFace>);
+    converters["Material"] = DNA::FactoryPair(&Structure::Allocate<Material>, &Structure::Convert<Material>);
+    converters["MTexPoly"] = DNA::FactoryPair(&Structure::Allocate<MTexPoly>, &Structure::Convert<MTexPoly>);
+    converters["Mesh"] = DNA::FactoryPair(&Structure::Allocate<Mesh>, &Structure::Convert<Mesh>);
+    converters["MDeformVert"] = DNA::FactoryPair(&Structure::Allocate<MDeformVert>, &Structure::Convert<MDeformVert>);
+    converters["World"] = DNA::FactoryPair(&Structure::Allocate<World>, &Structure::Convert<World>);
+    converters["MLoopCol"] = DNA::FactoryPair(&Structure::Allocate<MLoopCol>, &Structure::Convert<MLoopCol>);
+    converters["MVert"] = DNA::FactoryPair(&Structure::Allocate<MVert>, &Structure::Convert<MVert>);
+    converters["MEdge"] = DNA::FactoryPair(&Structure::Allocate<MEdge>, &Structure::Convert<MEdge>);
+    converters["MLoopUV"] = DNA::FactoryPair(&Structure::Allocate<MLoopUV>, &Structure::Convert<MLoopUV>);
+    converters["GroupObject"] = DNA::FactoryPair(&Structure::Allocate<GroupObject>, &Structure::Convert<GroupObject>);
+    converters["ListBase"] = DNA::FactoryPair(&Structure::Allocate<ListBase>, &Structure::Convert<ListBase>);
+    converters["MLoop"] = DNA::FactoryPair(&Structure::Allocate<MLoop>, &Structure::Convert<MLoop>);
+    converters["ModifierData"] = DNA::FactoryPair(&Structure::Allocate<ModifierData>, &Structure::Convert<ModifierData>);
+    converters["ID"] = DNA::FactoryPair(&Structure::Allocate<ID>, &Structure::Convert<ID>);
+    converters["MCol"] = DNA::FactoryPair(&Structure::Allocate<MCol>, &Structure::Convert<MCol>);
+    converters["MPoly"] = DNA::FactoryPair(&Structure::Allocate<MPoly>, &Structure::Convert<MPoly>);
+    converters["Scene"] = DNA::FactoryPair(&Structure::Allocate<Scene>, &Structure::Convert<Scene>);
+    converters["Library"] = DNA::FactoryPair(&Structure::Allocate<Library>, &Structure::Convert<Library>);
+    converters["Tex"] = DNA::FactoryPair(&Structure::Allocate<Tex>, &Structure::Convert<Tex>);
+    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

+ 252 - 277
code/Blender/BlenderScene.h → code/AssetLib/Blender/BlenderScene.h

@@ -48,14 +48,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "BlenderDNA.h"
 
-namespace Assimp    {
+namespace Assimp {
 namespace Blender {
 
 // Minor parts of this file are extracts from blender data structures,
 // declared in the ./source/blender/makesdna directory.
 // Stuff that is not used by Assimp is commented.
 
-
 // NOTE
 // this file serves as input data to the `./scripts/genblenddna.py`
 // script. This script generates the actual binding code to read a
@@ -95,15 +94,15 @@ namespace Blender {
 
 // warn if field is missing, substitute default value
 #ifdef WARN
-#  undef WARN
+#undef WARN
 #endif
-#define WARN 
+#define WARN
 
 // fail the import if the field does not exist
 #ifdef FAIL
-#  undef FAIL
+#undef FAIL
 #endif
-#define FAIL 
+#define FAIL
 
 struct Object;
 struct MTex;
@@ -117,7 +116,7 @@ static const size_t MaxNameLen = 1024;
 
 // -------------------------------------------------------------------------------
 struct ID : ElemBase {
-    char name[ MaxNameLen ] WARN;
+    char name[MaxNameLen] WARN;
     short flag;
 };
 
@@ -127,17 +126,16 @@ struct ListBase : ElemBase {
     std::shared_ptr<ElemBase> last;
 };
 
-
 // -------------------------------------------------------------------------------
 struct PackedFile : ElemBase {
-     int size WARN;
-     int seek WARN;
-     std::shared_ptr< FileOffset > data WARN;
+    int size WARN;
+    int seek WARN;
+    std::shared_ptr<FileOffset> data WARN;
 };
 
 // -------------------------------------------------------------------------------
 struct GroupObject : ElemBase {
-    std::shared_ptr<GroupObject> prev,next FAIL;
+    std::shared_ptr<GroupObject> prev, next FAIL;
     std::shared_ptr<Object> ob;
 };
 
@@ -157,23 +155,20 @@ struct World : ElemBase {
 // -------------------------------------------------------------------------------
 struct MVert : ElemBase {
     float co[3] FAIL;
-    float no[3] FAIL;       // readed as short and divided through / 32767.f
+    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)
-    {}
+    MVert() :
+            ElemBase(), flag(0), mat_nr(0), bweight(0) {}
 };
 
 // -------------------------------------------------------------------------------
 struct MEdge : ElemBase {
-      int v1, v2 FAIL;
-      char crease, bweight;
-      short flag;
+    int v1, v2 FAIL;
+    char crease, bweight;
+    short flag;
 };
 
 // -------------------------------------------------------------------------------
@@ -190,7 +185,7 @@ struct MLoopUV : ElemBase {
 // -------------------------------------------------------------------------------
 // Note that red and blue are not swapped, as with MCol
 struct MLoopCol : ElemBase {
-	unsigned char r, g, b, a;
+    unsigned char r, g, b, a;
 };
 
 // -------------------------------------------------------------------------------
@@ -203,19 +198,19 @@ struct MPoly : ElemBase {
 
 // -------------------------------------------------------------------------------
 struct MTexPoly : ElemBase {
-    Image* tpage;
+    Image *tpage;
     char flag, transp;
     short mode, tile, pad;
 };
 
 // -------------------------------------------------------------------------------
 struct MCol : ElemBase {
-    char r,g,b,a FAIL;
+    char r, g, b, a FAIL;
 };
 
 // -------------------------------------------------------------------------------
 struct MFace : ElemBase {
-    int v1,v2,v3,v4 FAIL;
+    int v1, v2, v3, v4 FAIL;
     int mat_nr FAIL;
     char flag;
 };
@@ -232,13 +227,9 @@ struct TFace : ElemBase {
 
 // -------------------------------------------------------------------------------
 struct MTFace : ElemBase {
-	MTFace()
-	: flag(0)
-	, mode(0)
-	, tile(0)
-	, unwrap(0)
-	{
-	}
+    MTFace() :
+            flag(0), mode(0), tile(0), unwrap(0) {
+    }
 
     float uv[4][2] FAIL;
     char flag;
@@ -250,31 +241,31 @@ struct MTFace : ElemBase {
 };
 
 // -------------------------------------------------------------------------------
-struct MDeformWeight : ElemBase  {
-      int    def_nr FAIL;
-      float  weight FAIL;
+struct MDeformWeight : ElemBase {
+    int def_nr FAIL;
+    float weight FAIL;
 };
 
 // -------------------------------------------------------------------------------
-struct MDeformVert : ElemBase  {
+struct MDeformVert : ElemBase {
     vector<MDeformWeight> dw WARN;
     int totweight;
 };
 
 // -------------------------------------------------------------------------------
-#define MA_RAYMIRROR    0x40000
+#define MA_RAYMIRROR 0x40000
 #define MA_TRANSPARENCY 0x10000
-#define MA_RAYTRANSP    0x20000
-#define MA_ZTRANSP      0x00040
+#define MA_RAYTRANSP 0x20000
+#define MA_ZTRANSP 0x00040
 
 struct Material : ElemBase {
     ID id FAIL;
 
-    float r,g,b WARN;
-    float specr,specg,specb WARN;
+    float r, g, b WARN;
+    float specr, specg, specb WARN;
     short har;
-    float ambr,ambg,ambb WARN;
-    float mirr,mirg,mirb;
+    float ambr, ambg, ambb WARN;
+    float mirr, mirg, mirb;
     float emit WARN;
     float ray_mirror;
     float alpha WARN;
@@ -399,20 +390,19 @@ struct CustomDataLayer : ElemBase {
     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)
-    {
+    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);
     }
 };
@@ -430,8 +420,8 @@ CustomData 208
     CustomDataExternal *external 200 8
 */
 struct CustomData : ElemBase {
-    vector<std::shared_ptr<struct CustomDataLayer> > layers;
-    int typemap[42];    // CD_NUMTYPES
+    vector<std::shared_ptr<struct CustomDataLayer>> layers;
+    int typemap[42]; // CD_NUMTYPES
     int totlayer;
     int maxlayer;
     int totsize;
@@ -469,7 +459,7 @@ struct Mesh : ElemBase {
     vector<MDeformVert> dvert;
     vector<MCol> mcol;
 
-    vector< std::shared_ptr<Material> > mat FAIL;
+    vector<std::shared_ptr<Material>> mat FAIL;
 
     struct CustomData vdata;
     struct CustomData edata;
@@ -490,142 +480,141 @@ struct Library : ElemBase {
 // -------------------------------------------------------------------------------
 struct Camera : ElemBase {
     enum Type {
-          Type_PERSP    =   0
-         ,Type_ORTHO    =   1
+        Type_PERSP = 0,
+        Type_ORTHO = 1
     };
 
     ID id FAIL;
 
-    Type type,flag WARN;
+    Type type, flag WARN;
     float lens WARN;
     float sensor_x WARN;
     float clipsta, clipend;
 };
 
-
 // -------------------------------------------------------------------------------
 struct Lamp : ElemBase {
 
     enum FalloffType {
-         FalloffType_Constant   = 0x0
-        ,FalloffType_InvLinear  = 0x1
-        ,FalloffType_InvSquare  = 0x2
+        FalloffType_Constant = 0x0,
+        FalloffType_InvLinear = 0x1,
+        FalloffType_InvSquare = 0x2
         //,FalloffType_Curve    = 0x3
         //,FalloffType_Sliders  = 0x4
     };
 
     enum Type {
-         Type_Local         = 0x0
-        ,Type_Sun           = 0x1
-        ,Type_Spot          = 0x2
-        ,Type_Hemi          = 0x3
-        ,Type_Area          = 0x4
+        Type_Local = 0x0,
+        Type_Sun = 0x1,
+        Type_Spot = 0x2,
+        Type_Hemi = 0x3,
+        Type_Area = 0x4
         //,Type_YFPhoton    = 0x5
     };
 
-      ID id FAIL;
-      //AnimData *adt;
+    ID id FAIL;
+    //AnimData *adt;
 
-      Type type FAIL;
-      short flags;
+    Type type FAIL;
+    short flags;
 
-      //int mode;
+    //int mode;
 
-      short colormodel, totex;
-      float r,g,b,k WARN;
-      //float shdwr, shdwg, shdwb;
+    short colormodel, totex;
+    float r, g, b, k WARN;
+    //float shdwr, shdwg, shdwb;
 
-      float energy, dist, spotsize, spotblend;
-      //float haint;
+    float energy, dist, spotsize, spotblend;
+    //float haint;
 
-      float constant_coefficient;
-      float linear_coefficient;
-      float quadratic_coefficient;
+    float constant_coefficient;
+    float linear_coefficient;
+    float quadratic_coefficient;
 
-      float att1, att2;
-      //struct CurveMapping *curfalloff;
-      FalloffType falloff_type;
+    float att1, att2;
+    //struct CurveMapping *curfalloff;
+    FalloffType falloff_type;
 
-      //float clipsta, clipend, shadspotsize;
-      //float bias, soft, compressthresh;
-      //short bufsize, samp, buffers, filtertype;
-      //char bufflag, buftype;
+    //float clipsta, clipend, shadspotsize;
+    //float bias, soft, compressthresh;
+    //short bufsize, samp, buffers, filtertype;
+    //char bufflag, buftype;
 
-      //short ray_samp, ray_sampy, ray_sampz;
-      //short ray_samp_type;
-      short area_shape;
-      float area_size, area_sizey, area_sizez;
-      //float adapt_thresh;
-      //short ray_samp_method;
+    //short ray_samp, ray_sampy, ray_sampz;
+    //short ray_samp_type;
+    short area_shape;
+    float area_size, area_sizey, area_sizez;
+    //float adapt_thresh;
+    //short ray_samp_method;
 
-      //short texact, shadhalostep;
+    //short texact, shadhalostep;
 
-      //short sun_effect_type;
-      //short skyblendtype;
-      //float horizon_brightness;
-      //float spread;
-      float sun_brightness;
-      //float sun_size;
-      //float backscattered_light;
-      //float sun_intensity;
-      //float atm_turbidity;
-      //float atm_inscattering_factor;
-      //float atm_extinction_factor;
-      //float atm_distance_factor;
-      //float skyblendfac;
-      //float sky_exposure;
-      //short sky_colorspace;
+    //short sun_effect_type;
+    //short skyblendtype;
+    //float horizon_brightness;
+    //float spread;
+    float sun_brightness;
+    //float sun_size;
+    //float backscattered_light;
+    //float sun_intensity;
+    //float atm_turbidity;
+    //float atm_inscattering_factor;
+    //float atm_extinction_factor;
+    //float atm_distance_factor;
+    //float skyblendfac;
+    //float sky_exposure;
+    //short sky_colorspace;
 
-      // int YF_numphotons, YF_numsearch;
-      // short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
-      // float YF_causticblur, YF_ltradius;
+    // int YF_numphotons, YF_numsearch;
+    // short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
+    // float YF_causticblur, YF_ltradius;
 
-      // float YF_glowint, YF_glowofs;
-      // short YF_glowtype, YF_pad2;
+    // float YF_glowint, YF_glowofs;
+    // short YF_glowtype, YF_pad2;
 
-      //struct Ipo *ipo;
-      //struct MTex *mtex[18];
-      // short pr_texture;
+    //struct Ipo *ipo;
+    //struct MTex *mtex[18];
+    // short pr_texture;
 
-      //struct PreviewImage *preview;
+    //struct PreviewImage *preview;
 };
 
 // -------------------------------------------------------------------------------
-struct ModifierData : ElemBase  {
+struct ModifierData : ElemBase {
     enum ModifierType {
-      eModifierType_None = 0,
-      eModifierType_Subsurf,
-      eModifierType_Lattice,
-      eModifierType_Curve,
-      eModifierType_Build,
-      eModifierType_Mirror,
-      eModifierType_Decimate,
-      eModifierType_Wave,
-      eModifierType_Armature,
-      eModifierType_Hook,
-      eModifierType_Softbody,
-      eModifierType_Boolean,
-      eModifierType_Array,
-      eModifierType_EdgeSplit,
-      eModifierType_Displace,
-      eModifierType_UVProject,
-      eModifierType_Smooth,
-      eModifierType_Cast,
-      eModifierType_MeshDeform,
-      eModifierType_ParticleSystem,
-      eModifierType_ParticleInstance,
-      eModifierType_Explode,
-      eModifierType_Cloth,
-      eModifierType_Collision,
-      eModifierType_Bevel,
-      eModifierType_Shrinkwrap,
-      eModifierType_Fluidsim,
-      eModifierType_Mask,
-      eModifierType_SimpleDeform,
-      eModifierType_Multires,
-      eModifierType_Surface,
-      eModifierType_Smoke,
-      eModifierType_ShapeKey
+        eModifierType_None = 0,
+        eModifierType_Subsurf,
+        eModifierType_Lattice,
+        eModifierType_Curve,
+        eModifierType_Build,
+        eModifierType_Mirror,
+        eModifierType_Decimate,
+        eModifierType_Wave,
+        eModifierType_Armature,
+        eModifierType_Hook,
+        eModifierType_Softbody,
+        eModifierType_Boolean,
+        eModifierType_Array,
+        eModifierType_EdgeSplit,
+        eModifierType_Displace,
+        eModifierType_UVProject,
+        eModifierType_Smooth,
+        eModifierType_Cast,
+        eModifierType_MeshDeform,
+        eModifierType_ParticleSystem,
+        eModifierType_ParticleInstance,
+        eModifierType_Explode,
+        eModifierType_Cloth,
+        eModifierType_Collision,
+        eModifierType_Bevel,
+        eModifierType_Shrinkwrap,
+        eModifierType_Fluidsim,
+        eModifierType_Mask,
+        eModifierType_SimpleDeform,
+        eModifierType_Multires,
+        eModifierType_Surface,
+        eModifierType_Smoke,
+        eModifierType_ShapeKey
     };
 
     std::shared_ptr<ElemBase> next WARN;
@@ -636,7 +625,7 @@ struct ModifierData : ElemBase  {
 };
 
 // -------------------------------------------------------------------------------
-struct SubsurfModifierData : ElemBase  {
+struct SubsurfModifierData : ElemBase {
 
     enum Type {
 
@@ -646,13 +635,13 @@ struct SubsurfModifierData : ElemBase  {
 
     enum Flags {
         // some omitted
-        FLAGS_SubsurfUV     =1<<3
+        FLAGS_SubsurfUV = 1 << 3
     };
 
     ModifierData modifier FAIL;
     short subdivType WARN;
     short levels FAIL;
-    short renderLevels ;
+    short renderLevels;
     short flags;
 };
 
@@ -660,13 +649,13 @@ struct SubsurfModifierData : ElemBase  {
 struct MirrorModifierData : ElemBase {
 
     enum Flags {
-        Flags_CLIPPING      =1<<0,
-        Flags_MIRROR_U      =1<<1,
-        Flags_MIRROR_V      =1<<2,
-        Flags_AXIS_X        =1<<3,
-        Flags_AXIS_Y        =1<<4,
-        Flags_AXIS_Z        =1<<5,
-        Flags_VGROUP        =1<<6
+        Flags_CLIPPING = 1 << 0,
+        Flags_MIRROR_U = 1 << 1,
+        Flags_MIRROR_V = 1 << 2,
+        Flags_AXIS_X = 1 << 3,
+        Flags_AXIS_Y = 1 << 4,
+        Flags_AXIS_Z = 1 << 5,
+        Flags_VGROUP = 1 << 6
     };
 
     ModifierData modifier FAIL;
@@ -677,22 +666,24 @@ struct MirrorModifierData : ElemBase {
 };
 
 // -------------------------------------------------------------------------------
-struct Object : ElemBase  {
+struct Object : ElemBase {
     ID id FAIL;
 
     enum Type {
-         Type_EMPTY     =   0
-        ,Type_MESH      =   1
-        ,Type_CURVE     =   2
-        ,Type_SURF      =   3
-        ,Type_FONT      =   4
-        ,Type_MBALL     =   5
-
-        ,Type_LAMP      =   10
-        ,Type_CAMERA    =   11
-
-        ,Type_WAVE      =   21
-        ,Type_LATTICE   =   22
+        Type_EMPTY = 0,
+        Type_MESH = 1,
+        Type_CURVE = 2,
+        Type_SURF = 3,
+        Type_FONT = 4,
+        Type_MBALL = 5
+
+        ,
+        Type_LAMP = 10,
+        Type_CAMERA = 11
+
+        ,
+        Type_WAVE = 21,
+        Type_LATTICE = 22
     };
 
     Type type FAIL;
@@ -700,39 +691,29 @@ struct Object : ElemBase  {
     float parentinv[4][4] WARN;
     char parsubstr[32] WARN;
 
-    Object* parent WARN;
+    Object *parent WARN;
     std::shared_ptr<Object> track WARN;
 
-    std::shared_ptr<Object> proxy,proxy_from,proxy_group WARN;
+    std::shared_ptr<Object> proxy, proxy_from, proxy_group WARN;
     std::shared_ptr<Group> dup_group WARN;
     std::shared_ptr<ElemBase> data FAIL;
 
     ListBase modifiers;
 
-    Object()
-    : ElemBase()
-    , type( Type_EMPTY )
-    , parent( nullptr )
-    , track()
-    , proxy()
-    , proxy_from()
-    , data() {
+    Object() :
+            ElemBase(), type(Type_EMPTY), parent(nullptr), track(), proxy(), proxy_from(), data() {
         // empty
     }
 };
 
-
 // -------------------------------------------------------------------------------
 struct Base : ElemBase {
-    Base* prev WARN;
+    Base *prev WARN;
     std::shared_ptr<Base> next WARN;
     std::shared_ptr<Object> object WARN;
 
-    Base() 
-    : ElemBase()
-    , prev( nullptr )
-    , next()
-    , object() {
+    Base() :
+            ElemBase(), prev(nullptr), next(), object() {
         // empty
         // empty
     }
@@ -748,11 +729,8 @@ struct Scene : ElemBase {
 
     ListBase base;
 
-    Scene()
-    : ElemBase()
-    , camera()
-    , world()
-    , basact() {
+    Scene() :
+            ElemBase(), camera(), world(), basact() {
         // empty
     }
 };
@@ -783,9 +761,9 @@ struct Image : ElemBase {
     short animspeed;
 
     short gen_x, gen_y, gen_type;
-    
-    Image()
-    : ElemBase() {
+
+    Image() :
+            ElemBase() {
         // empty
     }
 };
@@ -795,33 +773,33 @@ struct Tex : ElemBase {
 
     // actually, the only texture type we support is Type_IMAGE
     enum Type {
-         Type_CLOUDS        = 1
-        ,Type_WOOD          = 2
-        ,Type_MARBLE        = 3
-        ,Type_MAGIC         = 4
-        ,Type_BLEND         = 5
-        ,Type_STUCCI        = 6
-        ,Type_NOISE         = 7
-        ,Type_IMAGE         = 8
-        ,Type_PLUGIN        = 9
-        ,Type_ENVMAP        = 10
-        ,Type_MUSGRAVE      = 11
-        ,Type_VORONOI       = 12
-        ,Type_DISTNOISE     = 13
-        ,Type_POINTDENSITY  = 14
-        ,Type_VOXELDATA     = 15
+        Type_CLOUDS = 1,
+        Type_WOOD = 2,
+        Type_MARBLE = 3,
+        Type_MAGIC = 4,
+        Type_BLEND = 5,
+        Type_STUCCI = 6,
+        Type_NOISE = 7,
+        Type_IMAGE = 8,
+        Type_PLUGIN = 9,
+        Type_ENVMAP = 10,
+        Type_MUSGRAVE = 11,
+        Type_VORONOI = 12,
+        Type_DISTNOISE = 13,
+        Type_POINTDENSITY = 14,
+        Type_VOXELDATA = 15
     };
 
     enum ImageFlags {
-         ImageFlags_INTERPOL         = 1
-        ,ImageFlags_USEALPHA         = 2
-        ,ImageFlags_MIPMAP           = 4
-        ,ImageFlags_IMAROT           = 16
-        ,ImageFlags_CALCALPHA        = 32
-        ,ImageFlags_NORMALMAP        = 2048
-        ,ImageFlags_GAUSS_MIP        = 4096
-        ,ImageFlags_FILTER_MIN       = 8192
-        ,ImageFlags_DERIVATIVEMAP   = 16384
+        ImageFlags_INTERPOL = 1,
+        ImageFlags_USEALPHA = 2,
+        ImageFlags_MIPMAP = 4,
+        ImageFlags_IMAROT = 16,
+        ImageFlags_CALCALPHA = 32,
+        ImageFlags_NORMALMAP = 2048,
+        ImageFlags_GAUSS_MIP = 4096,
+        ImageFlags_FILTER_MIN = 8192,
+        ImageFlags_DERIVATIVEMAP = 16384
     };
 
     ID id FAIL;
@@ -876,11 +854,8 @@ struct Tex : ElemBase {
 
     //char use_nodes;
 
-    Tex()
-    : ElemBase()
-    , imaflag( ImageFlags_INTERPOL )
-    , type( Type_CLOUDS )
-    , ima() {
+    Tex() :
+            ElemBase(), imaflag(ImageFlags_INTERPOL), type(Type_CLOUDS), ima() {
         // empty
     }
 };
@@ -889,52 +864,52 @@ struct Tex : ElemBase {
 struct MTex : ElemBase {
 
     enum Projection {
-         Proj_N = 0
-        ,Proj_X = 1
-        ,Proj_Y = 2
-        ,Proj_Z = 3
+        Proj_N = 0,
+        Proj_X = 1,
+        Proj_Y = 2,
+        Proj_Z = 3
     };
 
     enum Flag {
-         Flag_RGBTOINT      = 0x1
-        ,Flag_STENCIL       = 0x2
-        ,Flag_NEGATIVE      = 0x4
-        ,Flag_ALPHAMIX      = 0x8
-        ,Flag_VIEWSPACE     = 0x10
+        Flag_RGBTOINT = 0x1,
+        Flag_STENCIL = 0x2,
+        Flag_NEGATIVE = 0x4,
+        Flag_ALPHAMIX = 0x8,
+        Flag_VIEWSPACE = 0x10
     };
 
     enum BlendType {
-         BlendType_BLEND            = 0
-        ,BlendType_MUL              = 1
-        ,BlendType_ADD              = 2
-        ,BlendType_SUB              = 3
-        ,BlendType_DIV              = 4
-        ,BlendType_DARK             = 5
-        ,BlendType_DIFF             = 6
-        ,BlendType_LIGHT            = 7
-        ,BlendType_SCREEN           = 8
-        ,BlendType_OVERLAY          = 9
-        ,BlendType_BLEND_HUE        = 10
-        ,BlendType_BLEND_SAT        = 11
-        ,BlendType_BLEND_VAL        = 12
-        ,BlendType_BLEND_COLOR      = 13
+        BlendType_BLEND = 0,
+        BlendType_MUL = 1,
+        BlendType_ADD = 2,
+        BlendType_SUB = 3,
+        BlendType_DIV = 4,
+        BlendType_DARK = 5,
+        BlendType_DIFF = 6,
+        BlendType_LIGHT = 7,
+        BlendType_SCREEN = 8,
+        BlendType_OVERLAY = 9,
+        BlendType_BLEND_HUE = 10,
+        BlendType_BLEND_SAT = 11,
+        BlendType_BLEND_VAL = 12,
+        BlendType_BLEND_COLOR = 13
     };
 
     enum MapType {
-         MapType_COL         = 1
-        ,MapType_NORM        = 2
-        ,MapType_COLSPEC     = 4
-        ,MapType_COLMIR      = 8
-        ,MapType_REF         = 16
-        ,MapType_SPEC        = 32
-        ,MapType_EMIT        = 64
-        ,MapType_ALPHA       = 128
-        ,MapType_HAR         = 256
-        ,MapType_RAYMIRR     = 512
-        ,MapType_TRANSLU     = 1024
-        ,MapType_AMB         = 2048
-        ,MapType_DISPLACE    = 4096
-        ,MapType_WARP        = 8192
+        MapType_COL = 1,
+        MapType_NORM = 2,
+        MapType_COLSPEC = 4,
+        MapType_COLMIR = 8,
+        MapType_REF = 16,
+        MapType_SPEC = 32,
+        MapType_EMIT = 64,
+        MapType_ALPHA = 128,
+        MapType_HAR = 256,
+        MapType_RAYMIRR = 512,
+        MapType_TRANSLU = 1024,
+        MapType_AMB = 2048,
+        MapType_DISPLACE = 4096,
+        MapType_WARP = 8192
     };
 
     // short texco, maptoneg;
@@ -945,7 +920,7 @@ struct MTex : ElemBase {
     std::shared_ptr<Tex> tex;
     char uvname[32];
 
-    Projection projx,projy,projz;
+    Projection projx, projy, projz;
     char mapping;
     float ofs[3], size[3], rot;
 
@@ -953,7 +928,7 @@ struct MTex : ElemBase {
     short colormodel, pmapto, pmaptoneg;
     //short normapspace, which_output;
     //char brush_map_mode;
-    float r,g,b,k WARN;
+    float r, g, b, k WARN;
     //float def_var, rt;
 
     //float colfac, varfac;
@@ -972,12 +947,12 @@ struct MTex : ElemBase {
     //float shadowfac;
     //float zenupfac, zendownfac, blendfac;
 
-    MTex()
-    : ElemBase() {
+    MTex() :
+            ElemBase() {
         // empty
     }
 };
 
-}
-}
+} // namespace Blender
+} // namespace Assimp
 #endif

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


+ 0 - 0
code/Blender/BlenderTessellator.cpp → code/AssetLib/Blender/BlenderTessellator.cpp


+ 0 - 0
code/Blender/BlenderTessellator.h → code/AssetLib/Blender/BlenderTessellator.h


+ 0 - 0
code/C4D/C4DImporter.cpp → code/AssetLib/C4D/C4DImporter.cpp


+ 0 - 0
code/C4D/C4DImporter.h → code/AssetLib/C4D/C4DImporter.h


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 248 - 287
code/AssetLib/COB/COBLoader.cpp


+ 0 - 0
code/COB/COBLoader.h → code/AssetLib/COB/COBLoader.h


+ 0 - 0
code/COB/COBScene.h → code/AssetLib/COB/COBScene.h


+ 0 - 0
code/CSM/CSMLoader.cpp → code/AssetLib/CSM/CSMLoader.cpp


+ 0 - 0
code/CSM/CSMLoader.h → code/AssetLib/CSM/CSMLoader.h


+ 1651 - 0
code/AssetLib/Collada/ColladaExporter.cpp

@@ -0,0 +1,1651 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
+
+#include "ColladaExporter.h"
+#include <assimp/Bitmap.h>
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/MathFunctions.h>
+#include <assimp/SceneCombiner.h>
+#include <assimp/StringUtils.h>
+#include <assimp/XMLTools.h>
+#include <assimp/commonMetaData.h>
+#include <assimp/fast_atof.h>
+#include <assimp/scene.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
+
+#include <assimp/Exceptional.h>
+
+#include <ctime>
+#include <iostream>
+#include <memory>
+#include <set>
+#include <vector>
+
+using namespace Assimp;
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
+void ExportSceneCollada(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
+    std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
+
+    // invoke the exporter
+    ColladaExporter iDoTheExportThing(pScene, pIOSystem, path, file);
+
+    if (iDoTheExportThing.mOutput.fail()) {
+        throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
+    }
+
+    // we're still here - export successfully completed. Write result to the given IOSYstem
+    std::unique_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wt"));
+    if (outfile == nullptr) {
+        throw DeadlyExportError("could not open output .dae file: " + std::string(pFile));
+    }
+
+    // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy.
+    outfile->Write(iDoTheExportThing.mOutput.str().c_str(), static_cast<size_t>(iDoTheExportThing.mOutput.tellp()), 1);
+}
+
+} // end of namespace Assimp
+
+// ------------------------------------------------------------------------------------------------
+// Encodes a string into a valid XML ID using the xsd:ID schema qualifications.
+static const std::string XMLIDEncode(const std::string &name) {
+    const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
+    const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char);
+
+    if (name.length() == 0) {
+        return name;
+    }
+
+    std::stringstream idEncoded;
+
+    // xsd:ID must start with letter or underscore
+    if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) {
+        idEncoded << '_';
+    }
+
+    for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) {
+        // xsd:ID can only contain letters, digits, underscores, hyphens and periods
+        if (strchr(XML_ID_CHARS, *it) != nullptr) {
+            idEncoded << *it;
+        } else {
+            // Select placeholder character based on invalid character to prevent name collisions
+            idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT];
+        }
+    }
+
+    return idEncoded.str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor for a specific scene to export
+ColladaExporter::ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file) :
+        mIOSystem(pIOSystem), mPath(path), mFile(file) {
+    // make sure that all formatting happens using the standard, C locale and not the user's current locale
+    mOutput.imbue(std::locale("C"));
+    mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
+
+    mScene = pScene;
+    mSceneOwned = false;
+
+    // set up strings
+    endstr = "\n";
+
+    // start writing the file
+    WriteFile();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor
+ColladaExporter::~ColladaExporter() {
+    if (mSceneOwned) {
+        delete mScene;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Starts writing the contents
+void ColladaExporter::WriteFile() {
+    // write the DTD
+    mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
+    // COLLADA element start
+    mOutput << "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">" << endstr;
+    PushTag();
+
+    WriteTextures();
+    WriteHeader();
+
+    WriteCamerasLibrary();
+    WriteLightsLibrary();
+    WriteMaterials();
+    WriteGeometryLibrary();
+    WriteControllerLibrary();
+
+    WriteSceneLibrary();
+
+    // customized, Writes the animation library
+    WriteAnimationsLibrary();
+
+    // useless Collada fu at the end, just in case we haven't had enough indirections, yet.
+    mOutput << startstr << "<scene>" << endstr;
+    PushTag();
+    mOutput << startstr << "<instance_visual_scene url=\"#" + XMLIDEncode(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
+    PopTag();
+    mOutput << startstr << "</scene>" << endstr;
+    PopTag();
+    mOutput << "</COLLADA>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the asset header
+void ColladaExporter::WriteHeader() {
+    static const ai_real epsilon = Math::getEpsilon<ai_real>();
+    static const aiQuaternion x_rot(aiMatrix3x3(
+            0, -1, 0,
+            1, 0, 0,
+            0, 0, 1));
+    static const aiQuaternion y_rot(aiMatrix3x3(
+            1, 0, 0,
+            0, 1, 0,
+            0, 0, 1));
+    static const aiQuaternion z_rot(aiMatrix3x3(
+            1, 0, 0,
+            0, 0, 1,
+            0, -1, 0));
+
+    static const unsigned int date_nb_chars = 20;
+    char date_str[date_nb_chars];
+    std::time_t date = std::time(NULL);
+    std::strftime(date_str, date_nb_chars, "%Y-%m-%dT%H:%M:%S", std::localtime(&date));
+
+    aiVector3D scaling;
+    aiQuaternion rotation;
+    aiVector3D position;
+    mScene->mRootNode->mTransformation.Decompose(scaling, rotation, position);
+    rotation.Normalize();
+
+    bool add_root_node = false;
+
+    ai_real scale = 1.0;
+    if (std::abs(scaling.x - scaling.y) <= epsilon && std::abs(scaling.x - scaling.z) <= epsilon && std::abs(scaling.y - scaling.z) <= epsilon) {
+        scale = (ai_real)((((double)scaling.x) + ((double)scaling.y) + ((double)scaling.z)) / 3.0);
+    } else {
+        add_root_node = true;
+    }
+
+    std::string up_axis = "Y_UP";
+    if (rotation.Equal(x_rot, epsilon)) {
+        up_axis = "X_UP";
+    } else if (rotation.Equal(y_rot, epsilon)) {
+        up_axis = "Y_UP";
+    } else if (rotation.Equal(z_rot, epsilon)) {
+        up_axis = "Z_UP";
+    } else {
+        add_root_node = true;
+    }
+
+    if (!position.Equal(aiVector3D(0, 0, 0))) {
+        add_root_node = true;
+    }
+
+    if (mScene->mRootNode->mNumChildren == 0) {
+        add_root_node = true;
+    }
+
+    if (add_root_node) {
+        aiScene *scene;
+        SceneCombiner::CopyScene(&scene, mScene);
+
+        aiNode *root = new aiNode("Scene");
+
+        root->mNumChildren = 1;
+        root->mChildren = new aiNode *[root->mNumChildren];
+
+        root->mChildren[0] = scene->mRootNode;
+        scene->mRootNode->mParent = root;
+        scene->mRootNode = root;
+
+        mScene = scene;
+        mSceneOwned = true;
+
+        up_axis = "Y_UP";
+        scale = 1.0;
+    }
+
+    mOutput << startstr << "<asset>" << endstr;
+    PushTag();
+    mOutput << startstr << "<contributor>" << endstr;
+    PushTag();
+
+    // If no Scene metadata, use root node metadata
+    aiMetadata *meta = mScene->mMetaData;
+    if (nullptr == meta) {
+        meta = mScene->mRootNode->mMetaData;
+    }
+
+    aiString value;
+    if (!meta || !meta->Get("Author", value)) {
+        mOutput << startstr << "<author>"
+                << "Assimp"
+                << "</author>" << endstr;
+    } else {
+        mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
+    }
+
+    if (nullptr == meta || !meta->Get(AI_METADATA_SOURCE_GENERATOR, value)) {
+        mOutput << startstr << "<authoring_tool>"
+                << "Assimp Exporter"
+                << "</authoring_tool>" << endstr;
+    } else {
+        mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
+    }
+
+    if (meta) {
+        if (meta->Get("Comments", value)) {
+            mOutput << startstr << "<comments>" << XMLEscape(value.C_Str()) << "</comments>" << endstr;
+        }
+        if (meta->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) {
+            mOutput << startstr << "<copyright>" << XMLEscape(value.C_Str()) << "</copyright>" << endstr;
+        }
+        if (meta->Get("SourceData", value)) {
+            mOutput << startstr << "<source_data>" << XMLEscape(value.C_Str()) << "</source_data>" << endstr;
+        }
+    }
+
+    PopTag();
+    mOutput << startstr << "</contributor>" << endstr;
+
+    if (nullptr == meta || !meta->Get("Created", value)) {
+        mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+    } else {
+        mOutput << startstr << "<created>" << XMLEscape(value.C_Str()) << "</created>" << endstr;
+    }
+
+    // Modified date is always the date saved
+    mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
+
+    if (meta) {
+        if (meta->Get("Keywords", value)) {
+            mOutput << startstr << "<keywords>" << XMLEscape(value.C_Str()) << "</keywords>" << endstr;
+        }
+        if (meta->Get("Revision", value)) {
+            mOutput << startstr << "<revision>" << XMLEscape(value.C_Str()) << "</revision>" << endstr;
+        }
+        if (meta->Get("Subject", value)) {
+            mOutput << startstr << "<subject>" << XMLEscape(value.C_Str()) << "</subject>" << endstr;
+        }
+        if (meta->Get("Title", value)) {
+            mOutput << startstr << "<title>" << XMLEscape(value.C_Str()) << "</title>" << endstr;
+        }
+    }
+
+    mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
+    mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
+    PopTag();
+    mOutput << startstr << "</asset>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write the embedded textures
+void ColladaExporter::WriteTextures() {
+    static const unsigned int buffer_size = 1024;
+    char str[buffer_size];
+
+    if (mScene->HasTextures()) {
+        for (unsigned int i = 0; i < mScene->mNumTextures; i++) {
+            // It would be great to be able to create a directory in portable standard C++, but it's not the case,
+            // so we just write the textures in the current directory.
+
+            aiTexture *texture = mScene->mTextures[i];
+            if (nullptr == texture) {
+                continue;
+            }
+
+            ASSIMP_itoa10(str, buffer_size, i + 1);
+
+            std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char *)texture->achFormatHint);
+
+            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
+            if (outfile == NULL) {
+                throw DeadlyExportError("could not open output texture file: " + mPath + name);
+            }
+
+            if (texture->mHeight == 0) {
+                outfile->Write((void *)texture->pcData, texture->mWidth, 1);
+            } else {
+                Bitmap::Save(texture, outfile.get());
+            }
+
+            outfile->Flush();
+
+            textures.insert(std::make_pair(i, name));
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write the embedded textures
+void ColladaExporter::WriteCamerasLibrary() {
+    if (mScene->HasCameras()) {
+
+        mOutput << startstr << "<library_cameras>" << endstr;
+        PushTag();
+
+        for (size_t a = 0; a < mScene->mNumCameras; ++a)
+            WriteCamera(a);
+
+        PopTag();
+        mOutput << startstr << "</library_cameras>" << endstr;
+    }
+}
+
+void ColladaExporter::WriteCamera(size_t pIndex) {
+
+    const aiCamera *cam = mScene->mCameras[pIndex];
+    const std::string cameraName = XMLEscape(cam->mName.C_Str());
+    const std::string cameraId = XMLIDEncode(cam->mName.C_Str());
+
+    mOutput << startstr << "<camera id=\"" << cameraId << "-camera\" name=\"" << cameraName << "\" >" << endstr;
+    PushTag();
+    mOutput << startstr << "<optics>" << endstr;
+    PushTag();
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+    //assimp doesn't support the import of orthographic cameras! se we write
+    //always perspective
+    mOutput << startstr << "<perspective>" << endstr;
+    PushTag();
+    mOutput << startstr << "<xfov sid=\"xfov\">" << AI_RAD_TO_DEG(cam->mHorizontalFOV)
+            << "</xfov>" << endstr;
+    mOutput << startstr << "<aspect_ratio>"
+            << cam->mAspect
+            << "</aspect_ratio>" << endstr;
+    mOutput << startstr << "<znear sid=\"znear\">"
+            << cam->mClipPlaneNear
+            << "</znear>" << endstr;
+    mOutput << startstr << "<zfar sid=\"zfar\">"
+            << cam->mClipPlaneFar
+            << "</zfar>" << endstr;
+    PopTag();
+    mOutput << startstr << "</perspective>" << endstr;
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+    PopTag();
+    mOutput << startstr << "</optics>" << endstr;
+    PopTag();
+    mOutput << startstr << "</camera>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write the embedded textures
+void ColladaExporter::WriteLightsLibrary() {
+    if (mScene->HasLights()) {
+
+        mOutput << startstr << "<library_lights>" << endstr;
+        PushTag();
+
+        for (size_t a = 0; a < mScene->mNumLights; ++a)
+            WriteLight(a);
+
+        PopTag();
+        mOutput << startstr << "</library_lights>" << endstr;
+    }
+}
+
+void ColladaExporter::WriteLight(size_t pIndex) {
+
+    const aiLight *light = mScene->mLights[pIndex];
+    const std::string lightName = XMLEscape(light->mName.C_Str());
+    const std::string lightId = XMLIDEncode(light->mName.C_Str());
+
+    mOutput << startstr << "<light id=\"" << lightId << "-light\" name=\""
+            << lightName << "\" >" << endstr;
+    PushTag();
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+    switch (light->mType) {
+    case aiLightSource_AMBIENT:
+        WriteAmbienttLight(light);
+        break;
+    case aiLightSource_DIRECTIONAL:
+        WriteDirectionalLight(light);
+        break;
+    case aiLightSource_POINT:
+        WritePointLight(light);
+        break;
+    case aiLightSource_SPOT:
+        WriteSpotLight(light);
+        break;
+    case aiLightSource_AREA:
+    case aiLightSource_UNDEFINED:
+    case _aiLightSource_Force32Bit:
+        break;
+    }
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</light>" << endstr;
+}
+
+void ColladaExporter::WritePointLight(const aiLight *const light) {
+    const aiColor3D &color = light->mColorDiffuse;
+    mOutput << startstr << "<point>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+    mOutput << startstr << "<constant_attenuation>"
+            << light->mAttenuationConstant
+            << "</constant_attenuation>" << endstr;
+    mOutput << startstr << "<linear_attenuation>"
+            << light->mAttenuationLinear
+            << "</linear_attenuation>" << endstr;
+    mOutput << startstr << "<quadratic_attenuation>"
+            << light->mAttenuationQuadratic
+            << "</quadratic_attenuation>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</point>" << endstr;
+}
+
+void ColladaExporter::WriteDirectionalLight(const aiLight *const light) {
+    const aiColor3D &color = light->mColorDiffuse;
+    mOutput << startstr << "<directional>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</directional>" << endstr;
+}
+
+void ColladaExporter::WriteSpotLight(const aiLight *const light) {
+
+    const aiColor3D &color = light->mColorDiffuse;
+    mOutput << startstr << "<spot>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+    mOutput << startstr << "<constant_attenuation>"
+            << light->mAttenuationConstant
+            << "</constant_attenuation>" << endstr;
+    mOutput << startstr << "<linear_attenuation>"
+            << light->mAttenuationLinear
+            << "</linear_attenuation>" << endstr;
+    mOutput << startstr << "<quadratic_attenuation>"
+            << light->mAttenuationQuadratic
+            << "</quadratic_attenuation>" << endstr;
+    /*
+    out->mAngleOuterCone = AI_DEG_TO_RAD (std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+
+                            srcLight->mFalloffAngle);
+    */
+
+    const ai_real fallOffAngle = AI_RAD_TO_DEG(light->mAngleInnerCone);
+    mOutput << startstr << "<falloff_angle sid=\"fall_off_angle\">"
+            << fallOffAngle
+            << "</falloff_angle>" << endstr;
+    double temp = light->mAngleOuterCone - light->mAngleInnerCone;
+
+    temp = std::cos(temp);
+    temp = std::log(temp) / std::log(0.1);
+    temp = 1 / temp;
+    mOutput << startstr << "<falloff_exponent sid=\"fall_off_exponent\">"
+            << temp
+            << "</falloff_exponent>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</spot>" << endstr;
+}
+
+void ColladaExporter::WriteAmbienttLight(const aiLight *const light) {
+
+    const aiColor3D &color = light->mColorAmbient;
+    mOutput << startstr << "<ambient>" << endstr;
+    PushTag();
+    mOutput << startstr << "<color sid=\"color\">"
+            << color.r << " " << color.g << " " << color.b
+            << "</color>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</ambient>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single surface entry from the given material keys
+void ColladaExporter::ReadMaterialSurface(Surface &poSurface, const aiMaterial *pSrcMat,
+        aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex) {
+    if (pSrcMat->GetTextureCount(pTexture) > 0) {
+        aiString texfile;
+        unsigned int uvChannel = 0;
+        pSrcMat->GetTexture(pTexture, 0, &texfile, NULL, &uvChannel);
+
+        std::string index_str(texfile.C_Str());
+
+        if (index_str.size() != 0 && index_str[0] == '*') {
+            unsigned int index;
+
+            index_str = index_str.substr(1, std::string::npos);
+
+            try {
+                index = (unsigned int)strtoul10_64(index_str.c_str());
+            } catch (std::exception &error) {
+                throw DeadlyExportError(error.what());
+            }
+
+            std::map<unsigned int, std::string>::const_iterator name = textures.find(index);
+
+            if (name != textures.end()) {
+                poSurface.texture = name->second;
+            } else {
+                throw DeadlyExportError("could not find embedded texture at index " + index_str);
+            }
+        } else {
+            poSurface.texture = texfile.C_Str();
+        }
+
+        poSurface.channel = uvChannel;
+        poSurface.exist = true;
+    } else {
+        if (pKey)
+            poSurface.exist = pSrcMat->Get(pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
+static bool isalnum_C(char c) {
+    return (nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", c));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes an image entry for the given surface
+void ColladaExporter::WriteImageEntry(const Surface &pSurface, const std::string &pNameAdd) {
+    if (!pSurface.texture.empty()) {
+        mOutput << startstr << "<image id=\"" << XMLIDEncode(pNameAdd) << "\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<init_from>";
+
+        // URL encode image file name first, then XML encode on top
+        std::stringstream imageUrlEncoded;
+        for (std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it) {
+            if (isalnum_C((unsigned char)*it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\')
+                imageUrlEncoded << *it;
+            else
+                imageUrlEncoded << '%' << std::hex << size_t((unsigned char)*it) << std::dec;
+        }
+        mOutput << XMLEscape(imageUrlEncoded.str());
+        mOutput << "</init_from>" << endstr;
+        PopTag();
+        mOutput << startstr << "</image>" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a color-or-texture entry into an effect definition
+void ColladaExporter::WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &pImageName) {
+    if (pSurface.exist) {
+        mOutput << startstr << "<" << pTypeName << ">" << endstr;
+        PushTag();
+        if (pSurface.texture.empty()) {
+            mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << "   " << pSurface.color.g << "   " << pSurface.color.b << "   " << pSurface.color.a << "</color>" << endstr;
+        } else {
+            mOutput << startstr << "<texture texture=\"" << XMLIDEncode(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+        }
+        PopTag();
+        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the two parameters necessary for referencing a texture in an effect entry
+void ColladaExporter::WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &pMatName) {
+    // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
+    if (!pSurface.texture.empty()) {
+        mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<surface type=\"2D\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<init_from>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
+        PopTag();
+        mOutput << startstr << "</surface>" << endstr;
+        PopTag();
+        mOutput << startstr << "</newparam>" << endstr;
+
+        mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<sampler2D>" << endstr;
+        PushTag();
+        mOutput << startstr << "<source>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
+        PopTag();
+        mOutput << startstr << "</sampler2D>" << endstr;
+        PopTag();
+        mOutput << startstr << "</newparam>" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a scalar property
+void ColladaExporter::WriteFloatEntry(const Property &pProperty, const std::string &pTypeName) {
+    if (pProperty.exist) {
+        mOutput << startstr << "<" << pTypeName << ">" << endstr;
+        PushTag();
+        mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr;
+        PopTag();
+        mOutput << startstr << "</" << pTypeName << ">" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the material setup
+void ColladaExporter::WriteMaterials() {
+    materials.resize(mScene->mNumMaterials);
+
+    /// collect all materials from the scene
+    size_t numTextures = 0;
+    for (size_t a = 0; a < mScene->mNumMaterials; ++a) {
+        const aiMaterial *mat = mScene->mMaterials[a];
+
+        aiString name;
+        if (mat->Get(AI_MATKEY_NAME, name) != aiReturn_SUCCESS) {
+            name = "mat";
+            materials[a].name = std::string("m") + to_string(a) + name.C_Str();
+        } else {
+            // try to use the material's name if no other material has already taken it, else append #
+            std::string testName = name.C_Str();
+            size_t materialCountWithThisName = 0;
+            for (size_t i = 0; i < a; i++) {
+                if (materials[i].name == testName) {
+                    materialCountWithThisName++;
+                }
+            }
+            if (materialCountWithThisName == 0) {
+                materials[a].name = name.C_Str();
+            } else {
+                materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName);
+            }
+        }
+
+        aiShadingMode shading = aiShadingMode_Flat;
+        materials[a].shading_model = "phong";
+        if (mat->Get(AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) {
+            if (shading == aiShadingMode_Phong) {
+                materials[a].shading_model = "phong";
+            } else if (shading == aiShadingMode_Blinn) {
+                materials[a].shading_model = "blinn";
+            } else if (shading == aiShadingMode_NoShading) {
+                materials[a].shading_model = "constant";
+            } else if (shading == aiShadingMode_Gouraud) {
+                materials[a].shading_model = "lambert";
+            }
+        }
+
+        ReadMaterialSurface(materials[a].ambient, mat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT);
+        if (!materials[a].ambient.texture.empty()) numTextures++;
+        ReadMaterialSurface(materials[a].diffuse, mat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE);
+        if (!materials[a].diffuse.texture.empty()) numTextures++;
+        ReadMaterialSurface(materials[a].specular, mat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR);
+        if (!materials[a].specular.texture.empty()) numTextures++;
+        ReadMaterialSurface(materials[a].emissive, mat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE);
+        if (!materials[a].emissive.texture.empty()) numTextures++;
+        ReadMaterialSurface(materials[a].reflective, mat, aiTextureType_REFLECTION, AI_MATKEY_COLOR_REFLECTIVE);
+        if (!materials[a].reflective.texture.empty()) numTextures++;
+        ReadMaterialSurface(materials[a].transparent, mat, aiTextureType_OPACITY, AI_MATKEY_COLOR_TRANSPARENT);
+        if (!materials[a].transparent.texture.empty()) numTextures++;
+        ReadMaterialSurface(materials[a].normal, mat, aiTextureType_NORMALS, NULL, 0, 0);
+        if (!materials[a].normal.texture.empty()) numTextures++;
+
+        materials[a].shininess.exist = mat->Get(AI_MATKEY_SHININESS, materials[a].shininess.value) == aiReturn_SUCCESS;
+        materials[a].transparency.exist = mat->Get(AI_MATKEY_OPACITY, materials[a].transparency.value) == aiReturn_SUCCESS;
+        materials[a].index_refraction.exist = mat->Get(AI_MATKEY_REFRACTI, materials[a].index_refraction.value) == aiReturn_SUCCESS;
+    }
+
+    // output textures if present
+    if (numTextures > 0) {
+        mOutput << startstr << "<library_images>" << endstr;
+        PushTag();
+        for (std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it) {
+            const Material &mat = *it;
+            WriteImageEntry(mat.ambient, mat.name + "-ambient-image");
+            WriteImageEntry(mat.diffuse, mat.name + "-diffuse-image");
+            WriteImageEntry(mat.specular, mat.name + "-specular-image");
+            WriteImageEntry(mat.emissive, mat.name + "-emission-image");
+            WriteImageEntry(mat.reflective, mat.name + "-reflective-image");
+            WriteImageEntry(mat.transparent, mat.name + "-transparent-image");
+            WriteImageEntry(mat.normal, mat.name + "-normal-image");
+        }
+        PopTag();
+        mOutput << startstr << "</library_images>" << endstr;
+    }
+
+    // output effects - those are the actual carriers of information
+    if (!materials.empty()) {
+        mOutput << startstr << "<library_effects>" << endstr;
+        PushTag();
+        for (std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it) {
+            const Material &mat = *it;
+            // this is so ridiculous it must be right
+            mOutput << startstr << "<effect id=\"" << XMLIDEncode(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<profile_COMMON>" << endstr;
+            PushTag();
+
+            // write sampler- and surface params for the texture entries
+            WriteTextureParamEntry(mat.emissive, "emission", mat.name);
+            WriteTextureParamEntry(mat.ambient, "ambient", mat.name);
+            WriteTextureParamEntry(mat.diffuse, "diffuse", mat.name);
+            WriteTextureParamEntry(mat.specular, "specular", mat.name);
+            WriteTextureParamEntry(mat.reflective, "reflective", mat.name);
+            WriteTextureParamEntry(mat.transparent, "transparent", mat.name);
+            WriteTextureParamEntry(mat.normal, "normal", mat.name);
+
+            mOutput << startstr << "<technique sid=\"standard\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<" << mat.shading_model << ">" << endstr;
+            PushTag();
+
+            WriteTextureColorEntry(mat.emissive, "emission", mat.name + "-emission-sampler");
+            WriteTextureColorEntry(mat.ambient, "ambient", mat.name + "-ambient-sampler");
+            WriteTextureColorEntry(mat.diffuse, "diffuse", mat.name + "-diffuse-sampler");
+            WriteTextureColorEntry(mat.specular, "specular", mat.name + "-specular-sampler");
+            WriteFloatEntry(mat.shininess, "shininess");
+            WriteTextureColorEntry(mat.reflective, "reflective", mat.name + "-reflective-sampler");
+            WriteTextureColorEntry(mat.transparent, "transparent", mat.name + "-transparent-sampler");
+            WriteFloatEntry(mat.transparency, "transparency");
+            WriteFloatEntry(mat.index_refraction, "index_of_refraction");
+
+            if (!mat.normal.texture.empty()) {
+                WriteTextureColorEntry(mat.normal, "bump", mat.name + "-normal-sampler");
+            }
+
+            PopTag();
+            mOutput << startstr << "</" << mat.shading_model << ">" << endstr;
+            PopTag();
+            mOutput << startstr << "</technique>" << endstr;
+            PopTag();
+            mOutput << startstr << "</profile_COMMON>" << endstr;
+            PopTag();
+            mOutput << startstr << "</effect>" << endstr;
+        }
+        PopTag();
+        mOutput << startstr << "</library_effects>" << endstr;
+
+        // write materials - they're just effect references
+        mOutput << startstr << "<library_materials>" << endstr;
+        PushTag();
+        for (std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it) {
+            const Material &mat = *it;
+            mOutput << startstr << "<material id=\"" << XMLIDEncode(mat.name) << "\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<instance_effect url=\"#" << XMLIDEncode(mat.name) << "-fx\"/>" << endstr;
+            PopTag();
+            mOutput << startstr << "</material>" << endstr;
+        }
+        PopTag();
+        mOutput << startstr << "</library_materials>" << endstr;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the controller library
+void ColladaExporter::WriteControllerLibrary() {
+    mOutput << startstr << "<library_controllers>" << endstr;
+    PushTag();
+
+    for (size_t a = 0; a < mScene->mNumMeshes; ++a) {
+        WriteController(a);
+    }
+
+    PopTag();
+    mOutput << startstr << "</library_controllers>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a skin controller of the given mesh
+void ColladaExporter::WriteController(size_t pIndex) {
+    const aiMesh *mesh = mScene->mMeshes[pIndex];
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string idstrEscaped = XMLIDEncode(idstr);
+
+    if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+        return;
+
+    if (mesh->mNumBones == 0)
+        return;
+
+    mOutput << startstr << "<controller id=\"" << idstrEscaped << "-skin\" ";
+    mOutput << "name=\"skinCluster" << pIndex << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<skin source=\"#" << idstrEscaped << "\">" << endstr;
+    PushTag();
+
+    // bind pose matrix
+    mOutput << startstr << "<bind_shape_matrix>" << endstr;
+    PushTag();
+
+    // I think it is identity in general cases.
+    aiMatrix4x4 mat;
+    mOutput << startstr << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << endstr;
+    mOutput << startstr << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << endstr;
+    mOutput << startstr << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << endstr;
+    mOutput << startstr << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4 << endstr;
+
+    PopTag();
+    mOutput << startstr << "</bind_shape_matrix>" << endstr;
+
+    mOutput << startstr << "<source id=\"" << idstrEscaped << "-skin-joints\" name=\"" << idstrEscaped << "-skin-joints\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
+
+    for (size_t i = 0; i < mesh->mNumBones; ++i)
+        mOutput << XMLIDEncode(mesh->mBones[i]->mName.C_Str()) << " ";
+
+    mOutput << "</Name_array>" << endstr;
+
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<accessor source=\"#" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\" stride=\"" << 1 << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<param name=\"JOINT\" type=\"Name\"></param>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</accessor>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</source>" << endstr;
+
+    std::vector<ai_real> bind_poses;
+    bind_poses.reserve(mesh->mNumBones * 16);
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
+        for (unsigned int j = 0; j < 4; ++j)
+            bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4);
+
+    WriteFloatArray(idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real *)bind_poses.data(), bind_poses.size() / 16);
+
+    bind_poses.clear();
+
+    std::vector<ai_real> skin_weights;
+    skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones);
+    for (size_t i = 0; i < mesh->mNumBones; ++i)
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+            skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight);
+
+    WriteFloatArray(idstr + "-skin-weights", FloatType_Weight, (const ai_real *)skin_weights.data(), skin_weights.size());
+
+    skin_weights.clear();
+
+    mOutput << startstr << "<joints>" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstrEscaped << "-skin-joints\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"INV_BIND_MATRIX\" source=\"#" << idstrEscaped << "-skin-bind_poses\"></input>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</joints>" << endstr;
+
+    mOutput << startstr << "<vertex_weights count=\"" << mesh->mNumVertices << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<input semantic=\"JOINT\" source=\"#" << idstrEscaped << "-skin-joints\" offset=\"0\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"WEIGHT\" source=\"#" << idstrEscaped << "-skin-weights\" offset=\"1\"></input>" << endstr;
+
+    mOutput << startstr << "<vcount>";
+
+    std::vector<ai_uint> num_influences(mesh->mNumVertices, (ai_uint)0);
+    for (size_t i = 0; i < mesh->mNumBones; ++i)
+        for (size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j)
+            ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId];
+
+    for (size_t i = 0; i < mesh->mNumVertices; ++i)
+        mOutput << num_influences[i] << " ";
+
+    mOutput << "</vcount>" << endstr;
+
+    mOutput << startstr << "<v>";
+
+    ai_uint joint_weight_indices_length = 0;
+    std::vector<ai_uint> accum_influences;
+    accum_influences.reserve(num_influences.size());
+    for (size_t i = 0; i < num_influences.size(); ++i) {
+        accum_influences.push_back(joint_weight_indices_length);
+        joint_weight_indices_length += num_influences[i];
+    }
+
+    ai_uint weight_index = 0;
+    std::vector<ai_int> joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1);
+    for (unsigned int i = 0; i < mesh->mNumBones; ++i)
+        for (unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
+            unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId;
+            for (ai_uint k = 0; k < num_influences[vId]; ++k) {
+                if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1) {
+                    joint_weight_indices[2 * (accum_influences[vId] + k)] = i;
+                    joint_weight_indices[2 * (accum_influences[vId] + k) + 1] = weight_index;
+                    break;
+                }
+            }
+            ++weight_index;
+        }
+
+    for (size_t i = 0; i < joint_weight_indices.size(); ++i)
+        mOutput << joint_weight_indices[i] << " ";
+
+    num_influences.clear();
+    accum_influences.clear();
+    joint_weight_indices.clear();
+
+    mOutput << "</v>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</vertex_weights>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</skin>" << endstr;
+
+    PopTag();
+    mOutput << startstr << "</controller>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the geometry library
+void ColladaExporter::WriteGeometryLibrary() {
+    mOutput << startstr << "<library_geometries>" << endstr;
+    PushTag();
+
+    for (size_t a = 0; a < mScene->mNumMeshes; ++a)
+        WriteGeometry(a);
+
+    PopTag();
+    mOutput << startstr << "</library_geometries>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the given mesh
+void ColladaExporter::WriteGeometry(size_t pIndex) {
+    const aiMesh *mesh = mScene->mMeshes[pIndex];
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string geometryName = XMLEscape(idstr);
+    const std::string geometryId = XMLIDEncode(idstr);
+
+    if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+        return;
+
+    // opening tag
+    mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<mesh>" << endstr;
+    PushTag();
+
+    // Positions
+    WriteFloatArray(idstr + "-positions", FloatType_Vector, (ai_real *)mesh->mVertices, mesh->mNumVertices);
+    // Normals, if any
+    if (mesh->HasNormals())
+        WriteFloatArray(idstr + "-normals", FloatType_Vector, (ai_real *)mesh->mNormals, mesh->mNumVertices);
+
+    // texture coords
+    for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+        if (mesh->HasTextureCoords(static_cast<unsigned int>(a))) {
+            WriteFloatArray(idstr + "-tex" + to_string(a), mesh->mNumUVComponents[a] == 3 ? FloatType_TexCoord3 : FloatType_TexCoord2,
+                    (ai_real *)mesh->mTextureCoords[a], mesh->mNumVertices);
+        }
+    }
+
+    // vertex colors
+    for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+        if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
+            WriteFloatArray(idstr + "-color" + to_string(a), FloatType_Color, (ai_real *)mesh->mColors[a], mesh->mNumVertices);
+    }
+
+    // assemble vertex structure
+    // Only write input for POSITION since we will write other as shared inputs in polygon definition
+    mOutput << startstr << "<vertices id=\"" << geometryId << "-vertices"
+            << "\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr;
+    PopTag();
+    mOutput << startstr << "</vertices>" << endstr;
+
+    // count the number of lines, triangles and polygon meshes
+    int countLines = 0;
+    int countPoly = 0;
+    for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+        if (mesh->mFaces[a].mNumIndices == 2)
+            countLines++;
+        else if (mesh->mFaces[a].mNumIndices >= 3)
+            countPoly++;
+    }
+
+    // lines
+    if (countLines) {
+        mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
+        if (mesh->HasNormals())
+            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+            if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+            if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+
+        mOutput << startstr << "<p>";
+        for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+            const aiFace &face = mesh->mFaces[a];
+            if (face.mNumIndices != 2) continue;
+            for (size_t b = 0; b < face.mNumIndices; ++b)
+                mOutput << face.mIndices[b] << " ";
+        }
+        mOutput << "</p>" << endstr;
+        PopTag();
+        mOutput << startstr << "</lines>" << endstr;
+    }
+
+    // triangle - don't use it, because compatibility problems
+
+    // polygons
+    if (countPoly) {
+        mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
+        if (mesh->HasNormals())
+            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+            if (mesh->HasTextureCoords(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+        for (size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+            if (mesh->HasVertexColors(static_cast<unsigned int>(a)))
+                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" "
+                        << "set=\"" << a << "\""
+                        << " />" << endstr;
+        }
+
+        mOutput << startstr << "<vcount>";
+        for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+            if (mesh->mFaces[a].mNumIndices < 3) continue;
+            mOutput << mesh->mFaces[a].mNumIndices << " ";
+        }
+        mOutput << "</vcount>" << endstr;
+
+        mOutput << startstr << "<p>";
+        for (size_t a = 0; a < mesh->mNumFaces; ++a) {
+            const aiFace &face = mesh->mFaces[a];
+            if (face.mNumIndices < 3) continue;
+            for (size_t b = 0; b < face.mNumIndices; ++b)
+                mOutput << face.mIndices[b] << " ";
+        }
+        mOutput << "</p>" << endstr;
+        PopTag();
+        mOutput << startstr << "</polylist>" << endstr;
+    }
+
+    // closing tags
+    PopTag();
+    mOutput << startstr << "</mesh>" << endstr;
+    PopTag();
+    mOutput << startstr << "</geometry>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a float array of the given type
+void ColladaExporter::WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount) {
+    size_t floatsPerElement = 0;
+    switch (pType) {
+    case FloatType_Vector: floatsPerElement = 3; break;
+    case FloatType_TexCoord2: floatsPerElement = 2; break;
+    case FloatType_TexCoord3: floatsPerElement = 3; break;
+    case FloatType_Color: floatsPerElement = 3; break;
+    case FloatType_Mat4x4: floatsPerElement = 16; break;
+    case FloatType_Weight: floatsPerElement = 1; break;
+    case FloatType_Time: floatsPerElement = 1; break;
+    default:
+        return;
+    }
+
+    std::string arrayId = XMLIDEncode(pIdString) + "-array";
+
+    mOutput << startstr << "<source id=\"" << XMLIDEncode(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
+    PushTag();
+
+    // source array
+    mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
+    PushTag();
+
+    if (pType == FloatType_TexCoord2) {
+        for (size_t a = 0; a < pElementCount; ++a) {
+            mOutput << pData[a * 3 + 0] << " ";
+            mOutput << pData[a * 3 + 1] << " ";
+        }
+    } else if (pType == FloatType_Color) {
+        for (size_t a = 0; a < pElementCount; ++a) {
+            mOutput << pData[a * 4 + 0] << " ";
+            mOutput << pData[a * 4 + 1] << " ";
+            mOutput << pData[a * 4 + 2] << " ";
+        }
+    } else {
+        for (size_t a = 0; a < pElementCount * floatsPerElement; ++a)
+            mOutput << pData[a] << " ";
+    }
+    mOutput << "</float_array>" << endstr;
+    PopTag();
+
+    // the usual Collada fun. Let's bloat it even more!
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+    mOutput << startstr << "<accessor count=\"" << pElementCount << "\" offset=\"0\" source=\"#" << arrayId << "\" stride=\"" << floatsPerElement << "\">" << endstr;
+    PushTag();
+
+    switch (pType) {
+    case FloatType_Vector:
+        mOutput << startstr << "<param name=\"X\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"Y\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"Z\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_TexCoord2:
+        mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_TexCoord3:
+        mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"P\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_Color:
+        mOutput << startstr << "<param name=\"R\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"G\" type=\"float\" />" << endstr;
+        mOutput << startstr << "<param name=\"B\" type=\"float\" />" << endstr;
+        break;
+
+    case FloatType_Mat4x4:
+        mOutput << startstr << "<param name=\"TRANSFORM\" type=\"float4x4\" />" << endstr;
+        break;
+
+    case FloatType_Weight:
+        mOutput << startstr << "<param name=\"WEIGHT\" type=\"float\" />" << endstr;
+        break;
+
+    // customized, add animation related
+    case FloatType_Time:
+        mOutput << startstr << "<param name=\"TIME\" type=\"float\" />" << endstr;
+        break;
+    }
+
+    PopTag();
+    mOutput << startstr << "</accessor>" << endstr;
+    PopTag();
+    mOutput << startstr << "</technique_common>" << endstr;
+    PopTag();
+    mOutput << startstr << "</source>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the scene library
+void ColladaExporter::WriteSceneLibrary() {
+    const std::string sceneName = XMLEscape(mScene->mRootNode->mName.C_Str());
+    const std::string sceneId = XMLIDEncode(mScene->mRootNode->mName.C_Str());
+
+    mOutput << startstr << "<library_visual_scenes>" << endstr;
+    PushTag();
+    mOutput << startstr << "<visual_scene id=\"" + sceneId + "\" name=\"" + sceneName + "\">" << endstr;
+    PushTag();
+
+    // start recursive write at the root node
+    for (size_t a = 0; a < mScene->mRootNode->mNumChildren; ++a)
+        WriteNode(mScene, mScene->mRootNode->mChildren[a]);
+
+    PopTag();
+    mOutput << startstr << "</visual_scene>" << endstr;
+    PopTag();
+    mOutput << startstr << "</library_visual_scenes>" << endstr;
+}
+// ------------------------------------------------------------------------------------------------
+void ColladaExporter::WriteAnimationLibrary(size_t pIndex) {
+    const aiAnimation *anim = mScene->mAnimations[pIndex];
+
+    if (anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels == 0)
+        return;
+
+    const std::string animation_name_escaped = XMLEscape(anim->mName.C_Str());
+    std::string idstr = anim->mName.C_Str();
+    std::string ending = std::string("AnimId") + to_string(pIndex);
+    if (idstr.length() >= ending.length()) {
+        if (0 != idstr.compare(idstr.length() - ending.length(), ending.length(), ending)) {
+            idstr = idstr + ending;
+        }
+    } else {
+        idstr = idstr + ending;
+    }
+
+    const std::string idstrEscaped = XMLIDEncode(idstr);
+
+    mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << endstr;
+    PushTag();
+
+    std::string cur_node_idstr;
+    for (size_t a = 0; a < anim->mNumChannels; ++a) {
+        const aiNodeAnim *nodeAnim = anim->mChannels[a];
+
+        // sanity check
+        if (nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys) {
+            continue;
+        }
+
+        {
+            cur_node_idstr.clear();
+            cur_node_idstr += nodeAnim->mNodeName.data;
+            cur_node_idstr += std::string("_matrix-input");
+
+            std::vector<ai_real> frames;
+            for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+                frames.push_back(static_cast<ai_real>(nodeAnim->mPositionKeys[i].mTime));
+            }
+
+            WriteFloatArray(cur_node_idstr, FloatType_Time, (const ai_real *)frames.data(), frames.size());
+            frames.clear();
+        }
+
+        {
+            cur_node_idstr.clear();
+
+            cur_node_idstr += nodeAnim->mNodeName.data;
+            cur_node_idstr += std::string("_matrix-output");
+
+            std::vector<ai_real> keyframes;
+            keyframes.reserve(nodeAnim->mNumPositionKeys * 16);
+            for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+                aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue;
+                aiMatrix4x4 ScalingM; // identity
+                ScalingM[0][0] = Scaling.x;
+                ScalingM[1][1] = Scaling.y;
+                ScalingM[2][2] = Scaling.z;
+
+                aiQuaternion RotationQ = nodeAnim->mRotationKeys[i].mValue;
+                aiMatrix4x4 s = aiMatrix4x4(RotationQ.GetMatrix());
+                aiMatrix4x4 RotationM(s.a1, s.a2, s.a3, 0, s.b1, s.b2, s.b3, 0, s.c1, s.c2, s.c3, 0, 0, 0, 0, 1);
+
+                aiVector3D Translation = nodeAnim->mPositionKeys[i].mValue;
+                aiMatrix4x4 TranslationM; // identity
+                TranslationM[0][3] = Translation.x;
+                TranslationM[1][3] = Translation.y;
+                TranslationM[2][3] = Translation.z;
+
+                // Combine the above transformations
+                aiMatrix4x4 mat = TranslationM * RotationM * ScalingM;
+
+                for (unsigned int j = 0; j < 4; ++j) {
+                    keyframes.insert(keyframes.end(), mat[j], mat[j] + 4);
+                }
+            }
+
+            WriteFloatArray(cur_node_idstr, FloatType_Mat4x4, (const ai_real *)keyframes.data(), keyframes.size() / 16);
+        }
+
+        {
+            std::vector<std::string> names;
+            for (size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) {
+                if (nodeAnim->mPreState == aiAnimBehaviour_DEFAULT || nodeAnim->mPreState == aiAnimBehaviour_LINEAR || nodeAnim->mPreState == aiAnimBehaviour_REPEAT) {
+                    names.push_back("LINEAR");
+                } else if (nodeAnim->mPostState == aiAnimBehaviour_CONSTANT) {
+                    names.push_back("STEP");
+                }
+            }
+
+            const std::string cur_node_idstr2 = nodeAnim->mNodeName.data + std::string("_matrix-interpolation");
+            std::string arrayId = XMLIDEncode(cur_node_idstr2) + "-array";
+
+            mOutput << startstr << "<source id=\"" << XMLIDEncode(cur_node_idstr2) << "\">" << endstr;
+            PushTag();
+
+            // source array
+            mOutput << startstr << "<Name_array id=\"" << arrayId << "\" count=\"" << names.size() << "\"> ";
+            for (size_t aa = 0; aa < names.size(); ++aa) {
+                mOutput << names[aa] << " ";
+            }
+            mOutput << "</Name_array>" << endstr;
+
+            mOutput << startstr << "<technique_common>" << endstr;
+            PushTag();
+
+            mOutput << startstr << "<accessor source=\"#" << arrayId << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
+            PushTag();
+
+            mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</accessor>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</technique_common>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</source>" << endstr;
+        }
+    }
+
+    for (size_t a = 0; a < anim->mNumChannels; ++a) {
+        const aiNodeAnim *nodeAnim = anim->mChannels[a];
+
+        {
+            // samplers
+            const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler");
+            mOutput << startstr << "<sampler id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
+            PushTag();
+
+            mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-input")) << "\"/>" << endstr;
+            mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-output")) << "\"/>" << endstr;
+            mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-interpolation")) << "\"/>" << endstr;
+
+            PopTag();
+            mOutput << startstr << "</sampler>" << endstr;
+        }
+    }
+
+    for (size_t a = 0; a < anim->mNumChannels; ++a) {
+        const aiNodeAnim *nodeAnim = anim->mChannels[a];
+
+        {
+            // channels
+            mOutput << startstr << "<channel source=\"#" << XMLIDEncode(nodeAnim->mNodeName.data + std::string("_matrix-sampler")) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+        }
+    }
+
+    PopTag();
+    mOutput << startstr << "</animation>" << endstr;
+}
+// ------------------------------------------------------------------------------------------------
+void ColladaExporter::WriteAnimationsLibrary() {
+    if (mScene->mNumAnimations > 0) {
+        mOutput << startstr << "<library_animations>" << endstr;
+        PushTag();
+
+        // start recursive write at the root node
+        for (size_t a = 0; a < mScene->mNumAnimations; ++a)
+            WriteAnimationLibrary(a);
+
+        PopTag();
+        mOutput << startstr << "</library_animations>" << endstr;
+    }
+}
+// ------------------------------------------------------------------------------------------------
+// Helper to find a bone by name in the scene
+aiBone *findBone(const aiScene *scene, const char *name) {
+    for (size_t m = 0; m < scene->mNumMeshes; m++) {
+        aiMesh *mesh = scene->mMeshes[m];
+        for (size_t b = 0; b < mesh->mNumBones; b++) {
+            aiBone *bone = mesh->mBones[b];
+            if (0 == strcmp(name, bone->mName.C_Str())) {
+                return bone;
+            }
+        }
+    }
+    return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiNode *findBoneNode(const aiNode *aNode, const aiBone *bone) {
+    if (aNode && bone && aNode->mName == bone->mName) {
+        return aNode;
+    }
+
+    if (aNode && bone) {
+        for (unsigned int i = 0; i < aNode->mNumChildren; ++i) {
+            aiNode *aChild = aNode->mChildren[i];
+            const aiNode *foundFromChild = 0;
+            if (aChild) {
+                foundFromChild = findBoneNode(aChild, bone);
+                if (foundFromChild) return foundFromChild;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+const aiNode *findSkeletonRootNode(const aiScene *scene, const aiMesh *mesh) {
+    std::set<const aiNode *> topParentBoneNodes;
+    if (mesh && mesh->mNumBones > 0) {
+        for (unsigned int i = 0; i < mesh->mNumBones; ++i) {
+            aiBone *bone = mesh->mBones[i];
+
+            const aiNode *node = findBoneNode(scene->mRootNode, bone);
+            if (node) {
+                while (node->mParent && findBone(scene, node->mParent->mName.C_Str()) != 0) {
+                    node = node->mParent;
+                }
+                topParentBoneNodes.insert(node);
+            }
+        }
+    }
+
+    if (!topParentBoneNodes.empty()) {
+        const aiNode *parentBoneNode = *topParentBoneNodes.begin();
+        if (topParentBoneNodes.size() == 1) {
+            return parentBoneNode;
+        } else {
+            for (auto it : topParentBoneNodes) {
+                if (it->mParent) return it->mParent;
+            }
+            return parentBoneNode;
+        }
+    }
+
+    return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively writes the given node
+void ColladaExporter::WriteNode(const aiScene *pScene, aiNode *pNode) {
+    // the node must have a name
+    if (pNode->mName.length == 0) {
+        std::stringstream ss;
+        ss << "Node_" << pNode;
+        pNode->mName.Set(ss.str());
+    }
+
+    // If the node is associated with a bone, it is a joint node (JOINT)
+    // otherwise it is a normal node (NODE)
+    const char *node_type;
+    bool is_joint, is_skeleton_root = false;
+    if (nullptr == findBone(pScene, pNode->mName.C_Str())) {
+        node_type = "NODE";
+        is_joint = false;
+    } else {
+        node_type = "JOINT";
+        is_joint = true;
+        if (!pNode->mParent || nullptr == findBone(pScene, pNode->mParent->mName.C_Str())) {
+            is_skeleton_root = true;
+        }
+    }
+
+    const std::string node_id = XMLIDEncode(pNode->mName.data);
+    const std::string node_name = XMLEscape(pNode->mName.data);
+    mOutput << startstr << "<node ";
+    if (is_skeleton_root) {
+        mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id + "\"" : ""); // For now, only support one skeleton in a scene.
+        mFoundSkeletonRootNodeID = node_id;
+    } else {
+        mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id + "\"" : "");
+    }
+
+    mOutput << " name=\"" << node_name
+            << "\" type=\"" << node_type
+            << "\">" << endstr;
+    PushTag();
+
+    // write transformation - we can directly put the matrix there
+    // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards
+    aiMatrix4x4 mat = pNode->mTransformation;
+
+    // If this node is a Camera node, the camera coordinate system needs to be multiplied in.
+    // When importing from Collada, the mLookAt is set to 0, 0, -1, and the node transform is unchanged.
+    // When importing from a different format, mLookAt is set to 0, 0, 1. Therefore, the local camera
+    // coordinate system must be changed to matche the Collada specification.
+    for (size_t i = 0; i < mScene->mNumCameras; i++) {
+        if (mScene->mCameras[i]->mName == pNode->mName) {
+            aiMatrix4x4 sourceView;
+            mScene->mCameras[i]->GetCameraMatrix(sourceView);
+
+            aiMatrix4x4 colladaView;
+            colladaView.a1 = colladaView.c3 = -1; // move into -z space.
+            mat *= (sourceView * colladaView);
+            break;
+        }
+    }
+
+    // customized, sid should be 'matrix' to match with loader code.
+    //mOutput << startstr << "<matrix sid=\"transform\">";
+    mOutput << startstr << "<matrix sid=\"matrix\">";
+
+    mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
+    mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " ";
+    mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " ";
+    mOutput << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4;
+    mOutput << "</matrix>" << endstr;
+
+    if (pNode->mNumMeshes == 0) {
+        //check if it is a camera node
+        for (size_t i = 0; i < mScene->mNumCameras; i++) {
+            if (mScene->mCameras[i]->mName == pNode->mName) {
+                mOutput << startstr << "<instance_camera url=\"#" << node_id << "-camera\"/>" << endstr;
+                break;
+            }
+        }
+        //check if it is a light node
+        for (size_t i = 0; i < mScene->mNumLights; i++) {
+            if (mScene->mLights[i]->mName == pNode->mName) {
+                mOutput << startstr << "<instance_light url=\"#" << node_id << "-light\"/>" << endstr;
+                break;
+            }
+        }
+
+    } else
+        // instance every geometry
+        for (size_t a = 0; a < pNode->mNumMeshes; ++a) {
+            const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[a]];
+            // do not instantiate mesh if empty. I wonder how this could happen
+            if (mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+                continue;
+
+            const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str();
+
+            if (mesh->mNumBones == 0) {
+                mOutput << startstr << "<instance_geometry url=\"#" << XMLIDEncode(meshName) << "\">" << endstr;
+                PushTag();
+            } else {
+                mOutput << startstr
+                        << "<instance_controller url=\"#" << XMLIDEncode(meshName) << "-skin\">"
+                        << endstr;
+                PushTag();
+
+                // note! this mFoundSkeletonRootNodeID some how affects animation, it makes the mesh attaches to armature skeleton root node.
+                // use the first bone to find skeleton root
+                const aiNode *skeletonRootBoneNode = findSkeletonRootNode(pScene, mesh);
+                if (skeletonRootBoneNode) {
+                    mFoundSkeletonRootNodeID = XMLIDEncode(skeletonRootBoneNode->mName.C_Str());
+                }
+                mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
+            }
+            mOutput << startstr << "<bind_material>" << endstr;
+            PushTag();
+            mOutput << startstr << "<technique_common>" << endstr;
+            PushTag();
+            mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLIDEncode(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
+            PushTag();
+            for (size_t aa = 0; aa < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++aa) {
+                if (mesh->HasTextureCoords(static_cast<unsigned int>(aa)))
+                    // semantic       as in <texture texcoord=...>
+                    // input_semantic as in <input semantic=...>
+                    // input_set      as in <input set=...>
+                    mOutput << startstr << "<bind_vertex_input semantic=\"CHANNEL" << aa << "\" input_semantic=\"TEXCOORD\" input_set=\"" << aa << "\"/>" << endstr;
+            }
+            PopTag();
+            mOutput << startstr << "</instance_material>" << endstr;
+            PopTag();
+            mOutput << startstr << "</technique_common>" << endstr;
+            PopTag();
+            mOutput << startstr << "</bind_material>" << endstr;
+
+            PopTag();
+            if (mesh->mNumBones == 0)
+                mOutput << startstr << "</instance_geometry>" << endstr;
+            else
+                mOutput << startstr << "</instance_controller>" << endstr;
+        }
+
+    // recurse into subnodes
+    for (size_t a = 0; a < pNode->mNumChildren; ++a)
+        WriteNode(pScene, pNode->mChildren[a]);
+
+    PopTag();
+    mOutput << startstr << "</node>" << endstr;
+}
+
+#endif
+#endif

+ 0 - 0
code/Collada/ColladaExporter.h → code/AssetLib/Collada/ColladaExporter.h


+ 0 - 0
code/Collada/ColladaHelper.cpp → code/AssetLib/Collada/ColladaHelper.cpp


+ 0 - 0
code/Collada/ColladaHelper.h → code/AssetLib/Collada/ColladaHelper.h


+ 0 - 0
code/Collada/ColladaLoader.cpp → code/AssetLib/Collada/ColladaLoader.cpp


+ 0 - 0
code/Collada/ColladaLoader.h → code/AssetLib/Collada/ColladaLoader.h


+ 0 - 0
code/Collada/ColladaParser.cpp → code/AssetLib/Collada/ColladaParser.cpp


+ 0 - 0
code/Collada/ColladaParser.h → code/AssetLib/Collada/ColladaParser.h


+ 0 - 0
code/DXF/DXFHelper.h → code/AssetLib/DXF/DXFHelper.h


+ 2 - 2
code/DXF/DXFLoader.cpp → code/AssetLib/DXF/DXFLoader.cpp

@@ -48,8 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
 
-#include "DXF/DXFLoader.h"
-#include "DXF/DXFHelper.h"
+#include "AssetLib/DXF/DXFLoader.h"
+#include "AssetLib/DXF/DXFHelper.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 
 #include <assimp/ParsingUtils.h>

+ 0 - 0
code/DXF/DXFLoader.h → code/AssetLib/DXF/DXFLoader.h


+ 0 - 0
code/FBX/FBXAnimation.cpp → code/AssetLib/FBX/FBXAnimation.cpp


+ 0 - 0
code/FBX/FBXBinaryTokenizer.cpp → code/AssetLib/FBX/FBXBinaryTokenizer.cpp


+ 0 - 0
code/FBX/FBXCommon.h → code/AssetLib/FBX/FBXCommon.h


+ 0 - 0
code/FBX/FBXCompileConfig.h → code/AssetLib/FBX/FBXCompileConfig.h


+ 2 - 1
code/FBX/FBXConverter.cpp → code/AssetLib/FBX/FBXConverter.cpp

@@ -3401,7 +3401,8 @@ void FBXConverter::ConvertGlobalSettings() {
     mSceneOut->mMetaData->Set(5, "CoordAxisSign", doc.GlobalSettings().CoordAxisSign());
     mSceneOut->mMetaData->Set(6, "OriginalUpAxis", doc.GlobalSettings().OriginalUpAxis());
     mSceneOut->mMetaData->Set(7, "OriginalUpAxisSign", doc.GlobalSettings().OriginalUpAxisSign());
-    mSceneOut->mMetaData->Set(8, "UnitScaleFactor", (double)doc.GlobalSettings().UnitScaleFactor());
+    //const double unitScaleFactor = (double)doc.GlobalSettings().UnitScaleFactor();
+    mSceneOut->mMetaData->Set(8, "UnitScaleFactor", doc.GlobalSettings().UnitScaleFactor());
     mSceneOut->mMetaData->Set(9, "OriginalUnitScaleFactor", doc.GlobalSettings().OriginalUnitScaleFactor());
     mSceneOut->mMetaData->Set(10, "AmbientColor", doc.GlobalSettings().AmbientColor());
     mSceneOut->mMetaData->Set(11, "FrameRate", (int)doc.GlobalSettings().TimeMode());

+ 0 - 0
code/FBX/FBXConverter.h → code/AssetLib/FBX/FBXConverter.h


+ 0 - 0
code/FBX/FBXDeformer.cpp → code/AssetLib/FBX/FBXDeformer.cpp


+ 0 - 0
code/FBX/FBXDocument.cpp → code/AssetLib/FBX/FBXDocument.cpp


+ 0 - 0
code/FBX/FBXDocument.h → code/AssetLib/FBX/FBXDocument.h


+ 0 - 0
code/FBX/FBXDocumentUtil.cpp → code/AssetLib/FBX/FBXDocumentUtil.cpp


+ 0 - 0
code/FBX/FBXDocumentUtil.h → code/AssetLib/FBX/FBXDocumentUtil.h


+ 0 - 0
code/FBX/FBXExportNode.cpp → code/AssetLib/FBX/FBXExportNode.cpp


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels