Explorar el Código

update assimp to 5.2.3 Bugfix-Release

AzaezelX hace 3 años
padre
commit
f297476092
Se han modificado 100 ficheros con 19755 adiciones y 10192 borrados
  1. 607 0
      Engine/lib/assimp/CHANGES
  2. 183 0
      Engine/lib/assimp/CREDITS
  3. 78 0
      Engine/lib/assimp/LICENSE
  4. 120 0
      Engine/lib/assimp/Readme.md
  5. 0 652
      Engine/lib/assimp/code/3DS/3DSHelper.h
  6. 0 104
      Engine/lib/assimp/code/3MF/3MFXmlTags.h
  7. 0 483
      Engine/lib/assimp/code/3MF/D3MFImporter.cpp
  8. 0 207
      Engine/lib/assimp/code/3MF/D3MFOpcPackage.cpp
  9. 0 705
      Engine/lib/assimp/code/AMF/AMFImporter.cpp
  10. 0 432
      Engine/lib/assimp/code/AMF/AMFImporter.hpp
  11. 0 357
      Engine/lib/assimp/code/AMF/AMFImporter_Geometry.cpp
  12. 0 166
      Engine/lib/assimp/code/AMF/AMFImporter_Macro.hpp
  13. 0 340
      Engine/lib/assimp/code/AMF/AMFImporter_Node.hpp
  14. 0 978
      Engine/lib/assimp/code/AMF/AMFImporter_Postprocess.cpp
  15. 0 846
      Engine/lib/assimp/code/Assbin/AssbinExporter.cpp
  16. 247 311
      Engine/lib/assimp/code/AssetLib/3DS/3DSConverter.cpp
  17. 155 153
      Engine/lib/assimp/code/AssetLib/3DS/3DSExporter.cpp
  18. 2 2
      Engine/lib/assimp/code/AssetLib/3DS/3DSExporter.h
  19. 702 0
      Engine/lib/assimp/code/AssetLib/3DS/3DSHelper.h
  20. 270 336
      Engine/lib/assimp/code/AssetLib/3DS/3DSLoader.cpp
  21. 16 11
      Engine/lib/assimp/code/AssetLib/3DS/3DSLoader.h
  22. 165 0
      Engine/lib/assimp/code/AssetLib/3MF/3MFTypes.h
  23. 117 0
      Engine/lib/assimp/code/AssetLib/3MF/3MFXmlTags.h
  24. 145 144
      Engine/lib/assimp/code/AssetLib/3MF/D3MFExporter.cpp
  25. 14 9
      Engine/lib/assimp/code/AssetLib/3MF/D3MFExporter.h
  26. 130 0
      Engine/lib/assimp/code/AssetLib/3MF/D3MFImporter.cpp
  27. 91 0
      Engine/lib/assimp/code/AssetLib/3MF/D3MFImporter.h
  28. 256 0
      Engine/lib/assimp/code/AssetLib/3MF/D3MFOpcPackage.cpp
  29. 13 12
      Engine/lib/assimp/code/AssetLib/3MF/D3MFOpcPackage.h
  30. 593 0
      Engine/lib/assimp/code/AssetLib/3MF/XmlSerializer.cpp
  31. 96 0
      Engine/lib/assimp/code/AssetLib/3MF/XmlSerializer.h
  32. 308 347
      Engine/lib/assimp/code/AssetLib/AC/ACLoader.cpp
  33. 64 70
      Engine/lib/assimp/code/AssetLib/AC/ACLoader.h
  34. 524 0
      Engine/lib/assimp/code/AssetLib/AMF/AMFImporter.cpp
  35. 310 0
      Engine/lib/assimp/code/AssetLib/AMF/AMFImporter.hpp
  36. 284 0
      Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Geometry.cpp
  37. 157 159
      Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Material.cpp
  38. 305 0
      Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Node.hpp
  39. 894 0
      Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  40. 273 299
      Engine/lib/assimp/code/AssetLib/ASE/ASELoader.cpp
  41. 6 21
      Engine/lib/assimp/code/AssetLib/ASE/ASELoader.h
  42. 253 376
      Engine/lib/assimp/code/AssetLib/ASE/ASEParser.cpp
  43. 109 126
      Engine/lib/assimp/code/AssetLib/ASE/ASEParser.h
  44. 68 0
      Engine/lib/assimp/code/AssetLib/Assbin/AssbinExporter.cpp
  45. 5 3
      Engine/lib/assimp/code/AssetLib/Assbin/AssbinExporter.h
  46. 833 0
      Engine/lib/assimp/code/AssetLib/Assbin/AssbinFileWriter.cpp
  47. 19 19
      Engine/lib/assimp/code/AssetLib/Assbin/AssbinFileWriter.h
  48. 228 231
      Engine/lib/assimp/code/AssetLib/Assbin/AssbinLoader.cpp
  49. 6 13
      Engine/lib/assimp/code/AssetLib/Assbin/AssbinLoader.h
  50. 18 10
      Engine/lib/assimp/code/AssetLib/Assjson/cencode.c
  51. 4 0
      Engine/lib/assimp/code/AssetLib/Assjson/cencode.h
  52. 112 111
      Engine/lib/assimp/code/AssetLib/Assjson/json_exporter.cpp
  53. 10 11
      Engine/lib/assimp/code/AssetLib/Assjson/mesh_splitter.cpp
  54. 14 23
      Engine/lib/assimp/code/AssetLib/Assjson/mesh_splitter.h
  55. 68 0
      Engine/lib/assimp/code/AssetLib/Assxml/AssxmlExporter.cpp
  56. 2 1
      Engine/lib/assimp/code/AssetLib/Assxml/AssxmlExporter.h
  57. 662 0
      Engine/lib/assimp/code/AssetLib/Assxml/AssxmlFileWriter.cpp
  58. 64 0
      Engine/lib/assimp/code/AssetLib/Assxml/AssxmlFileWriter.h
  59. 744 0
      Engine/lib/assimp/code/AssetLib/B3D/B3DImporter.cpp
  60. 13 15
      Engine/lib/assimp/code/AssetLib/B3D/B3DImporter.h
  61. 528 0
      Engine/lib/assimp/code/AssetLib/BVH/BVHLoader.cpp
  62. 25 31
      Engine/lib/assimp/code/AssetLib/BVH/BVHLoader.h
  63. 63 85
      Engine/lib/assimp/code/AssetLib/Blender/BlenderBMesh.cpp
  64. 1 1
      Engine/lib/assimp/code/AssetLib/Blender/BlenderBMesh.h
  65. 181 0
      Engine/lib/assimp/code/AssetLib/Blender/BlenderCustomData.cpp
  66. 0 0
      Engine/lib/assimp/code/AssetLib/Blender/BlenderCustomData.h
  67. 75 99
      Engine/lib/assimp/code/AssetLib/Blender/BlenderDNA.cpp
  68. 177 213
      Engine/lib/assimp/code/AssetLib/Blender/BlenderDNA.h
  69. 28 39
      Engine/lib/assimp/code/AssetLib/Blender/BlenderDNA.inl
  70. 1 1
      Engine/lib/assimp/code/AssetLib/Blender/BlenderIntermediate.h
  71. 295 334
      Engine/lib/assimp/code/AssetLib/Blender/BlenderLoader.cpp
  72. 198 0
      Engine/lib/assimp/code/AssetLib/Blender/BlenderLoader.h
  73. 89 89
      Engine/lib/assimp/code/AssetLib/Blender/BlenderModifier.cpp
  74. 11 11
      Engine/lib/assimp/code/AssetLib/Blender/BlenderModifier.h
  75. 891 0
      Engine/lib/assimp/code/AssetLib/Blender/BlenderScene.cpp
  76. 278 278
      Engine/lib/assimp/code/AssetLib/Blender/BlenderScene.h
  77. 7 1
      Engine/lib/assimp/code/AssetLib/Blender/BlenderSceneGen.h
  78. 8 2
      Engine/lib/assimp/code/AssetLib/Blender/BlenderTessellator.cpp
  79. 1 1
      Engine/lib/assimp/code/AssetLib/Blender/BlenderTessellator.h
  80. 34 35
      Engine/lib/assimp/code/AssetLib/C4D/C4DImporter.cpp
  81. 15 24
      Engine/lib/assimp/code/AssetLib/C4D/C4DImporter.h
  82. 256 288
      Engine/lib/assimp/code/AssetLib/COB/COBLoader.cpp
  83. 152 0
      Engine/lib/assimp/code/AssetLib/COB/COBLoader.h
  84. 7 8
      Engine/lib/assimp/code/AssetLib/COB/COBScene.h
  85. 7 16
      Engine/lib/assimp/code/AssetLib/CSM/CSMLoader.cpp
  86. 12 16
      Engine/lib/assimp/code/AssetLib/CSM/CSMLoader.h
  87. 1748 0
      Engine/lib/assimp/code/AssetLib/Collada/ColladaExporter.cpp
  88. 257 0
      Engine/lib/assimp/code/AssetLib/Collada/ColladaExporter.h
  89. 99 0
      Engine/lib/assimp/code/AssetLib/Collada/ColladaHelper.cpp
  90. 679 0
      Engine/lib/assimp/code/AssetLib/Collada/ColladaHelper.h
  91. 280 280
      Engine/lib/assimp/code/AssetLib/Collada/ColladaLoader.cpp
  92. 71 81
      Engine/lib/assimp/code/AssetLib/Collada/ColladaLoader.h
  93. 2402 0
      Engine/lib/assimp/code/AssetLib/Collada/ColladaParser.cpp
  94. 348 0
      Engine/lib/assimp/code/AssetLib/Collada/ColladaParser.h
  95. 2 2
      Engine/lib/assimp/code/AssetLib/DXF/DXFHelper.h
  96. 24 30
      Engine/lib/assimp/code/AssetLib/DXF/DXFLoader.cpp
  97. 8 11
      Engine/lib/assimp/code/AssetLib/DXF/DXFLoader.h
  98. 78 93
      Engine/lib/assimp/code/AssetLib/FBX/FBXAnimation.cpp
  99. 25 6
      Engine/lib/assimp/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  100. 37 34
      Engine/lib/assimp/code/AssetLib/FBX/FBXCommon.h

+ 607 - 0
Engine/lib/assimp/CHANGES

@@ -0,0 +1,607 @@
+----------------------------------------------------------------------
+CHANGELOG
+----------------------------------------------------------------------
+4.1.0 (2017-12):
+- FEATURES:
+ - Export 3MF ( experimental )
+ - Import / Export glTF 2
+ - Introduce new zib-lib to eb able to export zip-archives
+- FIXES/HOUSEKEEPING:
+ - Added missing include to stdlib.h and remove load library call
+ - Fix install for builds with MSVC compiler and NMake.
+ - Update list of supported file formats.
+ - Add TriLib to the official list of supported ports.
+ - Re-enabling PACK_STRUCT for MDL files.
+ - Use std.::unique_ptr
+ - Update D3MFExporter.h
+ - Update MD3Loader.cpp, using index
+ - Fix all warnings on MSVC14
+ - Copy assimp dll to unit folder on windows
+ - Update jvm port supported formats
+ - Add support for building Mac OS X Framework bundles
+ - Check for nullptr dereferencing before copying scene data
+ - Update ValidateDataStructure.h, typo
+ - Enable data structure validation in cases where it doesn't cause failures
+ - Remove some dead assignments
+ - fast_atof: Silence some uninitialized variable warnings
+ - Check for area test if the face is a triangle.
+ - Set mNumUVComponents to 0 when deleting texture coordinate sets
+ - Only scale the root node because this will rescale all children nodes as well.
+ - Issue 1514: Fix frame pointer arithmetic
+ - Prevent failing stringstream to crash the export process
+ - powf -> pow
+ - add Defines.h to include folder for install.
+ - Android:
+  - Fix android build
+  - Fix assimp for cross compile for android
+  - Use define for D_FILE_OFFSET_BITS only for not-android systems.
+ - FBX:
+  - Fix handling with embedded textures
+  - FBX 7500 Binary reading
+  - Remove dead assignment
+  - Fix export of deleted meshes; Add LazyDict::Remove method
+  - Log an error instead of letting the fbx-importer crash. ( issue 213 )
+  - Replace bad pointer casting with memcpy
+  - Remove useless const qualifier from return value
+  - Add explicit instantiation of log_prefix so other FBX source files can see it
+  - add missing inversion of postrotation matrix for fbx.
+  - FIReader: Silence uninitialized variable warning
+  - Update version check in FBX reader to check for version >= 7500
+  - Use actual min/max of anim keys when start/stop time is missing
+- GLTF1:
+ - Fix output of glTF 1 version string
+ - Fix delete / delete[] mismatch in glTFAsset
+ - Don’t ignore rgba(1,1,1,1) color properties
+ - glTF2 primitives fixes
+ - Don’t ignore rgba(1,1,1,1) color properties
+ - Fix delete / delete[] mismatch in glTFAsset
+ - Remove KHR_binary_glTF code
+ - glTF nodes can only hold one mesh. this simply assigns to and check’s a Node’s Mesh
+ - version in glb header is stored as uint32_t
+- GLTF2:
+ - node name conflict fix
+ - Fix transform matrices multiplication order
+ - Preserve node names when importing
+ - Add support for tangents in import
+ - Fix typo on gltf2 camera parameters
+ - Moved byteStride from accessor to bufferView
+ - Implemented reading binary glTF2 (glb) files
+ - Fix signed/unsigned warning
+ - Add postprocess step for scaling
+ - Fix shininess to roughness conversion
+ - Prefer “BLEND” over “MASK” as an alphaMode default
+ - Approximate specularity / glossiness in metallicRoughness materials
+ - Diffuse color and diffuse texture import and export improvements
+ - Addressed some mismatched news/deletes caused by the new glTF2 sources.
+ - Fix delete / delete[] mismatches in glTF2 importer
+ - use correct name of exporter to gltf2
+ - Fix possible infinite loop when exporting to gltf2
+ - Fix glTF2::Asset::FindUniqueID() when the input string is >= 256 chars
+ - Fix glTF2 alphaMode storage and reading
+ - Fix glTF 2.0 multi-primitive support
+ - Load gltf .bin files from correct directory
+ - Add support for importing both glTF and glTF2 files
+ - ampler improvements; Add new LazyDict method
+ - Changes to GLTF2 materials
+ - Remove Light, Technique references
+ - Start removing materials common, and adding pbrSpecularGlossiness
+ - Use !ObjectEmpty() vs. MemberCount() > 0
+ - Working read, import, export, and write of gltf2 (pbr) material
+ - Check in gltf2 models to test directory
+ - Remove un-needed test models
+ - Start managing and importing gltf2 pbr materials
+ - Update glTF2 Asset to use indexes
+ - Duplicate gltfImporter as gltf2Importer; Include glTF2 importer in CMake List
+ - glTF2: Fix animation export
+ - use opacity for diffuse alpha + alphaMode
+- STL:
+ - Restore import of multi mesh binary STLs
+- Blender:
+ - Silence warning about uninitialized member
+- MDLImporter:
+ - Don't take address of packed struct member
+- assimp_cmd:
+ - Fix strict-aliasing warnings
+- Open3DGC:
+ - Fix strict-aliasing warnings
+ - Add assertions to silence static analyzer warnings
+ - Remove redundant const qualifiers from return types
+ - Fix some uninitialized variable warnings
+ - Remove OPEN3DGC and compression references
+- unzip:
+ - Remove dead assignment
+ - Bail on bad compression method
+ - Fix possibly uninitialized variables
+- clipper:
+ - Add assertion to silence a static analyzer warning
+- OpenDDLExport:
+ - Reduce scope of a variable
+ - Remove dead variable
+ - Remove dead assignment
+ - Fix another potential memory leak
+- X3DImporter:
+ - Add assertions to silence static analyzer warnings
+ - Add missing unittest
+ - Workaround for buggy Android NDK (issue #1361)
+- TerragenLoader:
+ - Remove unused variable
+- SIBImporter:
+ - Add assertions to silence static analyzer warnings
+- IFC:
+ - Remove dead code
+ - Add explicit instantiation of log_prefix so IFCMaterial.cpp can see it
+- PLY:
+ - Remove dead assignment and reduce scope of a variable
+ - fix vertex attribute lookup.
+- OpenGEX:
+ - Add assertion to silence a static analyzer warning
+ - Fix for TextureFile with number in file name
+ - Return early when element is TextureFile
+- NFF:
+ - Add assertions to silence static analyzer warnings
+ - Split up some complicated assignments
+- Raw: Fix misleading indentation warning
+ - Reduce scope of a variable
+- LWO
+ - Reduce scope of a variable
+- IRRLoader:
+ - Fix confusing boolean casting
+- AssbinExporter:
+ - Add assertion to silence a static analyzer warning
+- ASE:
+ - Add assertion to silence a static analyzer warning
+- AMFImporter:
+ - Add assertion to silence a static analyzer warning
+ - Add a block
+- OptimizeGraph:
+ - Fix possible null pointer dereference
+ - RemoveRedundantMaterials:
+ - Add assertion to silence a static analyzer warning
+- ImproveCacheLocality:
+ - Add assertion to silence a static analyzer warning
+- RemoveRedundantMaterials:
+ - Set pointer to nullptr after deleting it
+- Travis:
+ - Disable unit tests in scan-build config
+ - Move slower builds earlier to improve parallelization
+ - Add static analysis to build
+ - Remove unused branch rule for travis.
+ - Add Clang UBSan build configuration
+ - Treat warnings as errors, without typos this time
+- Unittests:
+ - Add VS-based source groups for the unittests.
+- Collada:
+ - export <library_animations> tag
+ - Update ColladaExporter.cpp
+ - Silence uninitialized variable warning
+ - Add support for line strip primitives
+- Obj Wavefront:
+ - check in exporting against out-of-bounds-access .
+ - Issue 1351: use correct name for obj-meshname export for groups.
+ - fix mem-lead: face will be not released in case of an error.
+ - Anatoscope obj exporter nomtl
+ - Raise exception when obj file contains invalid face indices
+ - Added alternative displacement texture token in OBJ MTL material.
+ - Obj: rename attribute from exporter.
+ - Fix OBJ discarding all material names if the material library is missing
+- Step:
+ - use correct lookup for utf32
+- MD2:
+ - Fix MD2 frames containing garbage
+- STL
+ - add missing const.
+ - Fix memory-alignment bug.
+ - Fix issue 104: deal with more solids in one STL file.
+- CMake
+ - Fix issue 213: use correct include folder for assimp
+- Doxygen
+ - Fix issue 1513: put irrXML onto exclucde list for doxygen run
+- PyAssimp:
+ - Search for libassimp.so in LD_LIBRARY_PATH if available.
+ - Fix operator precedence issue in header check
+ - Split setup.py into multiple lines
+ - Detect if Anaconda and fixed 3d_viewer for Python 3
+ - created a python3 version of the 3dviewer and fixed the / = float in py3
+- Blender:
+ - Fix invalid access to mesh array when the array is empty.
+ - Fix short overflow.
+ - Silence warning about inline function which is declared but not defined
+- JAssimp
+ - Changed license header for IHMC contributions from Apache 2.0 to BSD
+ - Add Node metadata to the Jassmip Java API
+ - Added supported for custom IO Systems in Java. Implemented ClassLoader IO System
+ - Added a link to pure jvm assimp port
+- Clang sanitizer:
+ - Undefined Behavior sanitizer
+ - Fixed a divide by zero error in IFCBoolean that was latent, but nevertheless a bug
+- B3DImporter:
+ - Replace bad pointer casting with memcpy
+- AppVeyor:
+ - Cleanup and Addition of VS 2017 and running Tests
+ - Fixed File Size reported as 0 in tests that use temporary files
+ - x86 isn't a valid VS platform. Win32 it is, then.
+ - Replaced the worker image name, which doesn't work as generator name, with a manually created generator name.
+ - Cleaned up appveyor setup, added VS 2017 to the build matrix and attempted to add running of tests.
+ - Treat warnings as errors on Appveyor
+ - Disable warning 4351 on MSVC 2013
+- OpenGEXImporter:
+ - Copy materials to scene
+ - Store RefInfo in unique_ptr so they get automatically cleaned up
+ - Fix IOStream leak
+ - Store ChildInfo in unique_ptr so they get automatically cleaned up
+ - improve logging to be able to detect error-prone situations.
+-  AMFImporter:
+ - Fix memory leak
+- UnrealLoader:
+ - Fix IOStream leak
+- Upgrade RapidJSON to get rid of a clang warning
+- zlib:
+ - Update zlib contribution
+ - Removed unnecessary files from zlib contribution
+ - Replaced unsigned long for the crc table to z_crc_t, to match what is returned by get-crc_table
+- MakeVerboseFormat:
+  - Fix delete / delete[] mismatches in MakeVerboseFormat
+- MaterialSystem:
+ - Fix out-of-bounds read in MaterialSystem unit test
+- SIB:
+ - Added support for SIB models from Silo 2.5
+- AssbinExporter:
+ - Fix strict aliasing violation
+ - Add Write specialization for aiColor3D
+- DefaultLogger:
+ - Whitespace cleanup to fix GCC misleading indentation warning
+- MDP:
+ - Fix encoding issues.
+ - PreTransformVertices:
+ - fix name lost in mesh and nodes when load with flag
+- C4D:
+ - Fixes for C4D importer
+- Unzip:
+ - Latest greatest.
+
+4.0.1 (2017-07-28)
+    - FIXES/HOUSEKEEPING:
+    - fix version test.
+    - Not compiling when using ASSIMP_DOUBLE_PRECISION
+    - Added support for python3
+    - Check if cmake is installed with brew
+    - Low performance in OptimizeMeshesProcess::ProcessNode with huge numbers of meshes
+    - Elapsed seconds not shown correctly
+    - StreamReader: fix out-of-range exception
+    - PPdPmdParser: fix compilation for clang
+
+
+4.0.0 (2017-07-18)
+
+FEATURES:
+    - Double precision support provided ( available via cmake option )
+	- QT-Widget based assimp-viewer ( works for windows, linux, osx )
+	- Open3DGC codec supported by glFT-importer
+	- glTF: Read and write transparency values
+	- Add Triangulate post-processing step to glTF exporters
+	- Update rapidjson to v1.0.2
+	- Added method to append new metadata to structure
+	- Unittests: intoduce a prototype model differ
+	- X3D support
+	- AMF support
+	- Lugdunum3D support
+	- Obj-Importer: obj-homogeneous_coords support
+	- Obj-Importer: new streaming handling
+	- Added support for 64 bit version header introduced in FbxSdk2016
+	- Travis: enable coverall support.
+	- PyAssimp: New version of the pyASSIMP 3D viewer, with much improved 3D controls
+    - Morph animation support for collada	
+	- Added support for parameters Ni and Tf in OBJ/MTL file format
+	- aiScene: add method to add children
+	- Added new option to IFC importer to control tessellation angle + removed unused IFC option
+	- aiMetaData: introduce aiMetaData::Dealloc
+	- Samples: add a DX11 example
+	- travis ci: test on OXS ( XCode 6.3 ) as well
+	- travis ci: enable sudo support.
+	- openddlparser: integrate release v0.4.0
+	- aiMetaData: Added support for metadata in assbin format
+	
+FIXES/HOUSEKEEPING:
+    - Introduce usage of #pragma statement
+	- Put cmake-scripts into their own folder
+	- Fix install pathes ( issue 938 )
+	- Fix object_compare in blender importer( issue 946 )
+	- Fix OSX compilation error
+	- Fix unzip path when no other version was found ( issue 967 )
+	- Set _FILE_OFFSET_BITS=64 for 32-bit linux ( issue 975 )
+	- Fix constructor for radjson on OSX
+	- Use Assimp namespace to fix build for big-endian architectures
+	- Add -fPIC to C Flags for 64bit linux Shared Object builds
+	- MDLLoader: fix resource leak.
+	- MakeVerboseFormat: fix invalid delete statement
+	- IFC: fix possible use after free access bug
+	- ComputeUVMappingprocess: add missing initialization for scalar value
+    - Fix invalid release of mat + mesh
+	- IrrImporter: Fix release functions
+	- Split mesh before exporting gltf ( issue 995 )
+	- 3MFImporter: add source group for visual studio
+	- IFC: Switch generated file to 2 files to fix issue related to <mingw4.9 ( Thanks Qt! )
+	- ObjImporter: fix test for vertices import
+    - export scene combiner ( issues177 )
+	- FBX: make lookup test less strict ( issues 994 )
+	- OpenGEX-Importer: add import of vertex colors ( issue 954 )
+	- fix bug when exporting mRotationKeys data
+	- fix mingw build (mingw supports stat64 nowadays)
+	- cfileio: fix leaks by not closing files in the destructor
+	- Fix OBJ parser mtllib statement parsing bug.
+	- Q3BSP-Importer: remove dead code
+	- Fix BlenderDNA for clang cross compiler.
+	- ScenePreprocessor: fix invalid index counter.
+	- Fix compiler warnings ( issue 957 )
+	- Fix obj .mtl file loading
+	- Fixed a compile error on MSVC14 x64 caused by the /bigobj flag failing to be set for the 1 and 2-suffixed versions introduced in commit 0a25b076b8968b7ea2aa96d7d1b4381be2d72ce6
+	- Fixed build warnings on MSVC14 x64
+	- Remove scaling of specular exponent in OBJFileImporter.cpp
+	- use ai_assert instead of assert ( issue 1076 )
+	- Added a preprocessor definition for MSVC to silence safety warnings regarding C library functions. This addresses all warnings for MSVC x86 and x64 when building zlib, tools and viewer as a static lib
+	- fix parsing of texture name ( issue 899 )
+	- add warning when detecting invalid mat definition ( issue 1111 )
+	- copy aiTexture type declaration instead of using decltype for declaration to fix iOS build( issue 1101 )
+	- FBX: Add additional material properties
+	- FBX: Correct camera position and clip planes
+    - FBX: Add correct light locations and falloff values
+	- fix typo ( issue 1141 )
+	- Fix collada export. Don't duplicate TEXCOORD/NORMALS/COLORS in <vertices> and <polylist> ( issue 1084 )
+	- OBJParser: set material index when changing current material
+	- OBJ: check for null mesh before updating material index
+	- add vertex color export support ( issue 809 )
+	- Fix memory leak in Collada importer ( issue 1169 )
+	- add stp to the list of supported extensions for step-files ( issue 1183 )
+	- fix clang build ( Issue-1169 ) 
+	- fix for FreeBSD
+	- Import FindPkgMacros to main CMake Configuration
+	- Extended support for tessellation parameter to more IFC shapes
+	- defensice handling of utf-8 decode issues ( issue 1211 )
+	- Fixed compiler error on clang 4.0 running on OSX
+	- use test extension for exported test files ( issue 1228 )
+	- Set UVW index material properties for OBJ files
+	- Fixed no member named 'atop' in global namespace issue for Android NDK compilation
+	- Apply mechanism to decide use for IrrXML external or internal
+	- Fix static init ordering bug in OpenGEX importer
+	- GLTF exporter: ensure animation accessors have same count
+	- GLTF exporter: convert animation time from ticks to seconds
+	- Add support for reading texture coordinates from PLY meshes with properties named 'texture_u' and 'texture_v'
+	- Added TokensForSearch in BlenderLoader to allow CanRead return true for in-memory files.
+	- fix wrong delete ( issue 1266 )
+	- OpenGEX: fix invalid handling with color4 token ( issue 1262 )
+	- LWOLoader: fix link in loader description
+	- Fix error when custom CMAKE_C_FLAGS is specified
+	- Fast-atof: log overflow errors
+	- Obj-Importer: do not break when detecting an overflow ( issue 1244 )
+	- Obj-Importer: fix parsing of multible line data definitions
+	- Fixed bug where IFC models with multiple IFCSite only loaded 1 site instead of the complete model
+	- PLYImporter: - optimize memory and speed on ply importer / change parser to use a file stream - manage texture path in ply 
+	  import - manage texture coords on faces in ply import - correction on point cloud faces generation
+	- Utf8: integrate new lib ( issue 1158 )
+	- fixed CMAKE_MODULE_PATH overwriting previous values
+	- OpenGEX: Fixed bug in material color processing ( issue 1271 )
+	- SceneCombiner: move header for scenecombiner to public folder.
+	- GLTF exporter: ensure buffer view byte offsets are correctly aligned
+	- X3D importer: Added EXPORT and IMPORT to the list of ignored XML tags
+    - X3D Exporter: fixed missing attributes
+    - X3D importer: Fixed import of normals for the single index / normal per vertex case
+    - X3D importer: Fixed handling of inlined files
+    - X3D importer: fixed whitespace handling (issue 1202)
+	- X3D importer: Fixed iterator on MSVC 2015
+    - X3D importer: Fixed problems with auto, override and regex on older compilers
+    - X3D importer: Fixed missing header file
+    - X3D importer: Fixed path handling
+	- X3D importer: Implemented support for binary X3D files
+    - fix build without 3DS ( issue 1319 )
+	- pyassimp: Fixed indices for IndexedTriangleFanSet, IndexedTriangleSet and IndexedTriangleStripSet
+	- Fixes parameters to pyassimp.load
+	- Obj-Importe: Fixed texture bug due simultaneously using 'usemtl' and 'usemap' attributes
+	- check if all exporters are disabled ( issue 1320 )
+	- Remove std functions deprecated by C++11.
+	- X-Importer: make it deal with lines
+	- use correct path for compilers ( issue 1335 )
+	- Collada: add workaround to deal with polygon with holes 
+	- update python readme
+	- Use unique node names when loading Collada files
+	- Fixed many FBX bugs
+
+API COMPATIBILITY:
+    - Changed ABI-compatibility to v3.3.1, please rebuild your precompiled libraries ( see issue 1182 )
+	- VS2010 outdated
+
+3.3.1 (2016-07-08)
+
+FIXES/HOUSEKEEPING:
+    - Setup of default precision for 17 exporters
+    - Fix xcode project files
+    - Fix BlenderTesselator: offsetof operator
+    - Invalid version in cmake file
+    - Update pstdint.h to latest greatest
+
+
+3.3.0 (2016-07-05)
+
+FEATURES:
+    - C++11 support enabled
+    - New regression-test-UI
+    - Experimental glTF-importer support
+    - OpenGEX: add support for cameras and lights
+    - C4D: update to latest Melange-SDK
+    - Add a gitter channel
+    - Coverity check enabled
+    - Switch to <...> include brackets for public headers 
+    - Enable export by pyAssimp
+    - CI: check windows build
+    - Add functionality to perform a singlepost-processing step
+    - many more, just check the history
+
+FIXES/HOUSEKEEPING:
+    - Fix of many resource leaks in unittests and main lib
+    - Fix iOS-buildfor X64
+    - Choosing zlib manually for cmake
+    - many more, just check the history
+
+
+3.2.1 (2016-010-10)
+
+FEATURES:
+  - Updated glTF exporter to meet 1.0 specification.
+
+FIXES/HOUSEKEEPING:
+  - Fixed glTF Validator errors for exported glTF format.
+
+ISSUES:
+  - Hard coded sampler setting for
+    - magFilter
+    - minFilter
+  - void* in ExportData for accessor max and min.
+
+
+3.2.0 (2015-11-03)
+
+FEATURES:
+  - OpenDDL-Parser is part of contrib-source.
+  - Experimental OpenGEX-support
+  - CI-check for linux and windows
+  - Coverity check added
+  - New regression testsuite.
+
+FIXES/HOUSEKEEPING:
+  - Hundreds of bugfixes  in all parts of the library
+  - Unified line endings
+
+
+API COMPATIBILITY:
+  - Removed precompiled header to increase build speed for linux
+
+
+3.1.1 (2014-06-15)
+
+FEATURES:
+   - Support for FBX 2013 and newer, binary and ASCII (this is partly
+     work from Google Summer of Code 2012)
+   - Support for OGRE binary mesh and skeleton format
+   - Updated BLEND support for newer Blender versions
+   - Support for arbitrary meta data, used to hold FBX and DAE metadata
+   - OBJ Export now produces smaller files
+   - Meshes can now have names, this is supported by the major importers
+   - Improved IFC geometry generation
+   - M3 support has been removed
+
+FIXES/HOUSEKEEPING:
+    - Hundreds of bugfixes in all parts of the library
+    - CMake is now the primary build system
+
+API COMPATIBILITY:
+    - 3.1.1 is not binary compatible to 3.0 due to aiNode::mMetaData
+      and aiMesh::mName
+    - Export interface has been cleaned up and unified
+    - Other than that no relevant changes
+
+
+3.0 (2012-07-07)
+
+FEATURES:
+   - new export interface similar to the import API.
+   - Supported export formats: Collada, OBJ, PLY and STL
+   - added new import formats: XGL/ZGL, M3 (experimental)
+   - new postprocessing steps: Debone
+   - vastly improved IFC (Industry Foundation Classes) support
+   - introduced API to query importer meta information (such as supported
+       format versions, full name, maintainer info).
+   - reworked Ogre XML import
+   - C-API now supports per-import properties
+
+FIXES/HOUSEKEEPING:
+
+   - hundreds of bugfixes in all parts of the library
+   - unified naming and cleanup of public headers
+   - improved CMake build system
+   - templatized math library
+   - reduce dependency on boost.thread, only remaining spot
+     is synchronization for the C logging API
+
+API COMPATIBILITY:
+   - renamed headers, export interface, C API properties and meta data
+     prevent compatibility with code written for 2.0, but in
+     most cases these can be easily resolved
+   - Note: 3.0 is not binary compatible with 2.0
+
+
+2.0 (2010-11-21)
+
+FEATURES:
+   - Add support for static Blender (*.blend) scenes
+   - Add support for Q3BSP scenes
+   - Add a windows-based OpenGL sample featuring texturing & basic materials
+   - Add an experimental progress feedback interface.
+   - Vastly improved performance (up to 500%, depending on mesh size and
+     spatial structure) in some expensive postprocessing steps
+   - AssimpView now uses a reworked layout which leaves more space
+     to the scene hierarchy window
+
+   - Add C# bindings ('Assimp.NET')
+   - Keep BSD-licensed and otherwise free test files in separate
+     folders (./test/models and ./test/models-nonbsd).
+
+FIXES:
+   - Many Collada bugfixes, improve fault tolerance
+   - Fix possible crashes in the Obj loader
+   - Improve the Ogre XML loader
+   - OpenGL-sample now works with MinGW
+   - Fix Importer::FindLoader failing on uppercase file extensions
+   - Fix flawed path handling when locating external files
+   - Limit the maximum number of vertices, faces, face indices and
+     weights that Assimp is able to handle. This is to avoid
+     crashes due to overflowing counters.
+
+   - Updated XCode project files
+   - Further CMAKE build improvements
+
+
+API CHANGES:
+   - Add data structures for vertex-based animations (These are not
+     currently used, however ...)
+   - Some Assimp::Importer methods are const now.
+
+   
+1.1 (2010-04-17)
+This is the list of relevant changes from the 1.0 (r412) release to 1.1 (r700).
+
+FEATURES:
+  - Vastly improved Collada support
+  - Add MS3D (Milkshape 3D) support
+  - Add support for Ogre XML static meshes
+  - Add experimental COB (TrueSpace) support
+  - Automatic test suite to quickly locate regressions
+  - D bindings (`dAssimp`)
+  - Python 2.n bindings (`PyAssimp`)
+  - Add basic support for Unicode input files (utf8, utf16 and utf32)
+  - Add further utilities to the `assimp` tool (xml/binary dumps, quick file stats)
+  - Switch to a CMAKE-based build system including an install target for unix'es
+  - Automatic evaluation of subdivision surfaces for some formats.
+  - Add `Importer::ReadFileFromMemory` and the corresponding C-API `aiReadFileFromMemory`
+  - Expose further math utilities via the C-API (i.e. `aiMultiplyMatrix4`)
+
+  - Move noboost files away from the public include directory
+  - Many, many bugfixes and improvements in existing loaders and postprocessing steps
+  - Documentation improved and clarified in many places.
+  - Add a sample on using Assimp in conjunction with OpenGL
+
+  - Distribution/packaging: comfortable SDK installer for Windows
+  - Distribution/packaging: improved release packages for other architectures
+
+CRITICAL FIXES:
+  - Resolve problems with clashing heap managers, STL ABIs and runtime libraries (win32)
+  - Fix automatic detection of file type if no file extension is given
+  - Improved exception safety and robustness, prevent leaking of exceptions through the C interface
+  - Fix possible heap corruption due to material properties pulled in incorrectly
+  - Avoid leaking in certain error scenarios
+  - Fix 64 bit compatibility problems in some loaders (i.e. MDL)
+
+BREAKING API CHANGES:
+  - None -
+
+MINOR API BEHAVIOUR CHANGES:
+ - Change quaternion orientation to suit to the more common convention (-w).
+ - aiString is utf8 now. Not yet consistent, however.

+ 183 - 0
Engine/lib/assimp/CREDITS

@@ -0,0 +1,183 @@
+===============================================================
+Open Asset Import Library (Assimp)
+Developers and Contributors
+===============================================================
+
+The following is a non-exhaustive list of all constributors over the years.
+If you think your name should be listed here, drop us a line and we'll add you.
+
+- Alexander Gessler,
+3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design).
+
+- Thomas Schulze,
+X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation.
+
+- Kim Kulling,
+Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ).
+
+- R.Schmidt,
+Linux build, eclipse support.
+
+- Matthias Gubisch,
+Assimp.net
+Visual Studio 9 support, bugfixes.
+
+- Mark Sibly
+B3D-Loader, Assimp testing
+
+- Jonathan Klein
+Ogre Loader, VC2010 fixes and CMake fixes.
+
+- Sebastian Hempel,
+PyAssimp (first version)
+Compile-Bugfixes for mingw, add environment for static library support in make.
+
+- Jonathan Pokrass
+Supplied a bugfix concerning the scaling in the md3 loader.
+
+- Andrew Galante,
+Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace.
+
+- Andreas Nagel
+First Assimp testing & verification under Windows Vista 64 Bit.
+
+- Marius Schr�der
+Allowed us to use many of his models for screenshots and testing.
+
+- Christian Schubert
+Supplied various XFiles for testing purposes.
+
+- Tizian Wieland
+Searched the web for hundreds of test models for internal use
+
+- John Connors
+Supplied patches for linux and SCons.
+
+- T. R.
+The GUY who performed some of the CSM mocaps.
+
+- Andy Maloney
+Contributed fixes for the documentation and the doxygen markup
+
+- Zhao Lei
+Contributed several bugfixes fixing memory leaks and improving float parsing 
+
+- sueastside
+Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
+
+- Tobias Rittig
+Collada testing with Cinema 4D
+
+- Brad Grantham
+Improvements in OpenGL-Sample.
+
+- Robert Ramirez
+Add group loading feature to Obj-Loader.
+
+- Chris Maiwald
+Many bugreports, improving Assimp's portability, regular testing & feedback.
+
+- Stepan Hrbek
+Bugreport and fix for a obj-materialloader crash.
+
+- David Nadlinger
+D bindings, CMake install support.
+
+- Dario Accornero
+Contributed several patches regarding Mac OS/XCode targets, bug reports.
+
+- Martin Walser (Samhayne)
+Contributed the 'SimpleTexturedOpenGl' sample.
+
+- Matthias Fauconneau
+Contributed a fix for the Q3-BSP loader.
+
+- Jørgen P. Tjernø
+Contributed updated and improved xcode workspaces
+
+- drparallax
+Contributed the /samples/SimpleAssimpViewX sample
+
+- Carsten Fuchs
+Contributed a fix for the Normalize method in aiQuaternion.
+
+- dbburgess
+Contributes a Android-specific build issue: log the hardware architecture for ARM.
+
+- alfiereinre7
+Contributes a obj-fileparser fix: missing tokens in the obj-token list.
+
+- Roman Kharitonov
+Contributes a fix for the configure script environment.
+
+- Ed Diana
+Contributed AssimpDelphi (/port/AssimpDelphi).
+
+- rdb
+Contributes a bundle of fixes and improvements for the bsp-importer.
+
+- Mick P
+For contributing the De-bone postprocessing step and filing various bug reports.
+
+- Rosen Diankov
+Contributed patches to build assimp debian packages using cmake.
+
+- Mark Page
+Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
+
+- IOhannes
+Contributed the Debian build fixes ( architecture macro ).
+
+- gellule
+Several LWO and LWS fixes (pivoting). 
+
+- Marcel Metz
+GCC/Linux fixes for the SimpleOpenGL sample.
+
+- Brian Miller
+Bugfix for a compiler fix for iOS on arm.
+
+- Séverin Lemaignan
+Rewrite of PyAssimp, distutils and Python3 support
+
+- albert-wang
+Bugfixes for the collada parser
+
+- Ya ping Jin
+Bugfixes for uv-tanget calculation.
+
+- Jonne Nauha
+Ogre Binary format support
+
+- Filip Wasil, Tieto Poland Sp. z o.o.
+Android JNI asset extraction support
+
+- Richard Steffen
+Contributed ExportProperties interface
+Contributed X File exporter
+Contributed Step (stp) exporter
+
+- Thomas Iorns (mesilliac)
+Initial FBX Export support
+
+For a more detailed list just check: https://github.com/assimp/assimp/network/members
+
+
+========
+Patreons
+========
+
+Huge thanks to our Patreons!
+
+- migenius
+- Marcus
+- Cort
+- elect
+- Steffen
+
+
+===================
+Commercial Sponsors
+===================
+
+- MyDidimo (mydidimo.com): Sponsored development of FBX Export support

+ 78 - 0
Engine/lib/assimp/LICENSE

@@ -0,0 +1,78 @@
+Open Asset Import Library (assimp)
+
+Copyright (c) 2006-2021, 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.
+
+
+
+******************************************************************************
+
+AN EXCEPTION applies to all files in the ./test/models-nonbsd folder.
+These are 3d models for testing purposes, from various free sources
+on the internet. They are - unless otherwise stated - copyright of
+their respective creators, which may impose additional requirements
+on the use of their work. For any of these models, see
+<model-name>.source.txt for more legal information. Contact us if you
+are a copyright holder and believe that we credited you inproperly or
+if you don't want your files to appear in the repository.
+
+
+******************************************************************************
+
+Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+http://code.google.com/p/poly2tri/
+
+All rights reserved.
+Redistribution and use 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 Poly2Tri nor the names of its contributors may be
+  used to endorse or promote products derived from this software without specific
+  prior written permission.
+
+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.

+ 120 - 0
Engine/lib/assimp/Readme.md

@@ -0,0 +1,120 @@
+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)
+<a href="https://scan.coverity.com/projects/5607">
+  <img alt="Coverity Scan Build Status"
+       src="https://scan.coverity.com/projects/5607/badge.svg"/>
+</a>
+[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9973693b7bdd4543b07084d5d9cf4745)](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=assimp/assimp&amp;utm_campaign=Badge_Grade)
+[![Coverage Status](https://coveralls.io/repos/github/assimp/assimp/badge.svg?branch=master)](https://coveralls.io/github/assimp/assimp?branch=master)
+[![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue")
+[![Total alerts](https://img.shields.io/lgtm/alerts/g/assimp/assimp.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/assimp/assimp/alerts/)
+<br>
+
+APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
+Additionally, assimp features various __mesh post processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
+
+### Latest Doc's ###
+Please check the latest documents at [Asset-Importer-Lib-Doc](https://assimp-docs.readthedocs.io/en/latest/). 
+
+### Get involved ###
+This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
+<br>
+You find a bug in the docs? Use [Doc-Repo](https://github.com/assimp/assimp-docs).
+<br>
+Please check our Wiki as well: https://github.com/assimp/assimp/wiki
+
+If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
+
+#### Supported file formats ####
+You can find the complete list of supported file-formats [here](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md)
+
+### Building ###
+Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
+
+### Ports ###
+* [Android](port/AndroidJNI/README.md)
+* [Python](port/PyAssimp/README.md)
+* [.NET](https://bitbucket.org/Starnick/assimpnet/src/master/)
+* [Pascal](port/AssimpPascal/Readme.md)
+* [Javascript (Alpha)](https://github.com/makc/assimp2json)
+* [Javascript/Node.js Interface](https://github.com/kovacsv/assimpjs)
+* [Unity 3d Plugin](https://ricardoreis.net/trilib-2/)
+* [Unreal Engine Plugin](https://github.com/irajsb/UE4_Assimp/)
+* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status))
+* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
+* [Rust](https://github.com/jkvargas/russimp)
+
+### Other tools ###
+[open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.
+
+#### Repository structure ####
+Open Asset Import Library is implemented in C++. The directory structure looks like:
+
+	/code		Source code
+	/contrib	Third-party libraries
+	/doc		Documentation (doxysource and pre-compiled docs)
+	/include	Public header C and C++ header files
+	/scripts 	Scripts used to generate the loading code for some formats
+	/port		Ports to other languages and scripts to maintain those.
+	/test		Unit- and regression tests, test suite of models
+	/tools		Tools (old assimp viewer, command line `assimp`)
+	/samples	A small number of samples to illustrate possible
+                        use cases for Assimp
+
+The source code is organized in the following way:
+
+	code/Common			The base implementation for importers and the infrastructure
+	code/PostProcessing		The post-processing steps
+	code/AssetLib/<FormatName>	Implementation for import and export for the format
+
+### Where to get help ###
+For more information, visit [our website](http://assimp.org/). Or check out the `./doc`- folder, which contains the official documentation in HTML format.
+(CHMs for Windows are included in some release packages and should be located right here in the root folder).
+
+If the docs don't solve your problem, ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
+
+Open Asset Import Library is a library to load various 3d file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
+
+And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)<br>
+
+### Contributing ###
+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.
+
+An _informal_ summary is: do whatever you want, but include Assimp's license text with your product -
+and don't sue us if our code doesn't work. Note that, unlike LGPLed code, you may link statically to Assimp.
+For the legal details, see the `LICENSE` file.
+
+### Why this name ###
+Sorry, we're germans :-), no english native speakers ...

+ 0 - 652
Engine/lib/assimp/code/3DS/3DSHelper.h

@@ -1,652 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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 Defines helper data structures for the import of 3DS files */
-
-#ifndef AI_3DSFILEHELPER_H_INC
-#define AI_3DSFILEHELPER_H_INC
-
-#include <assimp/SpatialSort.h>
-#include <assimp/SmoothingGroups.h>
-#include <assimp/StringUtils.h>
-#include <assimp/qnan.h>
-#include <assimp/material.h>
-#include <assimp/camera.h>
-#include <assimp/light.h>
-#include <assimp/anim.h>
-#include <stdio.h> //sprintf
-
-namespace Assimp    {
-namespace D3DS  {
-
-#include <assimp/Compiler/pushpack1.h>
-
-// ---------------------------------------------------------------------------
-/** Discreet3DS class: Helper class for loading 3ds files. Defines chunks
-*  and data structures.
-*/
-class Discreet3DS {
-private:
-    Discreet3DS() AI_NO_EXCEPT {
-        // empty
-    }
-
-    ~Discreet3DS() {
-        // empty
-    }
-
-public:
-    //! data structure for a single chunk in a .3ds file
-    struct Chunk {
-        uint16_t    Flag;
-        uint32_t    Size;
-    } PACK_STRUCT;
-
-
-    //! Used for shading field in material3ds structure
-    //! From AutoDesk 3ds SDK
-    typedef enum
-    {
-        // translated to gouraud shading with wireframe active
-        Wire = 0x0,
-
-        // if this material is set, no vertex normals will
-        // be calculated for the model. Face normals + gouraud
-        Flat = 0x1,
-
-        // standard gouraud shading
-        Gouraud = 0x2,
-
-        // phong shading
-        Phong = 0x3,
-
-        // cooktorrance or anistropic phong shading ...
-        // the exact meaning is unknown, if you know it
-        // feel free to tell me ;-)
-        Metal = 0x4,
-
-        // required by the ASE loader
-        Blinn = 0x5
-    } shadetype3ds;
-
-    // Flags for animated keys
-    enum
-    {
-        KEY_USE_TENS         = 0x1,
-        KEY_USE_CONT         = 0x2,
-        KEY_USE_BIAS         = 0x4,
-        KEY_USE_EASE_TO      = 0x8,
-        KEY_USE_EASE_FROM    = 0x10
-    } ;
-
-    enum
-    {
-
-        // ********************************************************************
-        // Basic chunks which can be found everywhere in the file
-        CHUNK_VERSION   = 0x0002,
-        CHUNK_RGBF      = 0x0010,       // float4 R; float4 G; float4 B
-        CHUNK_RGBB      = 0x0011,       // int1 R; int1 G; int B
-
-        // Linear color values (gamma = 2.2?)
-        CHUNK_LINRGBF      = 0x0013,    // float4 R; float4 G; float4 B
-        CHUNK_LINRGBB      = 0x0012,    // int1 R; int1 G; int B
-
-        CHUNK_PERCENTW  = 0x0030,       // int2   percentage
-        CHUNK_PERCENTF  = 0x0031,       // float4  percentage
-        CHUNK_PERCENTD  = 0x0032,       // float8  percentage
-        // ********************************************************************
-
-        // Prj master chunk
-        CHUNK_PRJ       = 0xC23D,
-
-        // MDLI master chunk
-        CHUNK_MLI       = 0x3DAA,
-
-        // Primary main chunk of the .3ds file
-        CHUNK_MAIN      = 0x4D4D,
-
-        // Mesh main chunk
-        CHUNK_OBJMESH   = 0x3D3D,
-
-        // Specifies the background color of the .3ds file
-        // This is passed through the material system for
-        // viewing purposes.
-        CHUNK_BKGCOLOR  = 0x1200,
-
-        // Specifies the ambient base color of the scene.
-        // This is added to all materials in the file
-        CHUNK_AMBCOLOR  = 0x2100,
-
-        // Specifies the background image for the whole scene
-        // This value is passed through the material system
-        // to the viewer
-        CHUNK_BIT_MAP   = 0x1100,
-        CHUNK_BIT_MAP_EXISTS  = 0x1101,
-
-        // ********************************************************************
-        // Viewport related stuff. Ignored
-        CHUNK_DEFAULT_VIEW = 0x3000,
-        CHUNK_VIEW_TOP = 0x3010,
-        CHUNK_VIEW_BOTTOM = 0x3020,
-        CHUNK_VIEW_LEFT = 0x3030,
-        CHUNK_VIEW_RIGHT = 0x3040,
-        CHUNK_VIEW_FRONT = 0x3050,
-        CHUNK_VIEW_BACK = 0x3060,
-        CHUNK_VIEW_USER = 0x3070,
-        CHUNK_VIEW_CAMERA = 0x3080,
-        // ********************************************************************
-
-        // Mesh chunks
-        CHUNK_OBJBLOCK  = 0x4000,
-        CHUNK_TRIMESH   = 0x4100,
-        CHUNK_VERTLIST  = 0x4110,
-        CHUNK_VERTFLAGS = 0x4111,
-        CHUNK_FACELIST  = 0x4120,
-        CHUNK_FACEMAT   = 0x4130,
-        CHUNK_MAPLIST   = 0x4140,
-        CHUNK_SMOOLIST  = 0x4150,
-        CHUNK_TRMATRIX  = 0x4160,
-        CHUNK_MESHCOLOR = 0x4165,
-        CHUNK_TXTINFO   = 0x4170,
-        CHUNK_LIGHT     = 0x4600,
-        CHUNK_CAMERA    = 0x4700,
-        CHUNK_HIERARCHY = 0x4F00,
-
-        // Specifies the global scaling factor. This is applied
-        // to the root node's transformation matrix
-        CHUNK_MASTER_SCALE    = 0x0100,
-
-        // ********************************************************************
-        // Material chunks
-        CHUNK_MAT_MATERIAL  = 0xAFFF,
-
-            // asciiz containing the name of the material
-            CHUNK_MAT_MATNAME   = 0xA000,
-            CHUNK_MAT_AMBIENT   = 0xA010, // followed by color chunk
-            CHUNK_MAT_DIFFUSE   = 0xA020, // followed by color chunk
-            CHUNK_MAT_SPECULAR  = 0xA030, // followed by color chunk
-
-            // Specifies the shininess of the material
-            // followed by percentage chunk
-            CHUNK_MAT_SHININESS  = 0xA040,
-            CHUNK_MAT_SHININESS_PERCENT  = 0xA041 ,
-
-            // Specifies the shading mode to be used
-            // followed by a short
-            CHUNK_MAT_SHADING  = 0xA100,
-
-            // NOTE: Emissive color (self illumination) seems not
-            // to be a color but a single value, type is unknown.
-            // Make the parser accept both of them.
-            // followed by percentage chunk (?)
-            CHUNK_MAT_SELF_ILLUM = 0xA080,
-
-            // Always followed by percentage chunk  (?)
-            CHUNK_MAT_SELF_ILPCT = 0xA084,
-
-            // Always followed by percentage chunk
-            CHUNK_MAT_TRANSPARENCY = 0xA050,
-
-            // Diffuse texture channel 0
-            CHUNK_MAT_TEXTURE   = 0xA200,
-
-            // Contains opacity information for each texel
-            CHUNK_MAT_OPACMAP = 0xA210,
-
-            // Contains a reflection map to be used to reflect
-            // the environment. This is partially supported.
-            CHUNK_MAT_REFLMAP = 0xA220,
-
-            // Self Illumination map (emissive colors)
-            CHUNK_MAT_SELFIMAP = 0xA33d,
-
-            // Bumpmap. Not specified whether it is a heightmap
-            // or a normal map. Assme it is a heightmap since
-            // artist normally prefer this format.
-            CHUNK_MAT_BUMPMAP = 0xA230,
-
-            // Specular map. Seems to influence the specular color
-            CHUNK_MAT_SPECMAP = 0xA204,
-
-            // Holds shininess data.
-            CHUNK_MAT_MAT_SHINMAP = 0xA33C,
-
-            // Scaling in U/V direction.
-            // (need to gen separate UV coordinate set
-            // and do this by hand)
-            CHUNK_MAT_MAP_USCALE      = 0xA354,
-            CHUNK_MAT_MAP_VSCALE      = 0xA356,
-
-            // Translation in U/V direction.
-            // (need to gen separate UV coordinate set
-            // and do this by hand)
-            CHUNK_MAT_MAP_UOFFSET     = 0xA358,
-            CHUNK_MAT_MAP_VOFFSET     = 0xA35a,
-
-            // UV-coordinates rotation around the z-axis
-            // Assumed to be in radians.
-            CHUNK_MAT_MAP_ANG = 0xA35C,
-
-            // Tiling flags for 3DS files
-            CHUNK_MAT_MAP_TILING = 0xa351,
-
-            // Specifies the file name of a texture
-            CHUNK_MAPFILE   = 0xA300,
-
-            // Specifies whether a materail requires two-sided rendering
-            CHUNK_MAT_TWO_SIDE = 0xA081,
-        // ********************************************************************
-
-        // Main keyframer chunk. Contains translation/rotation/scaling data
-        CHUNK_KEYFRAMER     = 0xB000,
-
-        // Supported sub chunks
-        CHUNK_TRACKINFO     = 0xB002,
-        CHUNK_TRACKOBJNAME  = 0xB010,
-        CHUNK_TRACKDUMMYOBJNAME  = 0xB011,
-        CHUNK_TRACKPIVOT    = 0xB013,
-        CHUNK_TRACKPOS      = 0xB020,
-        CHUNK_TRACKROTATE   = 0xB021,
-        CHUNK_TRACKSCALE    = 0xB022,
-
-        // ********************************************************************
-        // Keyframes for various other stuff in the file
-        // Partially ignored
-        CHUNK_AMBIENTKEY    = 0xB001,
-        CHUNK_TRACKMORPH    = 0xB026,
-        CHUNK_TRACKHIDE     = 0xB029,
-        CHUNK_OBJNUMBER     = 0xB030,
-        CHUNK_TRACKCAMERA   = 0xB003,
-        CHUNK_TRACKFOV      = 0xB023,
-        CHUNK_TRACKROLL     = 0xB024,
-        CHUNK_TRACKCAMTGT   = 0xB004,
-        CHUNK_TRACKLIGHT    = 0xB005,
-        CHUNK_TRACKLIGTGT   = 0xB006,
-        CHUNK_TRACKSPOTL    = 0xB007,
-        CHUNK_FRAMES        = 0xB008,
-        // ********************************************************************
-
-        // light sub-chunks
-        CHUNK_DL_OFF                 = 0x4620,
-        CHUNK_DL_OUTER_RANGE         = 0x465A,
-        CHUNK_DL_INNER_RANGE         = 0x4659,
-        CHUNK_DL_MULTIPLIER          = 0x465B,
-        CHUNK_DL_EXCLUDE             = 0x4654,
-        CHUNK_DL_ATTENUATE           = 0x4625,
-        CHUNK_DL_SPOTLIGHT           = 0x4610,
-
-        // camera sub-chunks
-        CHUNK_CAM_RANGES             = 0x4720
-    };
-};
-
-// ---------------------------------------------------------------------------
-/** Helper structure representing a 3ds mesh face */
-struct Face : public FaceWithSmoothingGroup
-{
-};
-
-// ---------------------------------------------------------------------------
-/** Helper structure representing a texture */
-struct Texture {
-    //! Default constructor
-    Texture() AI_NO_EXCEPT
-    : mOffsetU  (0.0)
-    , mOffsetV  (0.0)
-    , mScaleU   (1.0)
-    , mScaleV   (1.0)
-    , mRotation (0.0)
-    , mMapMode  (aiTextureMapMode_Wrap)
-    , bPrivate()
-    , iUVSrc    (0) {
-        mTextureBlend = get_qnan();
-    }
-
-    //! Specifies the blend factor for the texture
-    ai_real mTextureBlend;
-
-    //! Specifies the filename of the texture
-    std::string mMapName;
-
-    //! Specifies texture coordinate offsets/scaling/rotations
-    ai_real mOffsetU;
-    ai_real mOffsetV;
-    ai_real mScaleU;
-    ai_real mScaleV;
-    ai_real mRotation;
-
-    //! Specifies the mapping mode to be used for the texture
-    aiTextureMapMode mMapMode;
-
-    //! Used internally
-    bool bPrivate;
-    int iUVSrc;
-};
-
-#include <assimp/Compiler/poppack1.h>
-
-// ---------------------------------------------------------------------------
-/** Helper structure representing a 3ds material */
-struct Material
-{
-    //! Default constructor has been deleted
-    Material() = delete;
-
-
-    //! Constructor with explicit name
-    explicit Material(const std::string &name)
-    : mName(name)
-    , mDiffuse            ( ai_real( 0.6 ), ai_real( 0.6 ), ai_real( 0.6 ) ) // FIX ... we won't want object to be black
-    , mSpecularExponent   ( ai_real( 0.0 ) )
-    , mShininessStrength  ( ai_real( 1.0 ) )
-    , mShading(Discreet3DS::Gouraud)
-    , mTransparency       ( ai_real( 1.0 ) )
-    , mBumpHeight         ( ai_real( 1.0 ) )
-    , mTwoSided           (false)
-    {
-    }
-
-
-    Material(const Material &other)            = default;
-    Material &operator=(const Material &other) = default;
-
-
-    //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
-    Material(Material &&other) AI_NO_EXCEPT
-    : mName(std::move(other.mName))
-    , mDiffuse(std::move(other.mDiffuse))
-    , mSpecularExponent(std::move(other.mSpecularExponent))
-    , mShininessStrength(std::move(other.mShininessStrength))
-    , mSpecular(std::move(other.mSpecular))
-    , mAmbient(std::move(other.mAmbient))
-    , mShading(std::move(other.mShading))
-    , mTransparency(std::move(other.mTransparency))
-    , sTexDiffuse(std::move(other.sTexDiffuse))
-    , sTexOpacity(std::move(other.sTexOpacity))
-    , sTexSpecular(std::move(other.sTexSpecular))
-    , sTexReflective(std::move(other.sTexReflective))
-    , sTexBump(std::move(other.sTexBump))
-    , sTexEmissive(std::move(other.sTexEmissive))
-    , sTexShininess(std::move(other.sTexShininess))
-    , mBumpHeight(std::move(other.mBumpHeight))
-    , mEmissive(std::move(other.mEmissive))
-    , sTexAmbient(std::move(other.sTexAmbient))
-    , mTwoSided(std::move(other.mTwoSided))
-    {
-    }
-
-
-    Material &operator=(Material &&other) AI_NO_EXCEPT {
-        if (this == &other) {
-            return *this;
-        }
-
-        mName = std::move(other.mName);
-        mDiffuse = std::move(other.mDiffuse);
-        mSpecularExponent = std::move(other.mSpecularExponent);
-        mShininessStrength = std::move(other.mShininessStrength),
-        mSpecular = std::move(other.mSpecular);
-        mAmbient = std::move(other.mAmbient);
-        mShading = std::move(other.mShading);
-        mTransparency = std::move(other.mTransparency);
-        sTexDiffuse = std::move(other.sTexDiffuse);
-        sTexOpacity = std::move(other.sTexOpacity);
-        sTexSpecular = std::move(other.sTexSpecular);
-        sTexReflective = std::move(other.sTexReflective);
-        sTexBump = std::move(other.sTexBump);
-        sTexEmissive = std::move(other.sTexEmissive);
-        sTexShininess = std::move(other.sTexShininess);
-        mBumpHeight = std::move(other.mBumpHeight);
-        mEmissive = std::move(other.mEmissive);
-        sTexAmbient = std::move(other.sTexAmbient);
-        mTwoSided = std::move(other.mTwoSided);
-
-        return *this;
-    }
-
-
-    virtual ~Material() {}
-
-
-    //! Name of the material
-    std::string mName;
-    //! Diffuse color of the material
-    aiColor3D mDiffuse;
-    //! Specular exponent
-    ai_real mSpecularExponent;
-    //! Shininess strength, in percent
-    ai_real mShininessStrength;
-    //! Specular color of the material
-    aiColor3D mSpecular;
-    //! Ambient color of the material
-    aiColor3D mAmbient;
-    //! Shading type to be used
-    Discreet3DS::shadetype3ds mShading;
-    //! Opacity of the material
-    ai_real mTransparency;
-    //! Diffuse texture channel
-    Texture sTexDiffuse;
-    //! Opacity texture channel
-    Texture sTexOpacity;
-    //! Specular texture channel
-    Texture sTexSpecular;
-    //! Reflective texture channel
-    Texture sTexReflective;
-    //! Bump texture channel
-    Texture sTexBump;
-    //! Emissive texture channel
-    Texture sTexEmissive;
-    //! Shininess texture channel
-    Texture sTexShininess;
-    //! Scaling factor for the bump values
-    ai_real mBumpHeight;
-    //! Emissive color
-    aiColor3D mEmissive;
-    //! Ambient texture channel
-    //! (used by the ASE format)
-    Texture sTexAmbient;
-    //! True if the material must be rendered from two sides
-    bool mTwoSided;
-};
-
-// ---------------------------------------------------------------------------
-/** Helper structure to represent a 3ds file mesh */
-struct Mesh : public MeshWithSmoothingGroups<D3DS::Face>
-{
-    //! Default constructor has been deleted
-    Mesh() = delete;
-
-    //! Constructor with explicit name
-    explicit Mesh(const std::string &name)
-    : mName(name)
-    {
-    }
-
-
-    //! Name of the mesh
-    std::string mName;
-
-    //! Texture coordinates
-    std::vector<aiVector3D> mTexCoords;
-
-    //! Face materials
-    std::vector<unsigned int> mFaceMaterials;
-
-    //! Local transformation matrix
-    aiMatrix4x4 mMat;
-};
-
-// ---------------------------------------------------------------------------
-/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the
-    C-API, so it would be difficult to make them a template. */
-struct aiFloatKey
-{
-    double mTime;      ///< The time of this key
-    ai_real mValue;   ///< The value of this key
-
-#ifdef __cplusplus
-
-    // time is not compared
-    bool operator == (const aiFloatKey& o) const
-        {return o.mValue == this->mValue;}
-
-    bool operator != (const aiFloatKey& o) const
-        {return o.mValue != this->mValue;}
-
-    // Only time is compared. This operator is defined
-    // for use with std::sort
-    bool operator < (const aiFloatKey& o) const
-        {return mTime < o.mTime;}
-
-    bool operator > (const aiFloatKey& o) const
-        {return mTime > o.mTime;}
-
-#endif
-};
-
-// ---------------------------------------------------------------------------
-/** Helper structure to represent a 3ds file node */
-struct Node
-{
-    Node() = delete;
-
-    explicit Node(const std::string &name)
-    : mParent(NULL)
-    , mName(name)
-    , mInstanceNumber(0)
-    , mHierarchyPos       (0)
-    , mHierarchyIndex     (0)
-    , mInstanceCount      (1)
-    {
-        aRotationKeys.reserve (20);
-        aPositionKeys.reserve (20);
-        aScalingKeys.reserve  (20);
-    }
-
-
-    ~Node()
-    {
-        for (unsigned int i = 0; i < mChildren.size();++i)
-            delete mChildren[i];
-    }
-
-    //! Pointer to the parent node
-    Node* mParent;
-
-    //! Holds all child nodes
-    std::vector<Node*> mChildren;
-
-    //! Name of the node
-    std::string mName;
-
-    //! InstanceNumber of the node
-    int32_t mInstanceNumber;
-
-    //! Dummy nodes: real name to be combined with the $$$DUMMY
-    std::string mDummyName;
-
-    //! Position of the node in the hierarchy (tree depth)
-    int16_t mHierarchyPos;
-
-    //! Index of the node
-    int16_t mHierarchyIndex;
-
-    //! Rotation keys loaded from the file
-    std::vector<aiQuatKey> aRotationKeys;
-
-    //! Position keys loaded from the file
-    std::vector<aiVectorKey> aPositionKeys;
-
-    //! Scaling keys loaded from the file
-    std::vector<aiVectorKey> aScalingKeys;
-
-
-    // For target lights (spot lights and directional lights):
-    // The position of the target
-    std::vector< aiVectorKey > aTargetPositionKeys;
-
-    // For cameras: the camera roll angle
-    std::vector< aiFloatKey > aCameraRollKeys;
-
-    //! Pivot position loaded from the file
-    aiVector3D vPivot;
-
-    //instance count, will be kept only for the first node
-    int32_t mInstanceCount;
-
-    //! Add a child node, setup the right parent node for it
-    //! \param pc Node to be 'adopted'
-    inline Node& push_back(Node* pc)
-    {
-        mChildren.push_back(pc);
-        pc->mParent = this;
-        return *this;
-    }
-};
-// ---------------------------------------------------------------------------
-/** Helper structure analogue to aiScene */
-struct Scene
-{
-    //! List of all materials loaded
-    //! NOTE: 3ds references materials globally
-    std::vector<Material> mMaterials;
-
-    //! List of all meshes loaded
-    std::vector<Mesh> mMeshes;
-
-    //! List of all cameras loaded
-    std::vector<aiCamera*> mCameras;
-
-    //! List of all lights loaded
-    std::vector<aiLight*> mLights;
-
-    //! Pointer to the root node of the scene
-    // --- moved to main class
-    // Node* pcRootNode;
-};
-
-
-} // end of namespace D3DS
-} // end of namespace Assimp
-
-#endif // AI_XFILEHELPER_H_INC

+ 0 - 104
Engine/lib/assimp/code/3MF/3MFXmlTags.h

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

+ 0 - 483
Engine/lib/assimp/code/3MF/D3MFImporter.cpp

@@ -1,483 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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_3MF_IMPORTER
-
-#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 <string>
-#include <vector>
-#include <map>
-#include <cassert>
-#include <memory>
-
-#include "D3MFOpcPackage.h"
-#include <assimp/irrXMLWrapper.h>
-#include "3MFXmlTags.h"
-#include <assimp/fast_atof.h>
-
-#include <iomanip>
-
-namespace Assimp {
-namespace D3MF {
-
-class XmlSerializer {
-public:
-    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() {
-        // empty
-    }
-
-    void ImportXml(aiScene* scene) {
-        if ( nullptr == scene ) {
-            return;
-        }
-
-        scene->mRootNode = new aiNode();
-        std::vector<aiNode*> children;
-
-        std::string nodeName;
-        while(ReadToEndElement(D3MF::XmlTag::model)) {
-            nodeName = xmlReader->getNodeName();
-            if( nodeName == D3MF::XmlTag::object) {
-                children.push_back(ReadObject(scene));
-            } else if( nodeName == D3MF::XmlTag::build) {
-                // 
-            } else if ( nodeName == D3MF::XmlTag::basematerials ) {
-                ReadBaseMaterials();
-            } else if ( nodeName == D3MF::XmlTag::meta ) {
-                ReadMetadata();
-            }
-        }
-
-        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 );
-            }
-        }
-
-        // 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);
-
-        // 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 );
-        }
-
-        // create the scenegraph
-        scene->mRootNode->mNumChildren = static_cast<unsigned int>(children.size());
-        scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren]();
-        std::copy(children.begin(), children.end(), scene->mRootNode->mChildren);
-    }
-
-private:
-    aiNode* ReadObject(aiScene* scene) {
-        std::unique_ptr<aiNode> node(new aiNode());
-
-        std::vector<unsigned long> meshIds;
-
-        const char *attrib( nullptr );
-        std::string name, type;
-        attrib = xmlReader->getAttributeValue( D3MF::XmlTag::id.c_str() );
-        if ( nullptr != attrib ) {
-            name = attrib;
-        }
-        attrib = xmlReader->getAttributeValue( D3MF::XmlTag::type.c_str() );
-        if ( nullptr != attrib ) {
-            type = attrib;
-        }
-
-        node->mParent = scene->mRootNode;
-        node->mName.Set(name);
-
-        size_t meshIdx = mMeshes.size();
-
-        while(ReadToEndElement(D3MF::XmlTag::object)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) {
-                auto mesh = ReadMesh();
-
-                mesh->mName.Set(name);
-                mMeshes.push_back(mesh);
-                meshIds.push_back(static_cast<unsigned long>(meshIdx));
-                ++meshIdx;
-            }
-        }
-
-        node->mNumMeshes = static_cast<unsigned int>(meshIds.size());
-
-        node->mMeshes = new unsigned int[node->mNumMeshes];
-
-        std::copy(meshIds.begin(), meshIds.end(), node->mMeshes);
-
-        return node.release();
-    }
-
-    aiMesh *ReadMesh() {
-        aiMesh* mesh = new aiMesh();
-        while(ReadToEndElement(D3MF::XmlTag::mesh)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) {
-                ImportVertices(mesh);
-            } else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) {
-                ImportTriangles(mesh);
-            }
-        }
-
-        return mesh;
-    }
-
-    void ReadMetadata() {
-        const std::string name = xmlReader->getAttributeValue( D3MF::XmlTag::meta_name.c_str() );
-        xmlReader->read();
-        const std::string value = xmlReader->getNodeData();
-
-        if ( name.empty() ) {
-            return;
-        }
-
-        MetaEntry entry;
-        entry.name = name;
-        entry.value = value;
-        mMetaData.push_back( entry );
-    }
-
-    void ImportVertices(aiMesh* mesh) {
-        std::vector<aiVector3D> vertices;
-        while(ReadToEndElement(D3MF::XmlTag::vertices)) {
-            if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) {
-                vertices.push_back(ReadVertex());
-            }
-        }
-        mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
-        mesh->mVertices = new aiVector3D[mesh->mNumVertices];
-
-        std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
-    }
-
-    aiVector3D ReadVertex() {
-        aiVector3D vertex;
-
-        vertex.x = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::x.c_str()), nullptr);
-        vertex.y = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::y.c_str()), nullptr);
-        vertex.z = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::z.c_str()), nullptr);
-
-        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;
-                 }
-             }
-         }
-
-        mesh->mNumFaces = static_cast<unsigned int>(faces.size());
-        mesh->mFaces = new aiFace[mesh->mNumFaces];
-        mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
-
-        std::copy(faces.begin(), faces.end(), mesh->mFaces);
-    }
-
-    aiFace ReadTriangle() {
-        aiFace face;
-
-        face.mNumIndices = 3;
-        face.mIndices = new unsigned int[face.mNumIndices];
-        face.mIndices[0] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v1.c_str())));
-        face.mIndices[1] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v2.c_str())));
-        face.mIndices[2] = static_cast<unsigned int>(std::atoi(xmlReader->getAttributeValue(D3MF::XmlTag::v3.c_str())));
-
-        return face;
-    }
-
-    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 ) {
-                mActiveMatGroup = id;
-                MatId2MatArray::const_iterator it( mMatId2MatArray.find( id ) );
-                if ( mMatId2MatArray.end() == it ) {
-                    MatIdArray.clear();
-                    mMatId2MatArray[ id ] = MatIdArray;
-                } else {
-                    MatIdArray = it->second;
-                }
-            }
-            MatIdArray.push_back( static_cast<unsigned int>( newMatIdx ) );
-            mMatId2MatArray[ mActiveMatGroup ] = MatIdArray;
-        }
-
-        while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) {
-            mMatArray.push_back( readMaterialDef() );
-        }
-    }
-
-    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) {
-            return false;
-        }
-
-        const char *buf( color );
-        if ( '#' != *buf ) {
-            return false;
-        }
-        ++buf;
-        char comp[ 3 ] = { 0,0,'\0' };
-
-        comp[ 0 ] = *buf;
-        ++buf;
-        comp[ 1 ] = *buf;
-        ++buf;
-        diffuse.r = static_cast<ai_real>( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
-
-
-        comp[ 0 ] = *buf;
-        ++buf;
-        comp[ 1 ] = *buf;
-        ++buf;
-        diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
-
-        comp[ 0 ] = *buf;
-        ++buf;
-        comp[ 1 ] = *buf;
-        ++buf;
-        diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ) / ai_real(255.0);
-
-        if(7 == len)
-            return true;
-        comp[ 0 ] = *buf;
-        ++buf;
-        comp[ 1 ] = *buf;
-        ++buf;
-        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() );
-        aiColor4D 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() );
-            std::string stdMatName;
-            aiString matName;
-            std::string strId( to_string( mActiveMatGroup ) );
-            stdMatName += "id";
-            stdMatName += strId;
-            stdMatName += "_";
-            if ( nullptr != name ) {
-                stdMatName += std::string( name );
-            } else {
-                stdMatName += "basemat";
-            }
-            matName.Set( stdMatName );
-
-            mat = new aiMaterial;
-            mat->AddProperty( &matName, AI_MATKEY_NAME );
-
-            assignDiffuseColor( mat );
-        }
-
-        return mat;
-    }
-
-private:
-    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) {
-                return false;
-            }
-        }
-
-        return false;
-    }
-
-    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) {
-                return false;
-            }
-        }
-        ASSIMP_LOG_ERROR("unexpected EOF, expected closing <" + closeTag + "> tag");
-
-        return false;
-    }
-
-private:
-    struct MetaEntry {
-        std::string name;
-        std::string value;
-    };
-    std::vector<MetaEntry> mMetaData;
-    std::vector<aiMesh*> mMeshes;
-    MatArray mMatArray;
-    unsigned int mActiveMatGroup;
-    MatId2MatArray mMatId2MatArray;
-    XmlReader* xmlReader;
-};
-
-} //namespace D3MF
-
-static const aiImporterDesc desc = {
-    "3mf Importer",
-    "",
-    "",
-    "http://3mf.io/",
-    aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour,
-    0,
-    0,
-    0,
-    0,
-    "3mf"
-};
-
-D3MFImporter::D3MFImporter()
-: BaseImporter() {
-    // empty
-}
-
-D3MFImporter::~D3MFImporter() {
-    // empty
-}
-
-bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
-    const std::string extension( GetExtension( filename ) );
-    if(extension == desc.mFileExtensions ) {
-        return true;
-    } else if ( !extension.length() || checkSig ) {
-        if ( nullptr == pIOHandler ) {
-            return false;
-        }
-        if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
-            return false;
-        }
-        D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );
-        return opcPackage.validate();
-    }
-
-    return false;
-}
-
-void D3MFImporter::SetupProperties(const Importer * /*pImp*/) {
-    // empty
-}
-
-const aiImporterDesc *D3MFImporter::GetInfo() const {
-    return &desc;
-}
-
-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()));
-    std::unique_ptr<D3MF::XmlReader> xmlReader(irr::io::createIrrXMLReader(xmlStream.get()));
-
-    D3MF::XmlSerializer xmlSerializer(xmlReader.get());
-
-    xmlSerializer.ImportXml(pScene);
-}
-
-} // Namespace Assimp
-
-#endif // ASSIMP_BUILD_NO_3MF_IMPORTER

+ 0 - 207
Engine/lib/assimp/code/3MF/D3MFOpcPackage.cpp

@@ -1,207 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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_3MF_IMPORTER
-
-#include "D3MFOpcPackage.h"
-#include <assimp/Exceptional.h>
-
-#include <assimp/IOStream.hpp>
-#include <assimp/IOSystem.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/ai_assert.h>
-#include <assimp/ZipArchiveIOSystem.h>
-
-#include <cstdlib>
-#include <memory>
-#include <vector>
-#include <map>
-#include <algorithm>
-#include <cassert>
-#include "3MFXmlTags.h"
-
-namespace Assimp {
-
-namespace D3MF {
-// ------------------------------------------------------------------------------------------------
-
-typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
-
-class OpcPackageRelationshipReader {
-public:
-    OpcPackageRelationshipReader(XmlReader* xmlReader) {        
-        while(xmlReader->read()) {
-            if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
-               xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_CONTAINER)
-            {
-                ParseRootNode(xmlReader);
-            }
-        }
-    }
-
-    void ParseRootNode(XmlReader* xmlReader)
-    {       
-        ParseAttributes(xmlReader);
-
-        while(xmlReader->read())
-        {
-            if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
-               xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_NODE)
-            {
-                ParseChildNode(xmlReader);
-            }
-        }
-    }
-
-    void ParseAttributes(XmlReader*) {
-        // empty
-    }
-
-    bool validateRels( OpcPackageRelationshipPtr &relPtr ) {
-        if ( relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty() ) {
-            return false;
-        }
-        return true;
-    }
-
-    void ParseChildNode(XmlReader* xmlReader) {        
-        OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
-
-        relPtr->id = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_ID.c_str());
-        relPtr->type = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TYPE.c_str());
-        relPtr->target = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TARGET.c_str());
-        if ( validateRels( relPtr ) ) {
-            m_relationShips.push_back( relPtr );
-        }
-    }
-
-    std::vector<OpcPackageRelationshipPtr> m_relationShips;
-};
-
-// ------------------------------------------------------------------------------------------------
-D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
-: mRootStream(nullptr)
-, mZipArchive() {    
-    mZipArchive.reset( new ZipArchiveIOSystem( pIOHandler, rFile ) );
-    if(!mZipArchive->isOpen()) {
-        throw DeadlyImportError("Failed to open file " + rFile+ ".");
-    }
-
-    std::vector<std::string> fileList;
-    mZipArchive->getFileList(fileList);
-
-    for (auto& file: fileList) {
-        if(file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) {
-            //PkgRelationshipReader pkgRelReader(file, archive);
-            ai_assert(mZipArchive->Exists(file.c_str()));
-
-            IOStream *fileStream = mZipArchive->Open(file.c_str());
-
-            ai_assert(fileStream != nullptr);
-
-            std::string rootFile = ReadPackageRootRelationship(fileStream);
-            if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) {
-                rootFile = rootFile.substr( 1 );
-                if ( rootFile[ 0 ] == '/' ) {
-                    // deal with zip-bug
-                    rootFile = rootFile.substr( 1 );
-                }
-            }
-
-            ASSIMP_LOG_DEBUG(rootFile);
-
-            mZipArchive->Close(fileStream);
-
-            mRootStream = mZipArchive->Open(rootFile.c_str());
-            ai_assert( mRootStream != nullptr );
-            if ( nullptr == mRootStream ) {
-                throw DeadlyExportError( "Cannot open root-file in archive : " + rootFile );
-            }
-
-        } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
-            ASSIMP_LOG_WARN_F("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES",file);
-        } else {
-            ASSIMP_LOG_WARN_F("Ignored file of unknown type: ",file);
-        }
-
-    }
-}
-
-D3MFOpcPackage::~D3MFOpcPackage() {
-    mZipArchive->Close(mRootStream);
-}
-
-IOStream* D3MFOpcPackage::RootStream() const {
-    return mRootStream;
-}
-
-static const std::string ModelRef = "3D/3dmodel.model";
-
-bool D3MFOpcPackage::validate() {
-    if ( nullptr == mRootStream || nullptr == mZipArchive ) {
-        return false;
-    }
-
-    return mZipArchive->Exists( ModelRef.c_str() );
-}
-
-std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) {
-    std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(stream));
-    std::unique_ptr<XmlReader> xml(irr::io::createIrrXMLReader(xmlStream.get()));
-
-    OpcPackageRelationshipReader reader(xml.get());
-
-    auto itr = std::find_if(reader.m_relationShips.begin(), reader.m_relationShips.end(), [](const OpcPackageRelationshipPtr& rel){
-        return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
-    });
-
-    if ( itr == reader.m_relationShips.end() ) {
-        throw DeadlyImportError( "Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE );
-    }
-
-    return (*itr)->target;
-}
-
-} // Namespace D3MF
-} // Namespace Assimp
-
-#endif //ASSIMP_BUILD_NO_3MF_IMPORTER

+ 0 - 705
Engine/lib/assimp/code/AMF/AMFImporter.cpp

@@ -1,705 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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 - 432
Engine/lib/assimp/code/AMF/AMFImporter.hpp

@@ -1,432 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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.hpp
-/// \brief AMF-format files importer for Assimp.
-/// \date 2016
-/// \author [email protected]
-// Thanks to acorn89 for support.
-
-#pragma once
-#ifndef INCLUDED_AI_AMF_IMPORTER_H
-#define INCLUDED_AI_AMF_IMPORTER_H
-
-#include "AMFImporter_Node.hpp"
-
-// Header files, Assimp.
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/importerdesc.h>
-#include "assimp/types.h"
-#include <assimp/BaseImporter.h>
-#include <assimp/irrXMLWrapper.h>
-
-// Header files, stdlib.
-#include <set>
-
-namespace Assimp {
-
-/// \class AMFImporter
-/// Class that holding scene graph which include: geometry, metadata, materials etc.
-///
-/// Implementing features.
-///
-/// Limitations.
-///
-/// 1. When for texture mapping used set of source textures (r, g, b, a) not only one then attribute "tiled" for all set will be true if it true in any of
-///    source textures.
-///    Example. Triangle use for texture mapping three textures. Two of them has "tiled" set to false and one - set to true. In scene all three textures
-///    will be tiled.
-///
-/// Unsupported features:
-/// 1. Node <composite>, formulas in <composite> and <color>. For implementing this feature can be used expression parser "muParser" like in project
-///    "amf_tools".
-/// 2. Attribute "profile" in node <color>.
-/// 3. Curved geometry: <edge>, <normal> and children nodes of them.
-/// 4. Attributes: "unit" and "version" in <amf> read but do nothing.
-/// 5. <metadata> stored only for root node <amf>.
-/// 6. Color averaging of vertices for which <triangle>'s set different colors.
-///
-/// Supported nodes:
-///    General:
-///        <amf>; <constellation>; <instance> and children <deltax>, <deltay>, <deltaz>, <rx>, <ry>, <rz>; <metadata>;
-///
-///    Geometry:
-///        <object>; <mesh>; <vertices>; <vertex>; <coordinates> and children <x>, <y>, <z>; <volume>; <triangle> and children <v1>, <v2>, <v3>;
-///
-///    Material:
-///        <color> and children <r>, <g>, <b>, <a>; <texture>; <material>;
-///        two variants of texture coordinates:
-///            new - <texmap> and children <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>
-///            old - <map> and children <u1>, <u2>, <u3>, <v1>, <v2>, <v3>
-///
-class AMFImporter : public BaseImporter {
-private:
-    struct SPP_Material;// forward declaration
-
-                        /// \struct SPP_Composite
-                        /// Data type for post-processing step. More suitable container for part of material's composition.
-    struct SPP_Composite {
-        SPP_Material* Material;///< Pointer to material - part of composition.
-        std::string Formula;///< Formula for calculating ratio of \ref Material.
-    };
-
-    /// \struct SPP_Material
-    /// Data type for post-processing step. More suitable container for material.
-    struct SPP_Material {
-        std::string ID;///< Material ID.
-        std::list<CAMFImporter_NodeElement_Metadata*> Metadata;///< Metadata of material.
-        CAMFImporter_NodeElement_Color* Color;///< Color of material.
-        std::list<SPP_Composite> Composition;///< List of child materials if current material is composition of few another.
-
-        /// Return color calculated for specified coordinate.
-        /// \param [in] pX - "x" coordinate.
-        /// \param [in] pY - "y" coordinate.
-        /// \param [in] pZ - "z" coordinate.
-        /// \return calculated color.
-        aiColor4D GetColor(const float pX, const float pY, const float pZ) const;
-    };
-
-    /// Data type for post-processing step. More suitable container for texture.
-    struct SPP_Texture {
-        std::string ID;
-        size_t      Width, Height, Depth;
-        bool        Tiled;
-        char        FormatHint[9];// 8 for string + 1 for terminator.
-        uint8_t    *Data;
-    };
-
-    /// Data type for post-processing step. Contain face data.
-    struct SComplexFace {
-        aiFace Face;///< Face vertices.
-        const CAMFImporter_NodeElement_Color* Color;///< Face color. Equal to nullptr if color is not set for the face.
-        const CAMFImporter_NodeElement_TexMap* TexMap;///< Face texture mapping data. Equal to nullptr if texture mapping is not set for the face.
-    };
-
-	/// Clear all temporary data.
-	void Clear();
-
-	/***********************************************/
-	/************* Functions: find set *************/
-	/***********************************************/
-
-	/// Find specified node element in node elements list ( \ref mNodeElement_List).
-	/// \param [in] pID - ID(name) of requested node element.
-	/// \param [in] pType - type of node element.
-	/// \param [out] pNode - pointer to pointer to item found.
-	/// \return true - if the node element is found, else - false.
-	bool Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const;
-
-	/// Find requested aiNode in node list.
-	/// \param [in] pID - ID(name) of requested node.
-	/// \param [in] pNodeList - list of nodes where to find the node.
-	/// \param [out] pNode - pointer to pointer to item found.
-	/// \return true - if the node is found, else - false.
-	bool Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const;
-
-	/// Find material in list for converted materials. Use at postprocessing step.
-	/// \param [in] pID - material ID.
-	/// \param [out] pConvertedMaterial - pointer to found converted material (\ref SPP_Material).
-	/// \return true - if the material is found, else - false.
-	bool Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const;
-
-    /// Find texture in list of converted textures. Use at postprocessing step,
-	/// \param [in] pID_R - ID of source "red" texture.
-	/// \param [in] pID_G - ID of source "green" texture.
-	/// \param [in] pID_B - ID of source "blue" texture.
-	/// \param [in] pID_A - ID of source "alpha" texture. Use empty string to find RGB-texture.
-	/// \param [out] pConvertedTextureIndex - pointer where index in list of found texture will be written. If equivalent to nullptr then nothing will be
-	/// written.
-	/// \return true - if the texture is found, else - false.
-	bool Find_ConvertedTexture(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A,
-								uint32_t* pConvertedTextureIndex = nullptr) const;
-
-
-	/// Get data stored in <vertices> and place it to arrays.
-	/// \param [in] pNodeElement - reference to node element which kept <object> data.
-	/// \param [in] pVertexCoordinateArray - reference to vertices coordinates kept in <vertices>.
-	/// \param [in] pVertexColorArray - reference to vertices colors for all <vertex's. If color for vertex is not set then corresponding member of array
-	/// contain nullptr.
-	void PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray,
-												std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const;
-
-	/// Return converted texture ID which related to specified source textures ID's. If converted texture does not exist then it will be created and ID on new
-	/// converted texture will be returned. Conversion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it
-	/// to converted textures list.
-	/// Any of source ID's can be absent(empty string) or even one ID only specified. But at least one ID must be specified.
-	/// \param [in] pID_R - ID of source "red" texture.
-	/// \param [in] pID_G - ID of source "green" texture.
-	/// \param [in] pID_B - ID of source "blue" texture.
-	/// \param [in] pID_A - ID of source "alpha" texture.
-	/// \return index of the texture in array of the converted textures.
-	size_t PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B, const std::string& pID_A);
-
-	/// Separate input list by texture IDs. This step is needed because aiMesh can contain mesh which is use only one texture (or set: diffuse, bump etc).
-	/// \param [in] pInputList - input list with faces. Some of them can contain color or texture mapping, or both of them, or nothing. Will be cleared after
-	/// processing.
-	/// \param [out] pOutputList_Separated - output list of the faces lists. Separated faces list by used texture IDs. Will be cleared before processing.
-	void PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> >& pOutputList_Separated);
-
-	/// Check if child elements of node element is metadata and add it to scene node.
-	/// \param [in] pMetadataList - reference to list with collected metadata.
-	/// \param [out] pSceneNode - scene node in which metadata will be added.
-	void Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& pMetadataList, aiNode& pSceneNode) const;
-
-	/// To create aiMesh and aiNode for it from <object>.
-	/// \param [in] pNodeElement - reference to node element which kept <object> data.
-	/// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
-	/// \param [out] pSceneNode - pointer to place where new aiNode will be created.
-	void Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode);
-
-	/// Create mesh for every <volume> in <mesh>.
-	/// \param [in] pNodeElement - reference to node element which kept <mesh> data.
-	/// \param [in] pVertexCoordinateArray - reference to vertices coordinates for all <volume>'s.
-	/// \param [in] pVertexColorArray - reference to vertices colors for all <volume>'s. If color for vertex is not set then corresponding member of array
-	/// contain nullptr.
-	/// \param [in] pObjectColor - pointer to colors for <object>. If color is not set then argument contain nullptr.
-	/// \param [in] pMaterialList - reference to a list with defined materials.
-	/// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
-	/// \param [out] pSceneNode - reference to aiNode which will own new aiMesh's.
-	void 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);
-
-	/// Convert material from \ref CAMFImporter_NodeElement_Material to \ref SPP_Material.
-	/// \param [in] pMaterial - source CAMFImporter_NodeElement_Material.
-	void Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial);
-
-	/// Create and add to aiNode's list new part of scene graph defined by <constellation>.
-	/// \param [in] pConstellation - reference to <constellation> node.
-	/// \param [out] pNodeList - reference to aiNode's list.
-	void Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const;
-
-	/// Build Assimp scene graph in aiScene from collected data.
-	/// \param [out] pScene - pointer to aiScene where tree will be built.
-	void Postprocess_BuildScene(aiScene* pScene);
-
-
-	/// Call that function when close tag of node not found and exception must be raised.
-	/// E.g.:
-	/// <amf>
-	///     <object>
-	/// </amf> <!--- object not closed --->
-	/// \throw DeadlyImportError.
-	/// \param [in] pNode - node name in which exception happened.
-	void Throw_CloseNotFound(const std::string& pNode);
-
-	/// Call that function when attribute name is incorrect and exception must be raised.
-	/// \param [in] pAttrName - attribute name.
-	/// \throw DeadlyImportError.
-	void Throw_IncorrectAttr(const std::string& pAttrName);
-
-	/// Call that function when attribute value is incorrect and exception must be raised.
-	/// \param [in] pAttrName - attribute name.
-	/// \throw DeadlyImportError.
-	void Throw_IncorrectAttrValue(const std::string& pAttrName);
-
-	/// Call that function when some type of nodes are defined twice or more when must be used only once and exception must be raised.
-	/// E.g.:
-	/// <object>
-	///     <color>...    <!--- color defined --->
-	///     <color>...    <!--- color defined again --->
-	/// </object>
-	/// \throw DeadlyImportError.
-	/// \param [in] pNodeType - type of node which defined one more time.
-	/// \param [in] pDescription - message about error. E.g. what the node defined while exception raised.
-	void Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription);
-
-	/// Call that function when referenced element ID are not found in graph and exception must be raised.
-	/// \param [in] pID - ID of of element which not found.
-	/// \throw DeadlyImportError.
-	void Throw_ID_NotFound(const std::string& pID) const;
-
-	/// Check if current node have children: <node>...</node>. If not then exception will throwed.
-	void XML_CheckNode_MustHaveChildren();
-
-	/// Check if current node name is equal to pNodeName.
-	/// \param [in] pNodeName - name for checking.
-	/// return true if current node name is equal to pNodeName, else - false.
-	bool XML_CheckNode_NameEqual(const std::string& pNodeName) { return mReader->getNodeName() == pNodeName; }
-
-	/// Skip unsupported node and report about that. Depend on node name can be skipped begin tag of node all whole node.
-	/// \param [in] pParentNodeName - parent node name. Used for reporting.
-	void XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName);
-
-	/// Search for specified node in file. XML file read pointer(mReader) will point to found node or file end after search is end.
-	/// \param [in] pNodeName - requested node name.
-	/// return true - if node is found, else - false.
-	bool XML_SearchNode(const std::string& pNodeName);
-
-	/// Read attribute value.
-	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
-	/// \return read data.
-	bool XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx);
-
-	/// Read attribute value.
-	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
-	/// \return read data.
-	float XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx);
-
-	/// Read attribute value.
-	/// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
-	/// \return read data.
-	uint32_t XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx);
-
-	/// Read node value.
-	/// \return read data.
-	float XML_ReadNode_GetVal_AsFloat();
-
-	/// Read node value.
-	/// \return read data.
-	uint32_t XML_ReadNode_GetVal_AsU32();
-
-	/// Read node value.
-	/// \return read data.
-	void XML_ReadNode_GetVal_AsString(std::string& pValue);
-
-	/// Make pNode as current and enter deeper for parsing child nodes. At end \ref ParseHelper_Node_Exit must be called.
-	/// \param [in] pNode - new current node.
-	void ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode);
-
-	/// This function must be called when exiting from grouping node. \ref ParseHelper_Group_Begin.
-	void ParseHelper_Node_Exit();
-
-	/// Attribute values of floating point types can take form ".x"(without leading zero). irrXMLReader can not read this form of values and it
-	/// must be converted to right form - "0.xxx".
-	/// \param [in] pInStr - pointer to input string which can contain incorrect form of values.
-	/// \param [out[ pOutString - output string with right form of values.
-	void ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString);
-
-	/// Decode Base64-encoded data.
-	/// \param [in] pInputBase64 - reference to input Base64-encoded string.
-	/// \param [out] pOutputData - reference to output array for decoded data.
-	void ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const;
-
-	/// Parse <AMF> node of the file.
-	void ParseNode_Root();
-
-	/// Parse <constellation> node of the file.
-	void ParseNode_Constellation();
-
-	/// Parse <instance> node of the file.
-	void ParseNode_Instance();
-
-	/// Parse <material> node of the file.
-	void ParseNode_Material();
-
-	/// Parse <metadata> node.
-	void ParseNode_Metadata();
-
-	/// Parse <object> node of the file.
-	void ParseNode_Object();
-
-	/// Parse <texture> node of the file.
-	void ParseNode_Texture();
-
-	/// Parse <coordinates> node of the file.
-	void ParseNode_Coordinates();
-
-	/// Parse <edge> node of the file.
-	void ParseNode_Edge();
-
-	/// Parse <mesh> node of the file.
-	void ParseNode_Mesh();
-
-	/// Parse <triangle> node of the file.
-	void ParseNode_Triangle();
-
-	/// Parse <vertex> node of the file.
-	void ParseNode_Vertex();
-
-	/// Parse <vertices> node of the file.
-	void ParseNode_Vertices();
-
-	/// Parse <volume> node of the file.
-	void ParseNode_Volume();
-
-	/// Parse <color> node of the file.
-	void ParseNode_Color();
-
-	/// Parse <texmap> of <map> node of the file.
-	/// \param [in] pUseOldName - if true then use old name of node(and children) - <map>, instead of new name - <texmap>.
-	void ParseNode_TexMap(const bool pUseOldName = false);
-
-public:
-	/// Default constructor.
-	AMFImporter() AI_NO_EXCEPT
-    : mNodeElement_Cur(nullptr)
-    , mReader(nullptr) {
-        // empty
-    }
-
-	/// Default destructor.
-	~AMFImporter();
-
-	/// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
-	/// Also exception can be thrown if trouble will found.
-	/// \param [in] pFile - name of file to be parsed.
-	/// \param [in] pIOHandler - pointer to IO helper object.
-	void ParseFile(const std::string& pFile, IOSystem* pIOHandler);
-
-	bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const;
-	void GetExtensionList(std::set<std::string>& pExtensionList);
-	void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
-	const aiImporterDesc* GetInfo ()const;
-
-    AMFImporter(const AMFImporter& pScene) = delete;
-    AMFImporter& operator=(const AMFImporter& pScene) = delete;
-
-private:
-    static const aiImporterDesc Description;
-
-    CAMFImporter_NodeElement* mNodeElement_Cur;///< Current element.
-    std::list<CAMFImporter_NodeElement*> mNodeElement_List;///< All elements of scene graph.
-    irr::io::IrrXMLReader* mReader;///< Pointer to XML-reader object
-    std::string mUnit;
-    std::list<SPP_Material> mMaterial_Converted;///< List of converted materials for postprocessing step.
-    std::list<SPP_Texture> mTexture_Converted;///< List of converted textures for postprocessing step.
-
-};
-
-}// namespace Assimp
-
-#endif // INCLUDED_AI_AMF_IMPORTER_H

+ 0 - 357
Engine/lib/assimp/code/AMF/AMFImporter_Geometry.cpp

@@ -1,357 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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_Geometry.cpp
-/// \brief Parsing data from geometry nodes.
-/// \date 2016
-/// \author [email protected]
-
-#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-
-#include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
-
-namespace Assimp
-{
-
-// <mesh>
-// </mesh>
-// A 3D mesh hull.
-// Multi elements - Yes.
-// Parent element - <object>.
-void AMFImporter::ParseNode_Mesh()
-{
-CAMFImporter_NodeElement* ne;
-
-	// create new mesh object.
-	ne = new CAMFImporter_NodeElement_Mesh(mNodeElement_Cur);
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool vert_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("mesh");
-			if(XML_CheckNode_NameEqual("vertices"))
-			{
-				// Check if data already defined.
-				if(vert_read) Throw_MoreThanOnceDefined("vertices", "Only one vertices set can be defined for <mesh>.");
-				// read data and set flag about it
-				ParseNode_Vertices();
-				vert_read = true;
-
-				continue;
-			}
-
-			if(XML_CheckNode_NameEqual("volume")) { ParseNode_Volume(); continue; }
-		MACRO_NODECHECK_LOOPEND("mesh");
-		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.
-}
-
-// <vertices>
-// </vertices>
-// The list of vertices to be used in defining triangles.
-// Multi elements - No.
-// Parent element - <mesh>.
-void AMFImporter::ParseNode_Vertices()
-{
-CAMFImporter_NodeElement* ne;
-
-	// create new mesh object.
-	ne = new CAMFImporter_NodeElement_Vertices(mNodeElement_Cur);
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("vertices");
-			if(XML_CheckNode_NameEqual("vertex")) { ParseNode_Vertex(); continue; }
-		MACRO_NODECHECK_LOOPEND("vertices");
-		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.
-}
-
-// <vertex>
-// </vertex>
-// A vertex to be referenced in triangles.
-// Multi elements - Yes.
-// Parent element - <vertices>.
-void AMFImporter::ParseNode_Vertex()
-{
-CAMFImporter_NodeElement* ne;
-
-	// create new mesh object.
-	ne = new CAMFImporter_NodeElement_Vertex(mNodeElement_Cur);
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool col_read = false;
-		bool coord_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("vertex");
-			if(XML_CheckNode_NameEqual("color"))
-			{
-				// Check if data already defined.
-				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <vertex>.");
-				// read data and set flag about it
-				ParseNode_Color();
-				col_read = true;
-
-				continue;
-			}
-
-			if(XML_CheckNode_NameEqual("coordinates"))
-			{
-				// Check if data already defined.
-				if(coord_read) Throw_MoreThanOnceDefined("coordinates", "Only one coordinates set can be defined for <vertex>.");
-				// read data and set flag about it
-				ParseNode_Coordinates();
-				coord_read = true;
-
-				continue;
-			}
-
-			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
-		MACRO_NODECHECK_LOOPEND("vertex");
-		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.
-}
-
-// <coordinates>
-// </coordinates>
-// Specifies the 3D location of this vertex.
-// Multi elements - No.
-// Parent element - <vertex>.
-//
-// Children elements:
-//   <x>, <y>, <z>
-//   Multi elements - No.
-//   X, Y, or Z coordinate, respectively, of a vertex position in space.
-void AMFImporter::ParseNode_Coordinates()
-{
-CAMFImporter_NodeElement* ne;
-
-	// create new color object.
-	ne = new CAMFImporter_NodeElement_Coordinates(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Coordinates& als = *((CAMFImporter_NodeElement_Coordinates*)ne);// alias for convenience
-
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool read_flag[3] = { false, false, false };
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("coordinates");
-			MACRO_NODECHECK_READCOMP_F("x", read_flag[0], als.Coordinate.x);
-			MACRO_NODECHECK_READCOMP_F("y", read_flag[1], als.Coordinate.y);
-			MACRO_NODECHECK_READCOMP_F("z", read_flag[2], als.Coordinate.z);
-		MACRO_NODECHECK_LOOPEND("coordinates");
-		ParseHelper_Node_Exit();
-		// check that all components was defined
-		if((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all coordinate's components are defined.");
-
-	}// 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.
-}
-
-// <volume
-// materialid="" - Which material to use.
-// type=""       - What this volume describes can be “region” or “support”. If none specified, “object” is assumed. If support, then the geometric
-//                 requirements 1-8 listed in section 5 do not need to be maintained.
-// >
-// </volume>
-// Defines a volume from the established vertex list.
-// Multi elements - Yes.
-// Parent element - <mesh>.
-void AMFImporter::ParseNode_Volume()
-{
-std::string materialid;
-std::string type;
-CAMFImporter_NodeElement* ne;
-
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("materialid", materialid, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new object.
-	ne = new CAMFImporter_NodeElement_Volume(mNodeElement_Cur);
-	// and assign read data
-	((CAMFImporter_NodeElement_Volume*)ne)->MaterialID = materialid;
-	((CAMFImporter_NodeElement_Volume*)ne)->Type = type;
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool col_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("volume");
-			if(XML_CheckNode_NameEqual("color"))
-			{
-				// Check if data already defined.
-				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <volume>.");
-				// read data and set flag about it
-				ParseNode_Color();
-				col_read = true;
-
-				continue;
-			}
-
-			if(XML_CheckNode_NameEqual("triangle")) { ParseNode_Triangle(); continue; }
-			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
-		MACRO_NODECHECK_LOOPEND("volume");
-		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.
-}
-
-// <triangle>
-// </triangle>
-// Defines a 3D triangle from three vertices, according to the right-hand rule (counter-clockwise when looking from the outside).
-// Multi elements - Yes.
-// Parent element - <volume>.
-//
-// Children elements:
-//   <v1>, <v2>, <v3>
-//   Multi elements - No.
-//   Index of the desired vertices in a triangle or edge.
-void AMFImporter::ParseNode_Triangle()
-{
-CAMFImporter_NodeElement* ne;
-
-	// create new color object.
-	ne = new CAMFImporter_NodeElement_Triangle(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Triangle& als = *((CAMFImporter_NodeElement_Triangle*)ne);// alias for convenience
-
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool col_read = false, tex_read = false;
-		bool read_flag[3] = { false, false, false };
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("triangle");
-			if(XML_CheckNode_NameEqual("color"))
-			{
-				// Check if data already defined.
-				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <triangle>.");
-				// read data and set flag about it
-				ParseNode_Color();
-				col_read = true;
-
-				continue;
-			}
-
-			if(XML_CheckNode_NameEqual("texmap"))// new name of node: "texmap".
-			{
-				// Check if data already defined.
-				if(tex_read) Throw_MoreThanOnceDefined("texmap", "Only one texture coordinate can be defined for <triangle>.");
-				// read data and set flag about it
-				ParseNode_TexMap();
-				tex_read = true;
-
-				continue;
-			}
-			else if(XML_CheckNode_NameEqual("map"))// old name of node: "map".
-			{
-				// Check if data already defined.
-				if(tex_read) Throw_MoreThanOnceDefined("map", "Only one texture coordinate can be defined for <triangle>.");
-				// read data and set flag about it
-				ParseNode_TexMap(true);
-				tex_read = true;
-
-				continue;
-			}
-
-			MACRO_NODECHECK_READCOMP_U32("v1", read_flag[0], als.V[0]);
-			MACRO_NODECHECK_READCOMP_U32("v2", read_flag[1], als.V[1]);
-			MACRO_NODECHECK_READCOMP_U32("v3", read_flag[2], als.V[2]);
-		MACRO_NODECHECK_LOOPEND("triangle");
-		ParseHelper_Node_Exit();
-		// check that all components was defined
-		if((read_flag[0] && read_flag[1] && read_flag[2]) == 0) throw DeadlyImportError("Not all vertices of the triangle are defined.");
-
-	}// 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.
-}
-
-}// namespace Assimp
-
-#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 0 - 166
Engine/lib/assimp/code/AMF/AMFImporter_Macro.hpp

@@ -1,166 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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_Macro.hpp
-/// \brief Useful macrodefines.
-/// \date 2016
-/// \author [email protected]
-
-#pragma once
-#ifndef AMFIMPORTER_MACRO_HPP_INCLUDED
-#define AMFIMPORTER_MACRO_HPP_INCLUDED
-
-/// \def MACRO_ATTRREAD_LOOPBEG
-/// Begin of loop that read attributes values.
-#define MACRO_ATTRREAD_LOOPBEG \
-	for(int idx = 0, idx_end = mReader->getAttributeCount(); idx < idx_end; idx++) \
-	{ \
-		std::string an(mReader->getAttributeName(idx));
-
-/// \def MACRO_ATTRREAD_LOOPEND
-/// End of loop that read attributes values.
-#define MACRO_ATTRREAD_LOOPEND \
-		Throw_IncorrectAttr(an); \
-	}
-
-/// \def MACRO_ATTRREAD_LOOPEND_WSKIP
-/// End of loop that read attributes values. Difference from \ref MACRO_ATTRREAD_LOOPEND in that: current macro skip unknown attributes, but
-/// \ref MACRO_ATTRREAD_LOOPEND throw an exception.
-#define MACRO_ATTRREAD_LOOPEND_WSKIP \
-		continue; \
-	}
-
-/// \def MACRO_ATTRREAD_CHECK_REF
-/// Check current attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
-/// "continue" will called.
-/// \param [in] pAttrName - attribute name.
-/// \param [out] pVarName - output variable name.
-/// \param [in] pFunction - function which read attribute value and write it to pVarName.
-#define MACRO_ATTRREAD_CHECK_REF(pAttrName, pVarName, pFunction) \
-	if(an == pAttrName) \
-	{ \
-		pFunction(idx, pVarName); \
-		continue; \
-	}
-
-/// \def MACRO_ATTRREAD_CHECK_RET
-/// Check current attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
-/// If result was read then  "continue" will called.
-/// \param [in] pAttrName - attribute name.
-/// \param [out] pVarName - output variable name.
-/// \param [in] pFunction - function which read attribute value and write it to pVarName.
-#define MACRO_ATTRREAD_CHECK_RET(pAttrName, pVarName, pFunction) \
-	if(an == pAttrName) \
-	{ \
-		pVarName = pFunction(idx); \
-		continue; \
-	}
-
-/// \def MACRO_NODECHECK_LOOPBEGIN(pNodeName)
-/// Begin of loop of parsing child nodes. Do not add ';' at end.
-/// \param [in] pNodeName - current node name.
-#define MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
-	do { \
-	bool close_found = false; \
-	 \
-	while(mReader->read()) \
-	{ \
-		if(mReader->getNodeType() == irr::io::EXN_ELEMENT) \
-		{
-
-/// \def MACRO_NODECHECK_LOOPEND(pNodeName)
-/// End of loop of parsing child nodes.
-/// \param [in] pNodeName - current node name.
-#define MACRO_NODECHECK_LOOPEND(pNodeName) \
-			XML_CheckNode_SkipUnsupported(pNodeName); \
-		}/* if(mReader->getNodeType() == irr::io::EXN_ELEMENT) */ \
-		else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) \
-		{ \
-			if(XML_CheckNode_NameEqual(pNodeName)) \
-			{ \
-				close_found = true; \
-	 \
-				break; \
-			} \
-		}/* else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) */ \
-	}/* while(mReader->read()) */ \
-	 \
-	if(!close_found) Throw_CloseNotFound(pNodeName); \
-	 \
-	} while(false)
-
-/// \def MACRO_NODECHECK_READCOMP_F
-/// Check current node name and if it equal to requested then read value. Result write to output variable of type "float".
-/// If result was read then  "continue" will called. Also check if node data already read then raise exception.
-/// \param [in] pNodeName - node name.
-/// \param [in, out] pReadFlag - read flag.
-/// \param [out] pVarName - output variable name.
-#define MACRO_NODECHECK_READCOMP_F(pNodeName, pReadFlag, pVarName) \
-	if(XML_CheckNode_NameEqual(pNodeName)) \
-	{ \
-		/* Check if field already read before. */ \
-		if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
-		/* Read color component and assign it to object. */ \
-		pVarName = XML_ReadNode_GetVal_AsFloat(); \
-		pReadFlag = true; \
-		continue; \
-	}
-
-/// \def MACRO_NODECHECK_READCOMP_U32
-/// Check current node name and if it equal to requested then read value. Result write to output variable of type "uint32_t".
-/// If result was read then  "continue" will called. Also check if node data already read then raise exception.
-/// \param [in] pNodeName - node name.
-/// \param [in, out] pReadFlag - read flag.
-/// \param [out] pVarName - output variable name.
-#define MACRO_NODECHECK_READCOMP_U32(pNodeName, pReadFlag, pVarName) \
-	if(XML_CheckNode_NameEqual(pNodeName)) \
-	{ \
-		/* Check if field already read before. */ \
-		if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
-		/* Read color component and assign it to object. */ \
-		pVarName = XML_ReadNode_GetVal_AsU32(); \
-		pReadFlag = true; \
-		continue; \
-	}
-
-#endif // AMFIMPORTER_MACRO_HPP_INCLUDED

+ 0 - 340
Engine/lib/assimp/code/AMF/AMFImporter_Node.hpp

@@ -1,340 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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_Node.hpp
-/// \brief Elements of scene graph.
-/// \date 2016
-/// \author [email protected]
-
-#pragma once
-#ifndef INCLUDED_AI_AMF_IMPORTER_NODE_H
-#define INCLUDED_AI_AMF_IMPORTER_NODE_H
-
-// Header files, stdlib.
-#include <list>
-#include <string>
-#include <vector>
-
-// Header files, Assimp.
-#include "assimp/types.h"
-#include "assimp/scene.h"
-
-/// \class CAMFImporter_NodeElement
-/// Base class for elements of nodes.
-class CAMFImporter_NodeElement {
-public:
-	/// Define what data type contain node element.
-	enum EType {
-		ENET_Color,        ///< Color element: <color>.
-		ENET_Constellation,///< Grouping element: <constellation>.
-		ENET_Coordinates,  ///< Coordinates element: <coordinates>.
-		ENET_Edge,         ///< Edge element: <edge>.
-		ENET_Instance,     ///< Grouping element: <constellation>.
-		ENET_Material,     ///< Material element: <material>.
-		ENET_Metadata,     ///< Metadata element: <metadata>.
-		ENET_Mesh,         ///< Metadata element: <mesh>.
-		ENET_Object,       ///< Element which hold object: <object>.
-		ENET_Root,         ///< Root element: <amf>.
-		ENET_Triangle,     ///< Triangle element: <triangle>.
-		ENET_TexMap,       ///< Texture coordinates element: <texmap> or <map>.
-		ENET_Texture,      ///< Texture element: <texture>.
-		ENET_Vertex,       ///< Vertex element: <vertex>.
-		ENET_Vertices,     ///< Vertex element: <vertices>.
-		ENET_Volume,       ///< Volume element: <volume>.
-
-		ENET_Invalid       ///< Element has invalid type and possible contain invalid data.
-	};
-
-	const EType Type;///< Type of element.
-	std::string ID;///< ID of element.
-	CAMFImporter_NodeElement* Parent;///< Parent element. If nullptr then this node is root.
-	std::list<CAMFImporter_NodeElement*> Child;///< Child elements.
-
-public:                                               /// Destructor, virtual..
-    virtual ~CAMFImporter_NodeElement() {
-        // empty
-    }
-
-	/// Disabled copy constructor and co.
-	CAMFImporter_NodeElement(const CAMFImporter_NodeElement& pNodeElement) = delete;
-    CAMFImporter_NodeElement(CAMFImporter_NodeElement&&) = delete;
-    CAMFImporter_NodeElement& operator=(const CAMFImporter_NodeElement& pNodeElement) = delete;
-	CAMFImporter_NodeElement() = delete;
-
-protected:
-	/// In constructor inheritor must set element type.
-	/// \param [in] pType - element type.
-	/// \param [in] pParent - parent element.
-	CAMFImporter_NodeElement(const EType pType, CAMFImporter_NodeElement* pParent)
-	: Type(pType)
-    , ID()
-    , Parent(pParent)
-    , Child() {
-        // empty
-    }
-};// class IAMFImporter_NodeElement
-
-/// \struct CAMFImporter_NodeElement_Constellation
-/// A collection of objects or constellations with specific relative locations.
-struct CAMFImporter_NodeElement_Constellation : public CAMFImporter_NodeElement {
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Constellation(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Constellation, pParent)
-	{}
-
-};// struct CAMFImporter_NodeElement_Constellation
-
-/// \struct CAMFImporter_NodeElement_Instance
-/// Part of constellation.
-struct CAMFImporter_NodeElement_Instance : public CAMFImporter_NodeElement {
-
-	std::string ObjectID;///< ID of object for instantiation.
-	/// \var Delta - The distance of translation in the x, y, or z direction, respectively, in the referenced object's coordinate system, to
-	/// create an instance of the object in the current constellation.
-	aiVector3D Delta;
-
-	/// \var Rotation - The rotation, in degrees, to rotate the referenced object about its x, y, and z axes, respectively, to create an
-	/// instance of the object in the current constellation. Rotations shall be executed in order of x first, then y, then z.
-	aiVector3D Rotation;
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Instance(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Instance, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Metadata
-/// Structure that define metadata node.
-struct CAMFImporter_NodeElement_Metadata : public CAMFImporter_NodeElement {
-
-	std::string Type;///< Type of "Value". 
-	std::string Value;///< Value.
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Metadata(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Metadata, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Root
-/// Structure that define root node.
-struct CAMFImporter_NodeElement_Root : public CAMFImporter_NodeElement {
-
-	std::string Unit;///< The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
-	std::string Version;///< Version of format.
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Root(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Root, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Color
-/// Structure that define object node.
-struct CAMFImporter_NodeElement_Color : public CAMFImporter_NodeElement {
-	bool Composed;                  ///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color.
-	std::string Color_Composed[4];  ///< By components formulas of composed color. [0..3] - RGBA.
-	aiColor4D Color;                ///< Constant color.
-	std::string Profile;            ///< The ICC color space used to interpret the three color channels r, g and b..
-
-	/// @brief  Constructor.
-	/// @param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Color(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_Color, pParent)
-    , Composed( false )
-    , Color()
-    , Profile() {
-        // empty
-    }
-};
-
-/// \struct CAMFImporter_NodeElement_Material
-/// Structure that define material node.
-struct CAMFImporter_NodeElement_Material : public CAMFImporter_NodeElement {
-	
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Material(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Material, pParent)
-	{}
-
-};
-
-/// \struct CAMFImporter_NodeElement_Object
-/// Structure that define object node.
-struct CAMFImporter_NodeElement_Object : public CAMFImporter_NodeElement {
-
-    /// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Object(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Object, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Mesh
-/// Structure that define mesh node.
-struct CAMFImporter_NodeElement_Mesh : public CAMFImporter_NodeElement {
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Mesh(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Mesh, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Vertex
-/// Structure that define vertex node.
-struct CAMFImporter_NodeElement_Vertex : public CAMFImporter_NodeElement {
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Vertex(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Vertex, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Edge
-/// Structure that define edge node.
-struct CAMFImporter_NodeElement_Edge : public CAMFImporter_NodeElement {
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Edge(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Edge, pParent)
-	{}
-
-};
-
-/// \struct CAMFImporter_NodeElement_Vertices
-/// Structure that define vertices node.
-struct CAMFImporter_NodeElement_Vertices : public CAMFImporter_NodeElement {
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Vertices(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Vertices, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Volume
-/// Structure that define volume node.
-struct CAMFImporter_NodeElement_Volume : public CAMFImporter_NodeElement {
-	std::string MaterialID;///< Which material to use.
-	std::string Type;///< What this volume describes can be “region” or “support”. If none specified, “object” is assumed.
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Volume(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Volume, pParent)
-	{}
-};
-
-/// \struct CAMFImporter_NodeElement_Coordinates
-/// Structure that define coordinates node.
-struct CAMFImporter_NodeElement_Coordinates : public CAMFImporter_NodeElement
-{
-	aiVector3D Coordinate;///< Coordinate.
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Coordinates(CAMFImporter_NodeElement* pParent)
-		: CAMFImporter_NodeElement(ENET_Coordinates, pParent)
-	{}
-
-};
-
-/// \struct CAMFImporter_NodeElement_TexMap
-/// Structure that define texture coordinates node.
-struct CAMFImporter_NodeElement_TexMap : public CAMFImporter_NodeElement {
-	aiVector3D TextureCoordinate[3];///< Texture coordinates.
-	std::string TextureID_R;///< Texture ID for red color component.
-	std::string TextureID_G;///< Texture ID for green color component.
-	std::string TextureID_B;///< Texture ID for blue color component.
-	std::string TextureID_A;///< Texture ID for alpha color component.
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_TexMap(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_TexMap, pParent)
-    , TextureCoordinate{}
-    , TextureID_R()
-    , TextureID_G()
-    , TextureID_B()
-    , TextureID_A()	{
-        // empty
-    }
-};
-
-/// \struct CAMFImporter_NodeElement_Triangle
-/// Structure that define triangle node.
-struct CAMFImporter_NodeElement_Triangle : public CAMFImporter_NodeElement {
-	size_t V[3];///< Triangle vertices.
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Triangle(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_Triangle, pParent) {
-        // empty
-    }
-};
-
-/// Structure that define texture node.
-struct CAMFImporter_NodeElement_Texture : public CAMFImporter_NodeElement {
-	size_t Width, Height, Depth;///< Size of the texture.
-	std::vector<uint8_t> Data;///< Data of the texture.
-	bool Tiled;
-
-	/// Constructor.
-	/// \param [in] pParent - pointer to parent node.
-	CAMFImporter_NodeElement_Texture(CAMFImporter_NodeElement* pParent)
-	: CAMFImporter_NodeElement(ENET_Texture, pParent)
-    , Width( 0 )
-    , Height( 0 )
-    , Depth( 0 )
-    , Data()
-    , Tiled( false ){
-        // empty
-    }
-};
-
-#endif // INCLUDED_AI_AMF_IMPORTER_NODE_H

+ 0 - 978
Engine/lib/assimp/code/AMF/AMFImporter_Postprocess.cpp

@@ -1,978 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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;
-
-					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

+ 0 - 846
Engine/lib/assimp/code/Assbin/AssbinExporter.cpp

@@ -1,846 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2019, 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  AssbinExporter.cpp
- *  ASSBIN exporter main code
- */
-
-#ifndef ASSIMP_BUILD_NO_EXPORT
-#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
-
-#include "Common/assbin_chunks.h"
-#include "PostProcessing/ProcessHelper.h"
-
-#include <assimp/version.h>
-#include <assimp/IOStream.hpp>
-#include <assimp/IOSystem.hpp>
-#include <assimp/Exporter.hpp>
-#include <assimp/Exceptional.h>
-
-#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#   include <zlib.h>
-#else
-#   include "../contrib/zlib/zlib.h"
-#endif
-
-#include <time.h>
-
-namespace Assimp {
-
-template <typename T>
-size_t Write(IOStream * stream, const T& v) {
-    return stream->Write( &v, sizeof(T), 1 );
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an aiString
-template <>
-inline
-size_t Write<aiString>(IOStream * stream, const aiString& s) {
-    const size_t s2 = (uint32_t)s.length;
-    stream->Write(&s,4,1);
-    stream->Write(s.data,s2,1);
-
-    return s2+4;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an unsigned int as uint32_t
-template <>
-inline
-size_t Write<unsigned int>(IOStream * stream, const unsigned int& w) {
-    const uint32_t t = (uint32_t)w;
-    if (w > t) {
-        // this shouldn't happen, integers in Assimp data structures never exceed 2^32
-        throw DeadlyExportError("loss of data due to 64 -> 32 bit integer conversion");
-    }
-
-    stream->Write(&t,4,1);
-
-    return 4;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an unsigned int as uint16_t
-template <>
-inline
-size_t Write<uint16_t>(IOStream * stream, const uint16_t& w) {
-    static_assert(sizeof(uint16_t)==2, "sizeof(uint16_t)==2");
-    stream->Write(&w,2,1);
-
-    return 2;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a float
-template <>
-inline
-size_t Write<float>(IOStream * stream, const float& f) {
-    static_assert(sizeof(float)==4, "sizeof(float)==4");
-    stream->Write(&f,4,1);
-
-    return 4;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a double
-template <>
-inline
-size_t Write<double>(IOStream * stream, const double& f) {
-    static_assert(sizeof(double)==8, "sizeof(double)==8");
-    stream->Write(&f,8,1);
-
-    return 8;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a vec3
-template <>
-inline
-size_t Write<aiVector3D>(IOStream * stream, const aiVector3D& v) {
-    size_t t = Write<float>(stream,v.x);
-    t += Write<float>(stream,v.y);
-    t += Write<float>(stream,v.z);
-
-    return t;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a color value
-template <>
-inline
-size_t Write<aiColor3D>(IOStream * stream, const aiColor3D& v) {
-    size_t t = Write<float>(stream,v.r);
-    t += Write<float>(stream,v.g);
-    t += Write<float>(stream,v.b);
-
-    return t;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a color value
-template <>
-inline
-size_t Write<aiColor4D>(IOStream * stream, const aiColor4D& v) {
-    size_t t = Write<float>(stream,v.r);
-    t += Write<float>(stream,v.g);
-    t += Write<float>(stream,v.b);
-    t += Write<float>(stream,v.a);
-
-    return t;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a quaternion
-template <>
-inline
-size_t Write<aiQuaternion>(IOStream * stream, const aiQuaternion& v) {
-    size_t t = Write<float>(stream,v.w);
-    t += Write<float>(stream,v.x);
-    t += Write<float>(stream,v.y);
-    t += Write<float>(stream,v.z);
-    ai_assert(t == 16);
-
-    return 16;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a vertex weight
-template <>
-inline
-size_t Write<aiVertexWeight>(IOStream * stream, const aiVertexWeight& v) {
-    size_t t = Write<unsigned int>(stream,v.mVertexId);
-
-    return t+Write<float>(stream,v.mWeight);
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize a mat4x4
-template <>
-inline
-size_t Write<aiMatrix4x4>(IOStream * stream, const aiMatrix4x4& m) {
-    for (unsigned int i = 0; i < 4;++i) {
-        for (unsigned int i2 = 0; i2 < 4;++i2) {
-            Write<float>(stream,m[i][i2]);
-        }
-    }
-
-    return 64;
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an aiVectorKey
-template <>
-inline
-size_t Write<aiVectorKey>(IOStream * stream, const aiVectorKey& v) {
-    const size_t t = Write<double>(stream,v.mTime);
-    return t + Write<aiVector3D>(stream,v.mValue);
-}
-
-// -----------------------------------------------------------------------------------
-// Serialize an aiQuatKey
-template <>
-inline
-size_t Write<aiQuatKey>(IOStream * stream, const aiQuatKey& v) {
-    const size_t t = Write<double>(stream,v.mTime);
-    return t + Write<aiQuaternion>(stream,v.mValue);
-}
-
-template <typename T>
-inline
-size_t WriteBounds(IOStream * stream, const T* in, unsigned int size) {
-    T minc, maxc;
-    ArrayBounds(in,size,minc,maxc);
-
-    const size_t t = Write<T>(stream,minc);
-    return t + Write<T>(stream,maxc);
-}
-
-// We use this to write out non-byte arrays so that we write using the specializations.
-// This way we avoid writing out extra bytes that potentially come from struct alignment.
-template <typename T>
-inline
-size_t WriteArray(IOStream * stream, const T* in, unsigned int size) {
-    size_t n = 0;
-    for (unsigned int i=0; i<size; i++) n += Write<T>(stream,in[i]);
-
-    return n;
-}
-
-// ----------------------------------------------------------------------------------
-/** @class  AssbinChunkWriter
- *  @brief  Chunk writer mechanism for the .assbin file structure
- *
- *  This is a standard in-memory IOStream (most of the code is based on BlobIOStream),
- *  the difference being that this takes another IOStream as a "container" in the
- *  constructor, and when it is destroyed, it appends the magic number, the chunk size,
- *  and the chunk contents to the container stream. This allows relatively easy chunk
- *  chunk construction, even recursively.
- */
-class AssbinChunkWriter : public IOStream
-{
-private:
-
-    uint8_t* buffer;
-    uint32_t magic;
-    IOStream * container;
-    size_t cur_size, cursor, initial;
-
-private:
-    // -------------------------------------------------------------------
-    void Grow(size_t need = 0)
-    {
-        size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) ));
-
-        const uint8_t* const old = buffer;
-        buffer = new uint8_t[new_size];
-
-        if (old) {
-            memcpy(buffer,old,cur_size);
-            delete[] old;
-        }
-
-        cur_size = new_size;
-    }
-
-public:
-
-    AssbinChunkWriter( IOStream * container, uint32_t magic, size_t initial = 4096)
-        : buffer(NULL), magic(magic), container(container), cur_size(0), cursor(0), initial(initial)
-    {
-    }
-
-    virtual ~AssbinChunkWriter()
-    {
-        if (container) {
-            container->Write( &magic, sizeof(uint32_t), 1 );
-            container->Write( &cursor, sizeof(uint32_t), 1 );
-            container->Write( buffer, 1, cursor );
-        }
-        if (buffer) delete[] buffer;
-    }
-
-    void * GetBufferPointer() { return buffer; }
-
-    // -------------------------------------------------------------------
-    virtual size_t Read(void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) {
-        return 0;
-    }
-    virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
-        return aiReturn_FAILURE;
-    }
-    virtual size_t Tell() const {
-        return cursor;
-    }
-    virtual void Flush() {
-        // not implemented
-    }
-
-    virtual size_t FileSize() const {
-        return cursor;
-    }
-
-    // -------------------------------------------------------------------
-    virtual size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {
-        pSize *= pCount;
-        if (cursor + pSize > cur_size) {
-            Grow(cursor + pSize);
-        }
-
-        memcpy(buffer+cursor, pvBuffer, pSize);
-        cursor += pSize;
-
-        return pCount;
-    }
-
-};
-
-// ----------------------------------------------------------------------------------
-/** @class  AssbinExport
- *  @brief  Assbin exporter class
- *
- *  This class performs the .assbin exporting, and is responsible for the file layout.
- */
-class AssbinExport
-{
-private:
-    bool shortened;
-    bool compressed;
-
-protected:
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryNode( IOStream * container, const aiNode* node)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODE );
-
-        unsigned int nb_metadata = (node->mMetaData != NULL ? node->mMetaData->mNumProperties : 0);
-
-        Write<aiString>(&chunk,node->mName);
-        Write<aiMatrix4x4>(&chunk,node->mTransformation);
-        Write<unsigned int>(&chunk,node->mNumChildren);
-        Write<unsigned int>(&chunk,node->mNumMeshes);
-        Write<unsigned int>(&chunk,nb_metadata);
-
-        for (unsigned int i = 0; i < node->mNumMeshes;++i) {
-            Write<unsigned int>(&chunk,node->mMeshes[i]);
-        }
-
-        for (unsigned int i = 0; i < node->mNumChildren;++i) {
-            WriteBinaryNode( &chunk, node->mChildren[i] );
-        }
-
-        for (unsigned int i = 0; i < nb_metadata; ++i) {
-            const aiString& key = node->mMetaData->mKeys[i];
-            aiMetadataType type = node->mMetaData->mValues[i].mType;
-            void* value = node->mMetaData->mValues[i].mData;
-
-            Write<aiString>(&chunk, key);
-            Write<uint16_t>(&chunk, 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;
-#ifdef SWIG
-                case FORCE_32BIT:
-#endif // SWIG
-                default:
-                    break;
-            }
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryTexture(IOStream * container, const aiTexture* tex)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AITEXTURE );
-
-        Write<unsigned int>(&chunk,tex->mWidth);
-        Write<unsigned int>(&chunk,tex->mHeight);
-        chunk.Write( tex->achFormatHint, sizeof(char), 4 );
-
-        if(!shortened) {
-            if (!tex->mHeight) {
-                chunk.Write(tex->pcData,1,tex->mWidth);
-            }
-            else {
-                chunk.Write(tex->pcData,1,tex->mWidth*tex->mHeight*4);
-            }
-        }
-
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryBone(IOStream * container, const aiBone* b)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIBONE );
-
-        Write<aiString>(&chunk,b->mName);
-        Write<unsigned int>(&chunk,b->mNumWeights);
-        Write<aiMatrix4x4>(&chunk,b->mOffsetMatrix);
-
-        // for the moment we write dumb min/max values for the bones, too.
-        // maybe I'll add a better, hash-like solution later
-        if (shortened) {
-            WriteBounds(&chunk,b->mWeights,b->mNumWeights);
-        } // else write as usual
-        else WriteArray<aiVertexWeight>(&chunk,b->mWeights,b->mNumWeights);
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryMesh(IOStream * container, const aiMesh* mesh)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMESH );
-
-        Write<unsigned int>(&chunk,mesh->mPrimitiveTypes);
-        Write<unsigned int>(&chunk,mesh->mNumVertices);
-        Write<unsigned int>(&chunk,mesh->mNumFaces);
-        Write<unsigned int>(&chunk,mesh->mNumBones);
-        Write<unsigned int>(&chunk,mesh->mMaterialIndex);
-
-        // first of all, write bits for all existent vertex components
-        unsigned int c = 0;
-        if (mesh->mVertices) {
-            c |= ASSBIN_MESH_HAS_POSITIONS;
-        }
-        if (mesh->mNormals) {
-            c |= ASSBIN_MESH_HAS_NORMALS;
-        }
-        if (mesh->mTangents && mesh->mBitangents) {
-            c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS;
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
-            if (!mesh->mTextureCoords[n]) {
-                break;
-            }
-            c |= ASSBIN_MESH_HAS_TEXCOORD(n);
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
-            if (!mesh->mColors[n]) {
-                break;
-            }
-            c |= ASSBIN_MESH_HAS_COLOR(n);
-        }
-        Write<unsigned int>(&chunk,c);
-
-        aiVector3D minVec, maxVec;
-        if (mesh->mVertices) {
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mVertices,mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiVector3D>(&chunk,mesh->mVertices,mesh->mNumVertices);
-        }
-        if (mesh->mNormals) {
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mNormals,mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiVector3D>(&chunk,mesh->mNormals,mesh->mNumVertices);
-        }
-        if (mesh->mTangents && mesh->mBitangents) {
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mTangents,mesh->mNumVertices);
-                WriteBounds(&chunk,mesh->mBitangents,mesh->mNumVertices);
-            } // else write as usual
-            else {
-                WriteArray<aiVector3D>(&chunk,mesh->mTangents,mesh->mNumVertices);
-                WriteArray<aiVector3D>(&chunk,mesh->mBitangents,mesh->mNumVertices);
-            }
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
-            if (!mesh->mColors[n])
-                break;
-
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mColors[n],mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiColor4D>(&chunk,mesh->mColors[n],mesh->mNumVertices);
-        }
-        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
-            if (!mesh->mTextureCoords[n])
-                break;
-
-            // write number of UV components
-            Write<unsigned int>(&chunk,mesh->mNumUVComponents[n]);
-
-            if (shortened) {
-                WriteBounds(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices);
-            } // else write as usual
-            else WriteArray<aiVector3D>(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices);
-        }
-
-        // write faces. There are no floating-point calculations involved
-        // in these, so we can write a simple hash over the face data
-        // to the dump file. We generate a single 32 Bit hash for 512 faces
-        // using Assimp's standard hashing function.
-        if (shortened) {
-            unsigned int processed = 0;
-            for (unsigned int job;(job = std::min(mesh->mNumFaces-processed,512u));processed += job) {
-
-                uint32_t hash = 0;
-                for (unsigned int a = 0; a < job;++a) {
-
-                    const aiFace& f = mesh->mFaces[processed+a];
-                    uint32_t tmp = f.mNumIndices;
-                    hash = SuperFastHash(reinterpret_cast<const char*>(&tmp),sizeof tmp,hash);
-                    for (unsigned int i = 0; i < f.mNumIndices; ++i) {
-                        static_assert(AI_MAX_VERTICES <= 0xffffffff, "AI_MAX_VERTICES <= 0xffffffff");
-                        tmp = static_cast<uint32_t>( f.mIndices[i] );
-                        hash = SuperFastHash(reinterpret_cast<const char*>(&tmp),sizeof tmp,hash);
-                    }
-                }
-                Write<unsigned int>(&chunk,hash);
-            }
-        }
-        else // else write as usual
-        {
-            // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
-            for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
-                const aiFace& f = mesh->mFaces[i];
-
-                static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
-                Write<uint16_t>(&chunk,f.mNumIndices);
-
-                for (unsigned int a = 0; a < f.mNumIndices;++a) {
-                    if (mesh->mNumVertices < (1u<<16)) {
-                        Write<uint16_t>(&chunk,f.mIndices[a]);
-                    }
-                    else Write<unsigned int>(&chunk,f.mIndices[a]);
-                }
-            }
-        }
-
-        // write bones
-        if (mesh->mNumBones) {
-            for (unsigned int a = 0; a < mesh->mNumBones;++a) {
-                const aiBone* b = mesh->mBones[a];
-                WriteBinaryBone(&chunk,b);
-            }
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryMaterialProperty(IOStream * container, const aiMaterialProperty* prop)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMATERIALPROPERTY );
-
-        Write<aiString>(&chunk,prop->mKey);
-        Write<unsigned int>(&chunk,prop->mSemantic);
-        Write<unsigned int>(&chunk,prop->mIndex);
-
-        Write<unsigned int>(&chunk,prop->mDataLength);
-        Write<unsigned int>(&chunk,(unsigned int)prop->mType);
-        chunk.Write(prop->mData,1,prop->mDataLength);
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryMaterial(IOStream * container, const aiMaterial* mat)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMATERIAL);
-
-        Write<unsigned int>(&chunk,mat->mNumProperties);
-        for (unsigned int i = 0; i < mat->mNumProperties;++i) {
-            WriteBinaryMaterialProperty( &chunk, mat->mProperties[i]);
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryNodeAnim(IOStream * container, const aiNodeAnim* nd)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODEANIM );
-
-        Write<aiString>(&chunk,nd->mNodeName);
-        Write<unsigned int>(&chunk,nd->mNumPositionKeys);
-        Write<unsigned int>(&chunk,nd->mNumRotationKeys);
-        Write<unsigned int>(&chunk,nd->mNumScalingKeys);
-        Write<unsigned int>(&chunk,nd->mPreState);
-        Write<unsigned int>(&chunk,nd->mPostState);
-
-        if (nd->mPositionKeys) {
-            if (shortened) {
-                WriteBounds(&chunk,nd->mPositionKeys,nd->mNumPositionKeys);
-
-            } // else write as usual
-            else WriteArray<aiVectorKey>(&chunk,nd->mPositionKeys,nd->mNumPositionKeys);
-        }
-        if (nd->mRotationKeys) {
-            if (shortened) {
-                WriteBounds(&chunk,nd->mRotationKeys,nd->mNumRotationKeys);
-
-            } // else write as usual
-            else WriteArray<aiQuatKey>(&chunk,nd->mRotationKeys,nd->mNumRotationKeys);
-        }
-        if (nd->mScalingKeys) {
-            if (shortened) {
-                WriteBounds(&chunk,nd->mScalingKeys,nd->mNumScalingKeys);
-
-            } // else write as usual
-            else WriteArray<aiVectorKey>(&chunk,nd->mScalingKeys,nd->mNumScalingKeys);
-        }
-    }
-
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryAnim( IOStream * container, const aiAnimation* anim )
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIANIMATION );
-
-        Write<aiString>(&chunk,anim->mName);
-        Write<double>(&chunk,anim->mDuration);
-        Write<double>(&chunk,anim->mTicksPerSecond);
-        Write<unsigned int>(&chunk,anim->mNumChannels);
-
-        for (unsigned int a = 0; a < anim->mNumChannels;++a) {
-            const aiNodeAnim* nd = anim->mChannels[a];
-            WriteBinaryNodeAnim(&chunk,nd);
-        }
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryLight( IOStream * container, const aiLight* l )
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AILIGHT );
-
-        Write<aiString>(&chunk,l->mName);
-        Write<unsigned int>(&chunk,l->mType);
-
-        if (l->mType != aiLightSource_DIRECTIONAL) {
-            Write<float>(&chunk,l->mAttenuationConstant);
-            Write<float>(&chunk,l->mAttenuationLinear);
-            Write<float>(&chunk,l->mAttenuationQuadratic);
-        }
-
-        Write<aiColor3D>(&chunk,l->mColorDiffuse);
-        Write<aiColor3D>(&chunk,l->mColorSpecular);
-        Write<aiColor3D>(&chunk,l->mColorAmbient);
-
-        if (l->mType == aiLightSource_SPOT) {
-            Write<float>(&chunk,l->mAngleInnerCone);
-            Write<float>(&chunk,l->mAngleOuterCone);
-        }
-
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryCamera( IOStream * container, const aiCamera* cam )
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AICAMERA );
-
-        Write<aiString>(&chunk,cam->mName);
-        Write<aiVector3D>(&chunk,cam->mPosition);
-        Write<aiVector3D>(&chunk,cam->mLookAt);
-        Write<aiVector3D>(&chunk,cam->mUp);
-        Write<float>(&chunk,cam->mHorizontalFOV);
-        Write<float>(&chunk,cam->mClipPlaneNear);
-        Write<float>(&chunk,cam->mClipPlaneFar);
-        Write<float>(&chunk,cam->mAspect);
-    }
-
-    // -----------------------------------------------------------------------------------
-    void WriteBinaryScene( IOStream * container, const aiScene* scene)
-    {
-        AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AISCENE );
-
-        // basic scene information
-        Write<unsigned int>(&chunk,scene->mFlags);
-        Write<unsigned int>(&chunk,scene->mNumMeshes);
-        Write<unsigned int>(&chunk,scene->mNumMaterials);
-        Write<unsigned int>(&chunk,scene->mNumAnimations);
-        Write<unsigned int>(&chunk,scene->mNumTextures);
-        Write<unsigned int>(&chunk,scene->mNumLights);
-        Write<unsigned int>(&chunk,scene->mNumCameras);
-
-        // write node graph
-        WriteBinaryNode( &chunk, scene->mRootNode );
-
-        // write all meshes
-        for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
-            const aiMesh* mesh = scene->mMeshes[i];
-            WriteBinaryMesh( &chunk,mesh);
-        }
-
-        // write materials
-        for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
-            const aiMaterial* mat = scene->mMaterials[i];
-            WriteBinaryMaterial(&chunk,mat);
-        }
-
-        // write all animations
-        for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
-            const aiAnimation* anim = scene->mAnimations[i];
-            WriteBinaryAnim(&chunk,anim);
-        }
-
-
-        // write all textures
-        for (unsigned int i = 0; i < scene->mNumTextures;++i) {
-            const aiTexture* mesh = scene->mTextures[i];
-            WriteBinaryTexture(&chunk,mesh);
-        }
-
-        // write lights
-        for (unsigned int i = 0; i < scene->mNumLights;++i) {
-            const aiLight* l = scene->mLights[i];
-            WriteBinaryLight(&chunk,l);
-        }
-
-        // write cameras
-        for (unsigned int i = 0; i < scene->mNumCameras;++i) {
-            const aiCamera* cam = scene->mCameras[i];
-            WriteBinaryCamera(&chunk,cam);
-        }
-
-    }
-
-public:
-    AssbinExport()
-        : shortened(false), compressed(false) // temporary settings until properties are introduced for exporters
-    {
-    }
-
-    // -----------------------------------------------------------------------------------
-    // Write a binary model dump
-    void WriteBinaryDump(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
-    {
-        IOStream * out = pIOSystem->Open( pFile, "wb" );
-        if (!out) return;
-
-        time_t tt = time(NULL);
-#if _WIN32
-        tm* p     = gmtime(&tt);
-#else
-        struct tm now;
-        tm* p = gmtime_r(&tt, &now);
-#endif
-
-        // header
-        char s[64];
-        memset( s, 0, 64 );
-#if _MSC_VER >= 1400
-        sprintf_s(s,"ASSIMP.binary-dump.%s",asctime(p));
-#else
-        ai_snprintf(s,64,"ASSIMP.binary-dump.%s",asctime(p));
-#endif
-        out->Write( s, 44, 1 );
-        // == 44 bytes
-
-        Write<unsigned int>( out, ASSBIN_VERSION_MAJOR );
-        Write<unsigned int>( out, ASSBIN_VERSION_MINOR );
-        Write<unsigned int>( out, aiGetVersionRevision() );
-        Write<unsigned int>( out, aiGetCompileFlags() );
-        Write<uint16_t>( out, shortened );
-        Write<uint16_t>( out, compressed );
-        // ==  20 bytes
-
-        char buff[256];
-        strncpy(buff,pFile,256);
-        out->Write(buff,sizeof(char),256);
-
-        char cmd[] = "\0";
-        strncpy(buff,cmd,128);
-        out->Write(buff,sizeof(char),128);
-
-        // leave 64 bytes free for future extensions
-        memset(buff,0xcd,64);
-        out->Write(buff,sizeof(char),64);
-        // == 435 bytes
-
-        // ==== total header size: 512 bytes
-        ai_assert( out->Tell() == ASSBIN_HEADER_LENGTH );
-
-        // Up to here the data is uncompressed. For compressed files, the rest
-        // is compressed using standard DEFLATE from zlib.
-        if (compressed)
-        {
-            AssbinChunkWriter uncompressedStream( NULL, 0 );
-            WriteBinaryScene( &uncompressedStream, pScene );
-
-            uLongf uncompressedSize = static_cast<uLongf>(uncompressedStream.Tell());
-            uLongf compressedSize = (uLongf)compressBound(uncompressedSize);
-            uint8_t* compressedBuffer = new uint8_t[ compressedSize ];
-
-            int res = compress2( compressedBuffer, &compressedSize, (const Bytef*)uncompressedStream.GetBufferPointer(), uncompressedSize, 9 );
-            if(res != Z_OK)
-            {
-                delete [] compressedBuffer;
-                pIOSystem->Close(out);
-                throw DeadlyExportError("Compression failed.");
-            }
-
-            out->Write( &uncompressedSize, sizeof(uint32_t), 1 );
-            out->Write( compressedBuffer, sizeof(char), compressedSize );
-
-            delete[] compressedBuffer;
-        }
-        else
-        {
-            WriteBinaryScene( out, pScene );
-        }
-
-        pIOSystem->Close( out );
-    }
-};
-
-void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
-    AssbinExport exporter;
-    exporter.WriteBinaryDump( pFile, pIOSystem, pScene );
-}
-} // end of namespace Assimp
-
-#endif // ASSIMP_BUILD_NO_ASSBIN_EXPORTER
-#endif // ASSIMP_BUILD_NO_EXPORT

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 247 - 311
Engine/lib/assimp/code/AssetLib/3DS/3DSConverter.cpp


+ 155 - 153
Engine/lib/assimp/code/3DS/3DSExporter.cpp → Engine/lib/assimp/code/AssetLib/3DS/3DSExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -43,120 +43,118 @@ 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(CHUNK_SIZE_NOT_SET);
-        }
+//////////////////////////////////////////////////////////////////////////////////////
+// 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
+    };
 
-        ~ChunkWriter() {
-            std::size_t head_pos = writer.GetCurrentPos();
+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);
+    }
 
-            ai_assert(head_pos > chunk_start_pos);
-            const std::size_t chunk_size = head_pos - chunk_start_pos;
+    ~ChunkWriter() {
+        std::size_t head_pos = writer.GetCurrentPos();
 
-            writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
-            writer.PutU4(static_cast<uint32_t>(chunk_size));
-            writer.SetCurrentPos(head_pos);
-        }
-
-    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 char underscore = '_';
+    char postfix[10] = { 0 };
+    ASSIMP_itoa10(postfix, index);
+
+    std::string result = node.mName.C_Str();
+    if (mesh.mName.length > 0) {
+        result += underscore;
+        result += 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 +165,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,28 +184,26 @@ 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);
 
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAIN);
+    ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAIN);
 
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJMESH);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_OBJMESH);
         WriteMaterials();
         WriteMeshes();
 
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MASTER_SCALE);
+            ChunkWriter curChunk1(writer, Discreet3DS::CHUNK_MASTER_SCALE);
             writer.PutF4(1.0f);
         }
     }
 
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_KEYFRAMER);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_KEYFRAMER);
         WriteHierarchy(*scene->mRootNode, -1, -1);
     }
 }
@@ -217,15 +213,13 @@ 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 chunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+        ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
 
             // Assimp node names are unique and distinct from all mesh-node
             // names we generate; thus we can use them as-is
@@ -237,7 +231,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 = sibling_level;
+                hierarchy_pos = (uint16_t)sibling_level;
             }
 
             // Write the hierarchy position
@@ -260,9 +254,9 @@ 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 chunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRACKINFO);
         {
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
             WriteString(GetMeshName(mesh, mesh_idx, node));
@@ -276,36 +270,41 @@ 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 chunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
-        const aiMaterial& mat = *scene->mMaterials[i];
+        ChunkWriter curRootChunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
+        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);
         }
 
         aiColor3D color;
         if (mat.Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_DIFFUSE);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_DIFFUSE);
             WriteColor(color);
         }
 
         if (mat.Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
             WriteColor(color);
         }
 
         if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
             WriteColor(color);
         }
 
+        float f;
+        if (mat.Get(AI_MATKEY_OPACITY, f) == AI_SUCCESS) {
+            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_TRANSPARENCY);
+            WritePercentChunk(1.0f - f);
+        }
+
         if (mat.Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
             WriteColor(color);
         }
 
@@ -314,7 +313,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;
@@ -331,6 +330,7 @@ void Discreet3DSExporter::WriteMaterials()
             case aiShadingMode_Blinn:
             case aiShadingMode_CookTorrance:
             case aiShadingMode_Fresnel:
+            case aiShadingMode_PBR_BRDF: // Possibly should be Discreet3DS::Metal in some cases but this is undocumented
                 shading_mode_out = Discreet3DS::Phong;
                 break;
 
@@ -341,8 +341,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);
             WritePercentChunk(f);
@@ -359,7 +357,10 @@ void Discreet3DSExporter::WriteMaterials()
             writer.PutI2(1);
         }
 
-        WriteTexture(mat, aiTextureType_DIFFUSE, Discreet3DS::CHUNK_MAT_TEXTURE);
+        // Fallback to BASE_COLOR if no DIFFUSE
+        if (!WriteTexture(mat, aiTextureType_DIFFUSE, Discreet3DS::CHUNK_MAT_TEXTURE))
+            WriteTexture(mat, aiTextureType_BASE_COLOR, Discreet3DS::CHUNK_MAT_TEXTURE);
+
         WriteTexture(mat, aiTextureType_HEIGHT, Discreet3DS::CHUNK_MAT_BUMPMAP);
         WriteTexture(mat, aiTextureType_OPACITY, Discreet3DS::CHUNK_MAT_OPACMAP);
         WriteTexture(mat, aiTextureType_SHININESS, Discreet3DS::CHUNK_MAT_MAT_SHINMAP);
@@ -370,48 +371,47 @@ void Discreet3DSExporter::WriteMaterials()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags)
-{
+// returns true if the texture existed
+bool Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type, uint16_t chunk_flags) {
     aiString path;
     aiTextureMapMode map_mode[2] = {
         aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
     };
     ai_real blend = 1.0;
-    if (mat.GetTexture(type, 0, &path, NULL, NULL, &blend, NULL, map_mode) != AI_SUCCESS || !path.length) {
-        return;
+    if (mat.GetTexture(type, 0, &path, nullptr, nullptr, &blend, nullptr, map_mode) != AI_SUCCESS || !path.length) {
+        return false;
     }
 
     // TODO: handle embedded textures properly
     if (path.data[0] == '*') {
-        ASSIMP_LOG_ERROR("Ignoring embedded texture for export: " + std::string(path.C_Str()));
-        return;
+        ASSIMP_LOG_ERROR("Ignoring embedded texture for export: ", path.C_Str());
+        return false;
     }
 
     ChunkWriter chunk(writer, chunk_flags);
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPFILE);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAPFILE);
         WriteString(path);
     }
 
     WritePercentChunk(blend);
 
     {
-        ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MAP_TILING);
+        ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_MAP_TILING);
         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);
     }
     // TODO: export texture transformation (i.e. UV offset, scale, rotation)
+    return true;
 }
 
 // ------------------------------------------------------------------------------------------------
-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,36 +423,35 @@ 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);
 
         // Vertices in world space
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_VERTLIST);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_VERTLIST);
 
             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 = mesh.mVertices[i];
                 writer.PutF4(v.x);
                 writer.PutF4(v.y);
                 writer.PutF4(v.z);
@@ -461,12 +460,12 @@ void Discreet3DSExporter::WriteMeshes()
 
         // UV coordinates
         if (mesh.HasTextureCoords(0)) {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPLIST);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAPLIST);
             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 = mesh.mTextureCoords[0][i];
+                const aiVector3D &v = mesh.mTextureCoords[0][i];
                 writer.PutF4(v.x);
                 writer.PutF4(v.y);
             }
@@ -474,14 +473,14 @@ void Discreet3DSExporter::WriteMeshes()
 
         // Faces (indices)
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_FACELIST);
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACELIST);
 
             ai_assert(mesh.mNumFaces <= 0xffff);
 
             // 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 +491,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;
                 }
@@ -513,21 +512,25 @@ void Discreet3DSExporter::WriteMeshes()
 
         // Transformation matrix by which the mesh vertices have been pre-transformed with.
         {
-            ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRMATRIX);
-            for (unsigned int r = 0; r < 4; ++r) {
+            ChunkWriter curChunk(writer, Discreet3DS::CHUNK_TRMATRIX);
+            // Store rotation 3x3 matrix row wise
+            for (unsigned int r = 0; r < 3; ++r) {
                 for (unsigned int c = 0; c < 3; ++c) {
                     writer.PutF4(trafo[r][c]);
                 }
             }
+            // Store translation sub vector column wise
+            for (unsigned int r = 0; r < 3; ++r) {
+                writer.PutF4(trafo[r][3]);
+            }
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
-{
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_FACEMAT);
-    const std::string& name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
+void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh &mesh) {
+    ChunkWriter curChunk(writer, Discreet3DS::CHUNK_FACEMAT);
+    const std::string &name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
     WriteString(name);
 
     // Because assimp splits meshes by material, only a single
@@ -542,7 +545,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 +553,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,8 +561,8 @@ void Discreet3DSExporter::WriteString(const aiString& s) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteColor(const aiColor3D& color) {
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_RGBF);
+void Discreet3DSExporter::WriteColor(const aiColor3D &color) {
+    ChunkWriter curChunk(writer, Discreet3DS::CHUNK_RGBF);
     writer.PutF4(color.r);
     writer.PutF4(color.g);
     writer.PutF4(color.b);
@@ -567,16 +570,15 @@ void Discreet3DSExporter::WriteColor(const aiColor3D& color) {
 
 // ------------------------------------------------------------------------------------------------
 void Discreet3DSExporter::WritePercentChunk(float f) {
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_PERCENTF);
+    ChunkWriter curChunk(writer, Discreet3DS::CHUNK_PERCENTF);
     writer.PutF4(f);
 }
 
 // ------------------------------------------------------------------------------------------------
 void Discreet3DSExporter::WritePercentChunk(double f) {
-    ChunkWriter chunk(writer, Discreet3DS::CHUNK_PERCENTD);
+    ChunkWriter ccurChunkhunk(writer, Discreet3DS::CHUNK_PERCENTD);
     writer.PutF8(f);
 }
 
-
 #endif // ASSIMP_BUILD_NO_3DS_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 2 - 2
Engine/lib/assimp/code/3DS/3DSExporter.h → Engine/lib/assimp/code/AssetLib/3DS/3DSExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -73,7 +73,7 @@ public:
 private:
     void WriteMeshes();
     void WriteMaterials();
-    void WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags);
+    bool WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags);
     void WriteFaceMaterialChunk(const aiMesh& mesh);
     int WriteHierarchy(const aiNode& node, int level, int sibling_level);
     void WriteString(const std::string& s);

+ 702 - 0
Engine/lib/assimp/code/AssetLib/3DS/3DSHelper.h

@@ -0,0 +1,702 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 Defines helper data structures for the import of 3DS files */
+
+#ifndef AI_3DSFILEHELPER_H_INC
+#define AI_3DSFILEHELPER_H_INC
+
+#include <assimp/SmoothingGroups.h>
+#include <assimp/SpatialSort.h>
+#include <assimp/StringUtils.h>
+#include <assimp/anim.h>
+#include <assimp/camera.h>
+#include <assimp/light.h>
+#include <assimp/material.h>
+#include <assimp/qnan.h>
+#include <cstdio> //sprintf
+
+namespace Assimp {
+namespace D3DS {
+
+#include <assimp/Compiler/pushpack1.h>
+
+// ---------------------------------------------------------------------------
+/** Defines chunks and data structures.
+*/
+namespace Discreet3DS {
+
+    //! data structure for a single chunk in a .3ds file
+    struct Chunk {
+        uint16_t Flag;
+        uint32_t Size;
+    } PACK_STRUCT;
+
+    //! Used for shading field in material3ds structure
+    //! From AutoDesk 3ds SDK
+    typedef enum {
+        // translated to gouraud shading with wireframe active
+        Wire = 0x0,
+
+        // if this material is set, no vertex normals will
+        // be calculated for the model. Face normals + gouraud
+        Flat = 0x1,
+
+        // standard gouraud shading
+        Gouraud = 0x2,
+
+        // phong shading
+        Phong = 0x3,
+
+        // cooktorrance or anistropic phong shading ...
+        // the exact meaning is unknown, if you know it
+        // feel free to tell me ;-)
+        Metal = 0x4,
+
+        // required by the ASE loader
+        Blinn = 0x5
+    } shadetype3ds;
+
+    // Flags for animated keys
+    enum {
+        KEY_USE_TENS = 0x1,
+        KEY_USE_CONT = 0x2,
+        KEY_USE_BIAS = 0x4,
+        KEY_USE_EASE_TO = 0x8,
+        KEY_USE_EASE_FROM = 0x10
+    };
+
+    enum {
+
+        // ********************************************************************
+        // Basic chunks which can be found everywhere in the file
+        CHUNK_VERSION = 0x0002,
+        CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B
+        CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B
+
+        // Linear color values (gamma = 2.2?)
+        CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B
+        CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B
+
+        CHUNK_PERCENTW = 0x0030, // int2   percentage
+        CHUNK_PERCENTF = 0x0031, // float4  percentage
+        CHUNK_PERCENTD = 0x0032, // float8  percentage
+        // ********************************************************************
+
+        // Prj master chunk
+        CHUNK_PRJ = 0xC23D,
+
+        // MDLI master chunk
+        CHUNK_MLI = 0x3DAA,
+
+        // Primary main chunk of the .3ds file
+        CHUNK_MAIN = 0x4D4D,
+
+        // Mesh main chunk
+        CHUNK_OBJMESH = 0x3D3D,
+
+        // Specifies the background color of the .3ds file
+        // This is passed through the material system for
+        // viewing purposes.
+        CHUNK_BKGCOLOR = 0x1200,
+
+        // Specifies the ambient base color of the scene.
+        // This is added to all materials in the file
+        CHUNK_AMBCOLOR = 0x2100,
+
+        // Specifies the background image for the whole scene
+        // This value is passed through the material system
+        // to the viewer
+        CHUNK_BIT_MAP = 0x1100,
+        CHUNK_BIT_MAP_EXISTS = 0x1101,
+
+        // ********************************************************************
+        // Viewport related stuff. Ignored
+        CHUNK_DEFAULT_VIEW = 0x3000,
+        CHUNK_VIEW_TOP = 0x3010,
+        CHUNK_VIEW_BOTTOM = 0x3020,
+        CHUNK_VIEW_LEFT = 0x3030,
+        CHUNK_VIEW_RIGHT = 0x3040,
+        CHUNK_VIEW_FRONT = 0x3050,
+        CHUNK_VIEW_BACK = 0x3060,
+        CHUNK_VIEW_USER = 0x3070,
+        CHUNK_VIEW_CAMERA = 0x3080,
+        // ********************************************************************
+
+        // Mesh chunks
+        CHUNK_OBJBLOCK = 0x4000,
+        CHUNK_TRIMESH = 0x4100,
+        CHUNK_VERTLIST = 0x4110,
+        CHUNK_VERTFLAGS = 0x4111,
+        CHUNK_FACELIST = 0x4120,
+        CHUNK_FACEMAT = 0x4130,
+        CHUNK_MAPLIST = 0x4140,
+        CHUNK_SMOOLIST = 0x4150,
+        CHUNK_TRMATRIX = 0x4160,
+        CHUNK_MESHCOLOR = 0x4165,
+        CHUNK_TXTINFO = 0x4170,
+        CHUNK_LIGHT = 0x4600,
+        CHUNK_CAMERA = 0x4700,
+        CHUNK_HIERARCHY = 0x4F00,
+
+        // Specifies the global scaling factor. This is applied
+        // to the root node's transformation matrix
+        CHUNK_MASTER_SCALE = 0x0100,
+
+        // ********************************************************************
+        // Material chunks
+        CHUNK_MAT_MATERIAL = 0xAFFF,
+
+        // asciiz containing the name of the material
+        CHUNK_MAT_MATNAME = 0xA000,
+        CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk
+        CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk
+        CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk
+
+        // Specifies the shininess of the material
+        // followed by percentage chunk
+        CHUNK_MAT_SHININESS = 0xA040,
+        CHUNK_MAT_SHININESS_PERCENT = 0xA041,
+
+        // Specifies the shading mode to be used
+        // followed by a short
+        CHUNK_MAT_SHADING = 0xA100,
+
+        // NOTE: Emissive color (self illumination) seems not
+        // to be a color but a single value, type is unknown.
+        // Make the parser accept both of them.
+        // followed by percentage chunk (?)
+        CHUNK_MAT_SELF_ILLUM = 0xA080,
+
+        // Always followed by percentage chunk  (?)
+        CHUNK_MAT_SELF_ILPCT = 0xA084,
+
+        // Always followed by percentage chunk
+        CHUNK_MAT_TRANSPARENCY = 0xA050,
+
+        // Diffuse texture channel 0
+        CHUNK_MAT_TEXTURE = 0xA200,
+
+        // Contains opacity information for each texel
+        CHUNK_MAT_OPACMAP = 0xA210,
+
+        // Contains a reflection map to be used to reflect
+        // the environment. This is partially supported.
+        CHUNK_MAT_REFLMAP = 0xA220,
+
+        // Self Illumination map (emissive colors)
+        CHUNK_MAT_SELFIMAP = 0xA33d,
+
+        // Bumpmap. Not specified whether it is a heightmap
+        // or a normal map. Assme it is a heightmap since
+        // artist normally prefer this format.
+        CHUNK_MAT_BUMPMAP = 0xA230,
+
+        // Specular map. Seems to influence the specular color
+        CHUNK_MAT_SPECMAP = 0xA204,
+
+        // Holds shininess data.
+        CHUNK_MAT_MAT_SHINMAP = 0xA33C,
+
+        // Scaling in U/V direction.
+        // (need to gen separate UV coordinate set
+        // and do this by hand)
+        CHUNK_MAT_MAP_USCALE = 0xA354,
+        CHUNK_MAT_MAP_VSCALE = 0xA356,
+
+        // Translation in U/V direction.
+        // (need to gen separate UV coordinate set
+        // and do this by hand)
+        CHUNK_MAT_MAP_UOFFSET = 0xA358,
+        CHUNK_MAT_MAP_VOFFSET = 0xA35a,
+
+        // UV-coordinates rotation around the z-axis
+        // Assumed to be in radians.
+        CHUNK_MAT_MAP_ANG = 0xA35C,
+
+        // Tiling flags for 3DS files
+        CHUNK_MAT_MAP_TILING = 0xa351,
+
+        // Specifies the file name of a texture
+        CHUNK_MAPFILE = 0xA300,
+
+        // Specifies whether a material requires two-sided rendering
+        CHUNK_MAT_TWO_SIDE = 0xA081,
+        // ********************************************************************
+
+        // Main keyframer chunk. Contains translation/rotation/scaling data
+        CHUNK_KEYFRAMER = 0xB000,
+
+        // Supported sub chunks
+        CHUNK_TRACKINFO = 0xB002,
+        CHUNK_TRACKOBJNAME = 0xB010,
+        CHUNK_TRACKDUMMYOBJNAME = 0xB011,
+        CHUNK_TRACKPIVOT = 0xB013,
+        CHUNK_TRACKPOS = 0xB020,
+        CHUNK_TRACKROTATE = 0xB021,
+        CHUNK_TRACKSCALE = 0xB022,
+
+        // ********************************************************************
+        // Keyframes for various other stuff in the file
+        // Partially ignored
+        CHUNK_AMBIENTKEY = 0xB001,
+        CHUNK_TRACKMORPH = 0xB026,
+        CHUNK_TRACKHIDE = 0xB029,
+        CHUNK_OBJNUMBER = 0xB030,
+        CHUNK_TRACKCAMERA = 0xB003,
+        CHUNK_TRACKFOV = 0xB023,
+        CHUNK_TRACKROLL = 0xB024,
+        CHUNK_TRACKCAMTGT = 0xB004,
+        CHUNK_TRACKLIGHT = 0xB005,
+        CHUNK_TRACKLIGTGT = 0xB006,
+        CHUNK_TRACKSPOTL = 0xB007,
+        CHUNK_FRAMES = 0xB008,
+        // ********************************************************************
+
+        // light sub-chunks
+        CHUNK_DL_OFF = 0x4620,
+        CHUNK_DL_OUTER_RANGE = 0x465A,
+        CHUNK_DL_INNER_RANGE = 0x4659,
+        CHUNK_DL_MULTIPLIER = 0x465B,
+        CHUNK_DL_EXCLUDE = 0x4654,
+        CHUNK_DL_ATTENUATE = 0x4625,
+        CHUNK_DL_SPOTLIGHT = 0x4610,
+
+        // camera sub-chunks
+        CHUNK_CAM_RANGES = 0x4720
+    };
+}
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds mesh face */
+struct Face : public FaceWithSmoothingGroup {
+};
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4315)
+#endif // _MSC_VER
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a texture */
+struct Texture {
+    //! Default constructor
+    Texture() AI_NO_EXCEPT
+            : mTextureBlend(0.0f),
+              mMapName(),
+              mOffsetU(0.0),
+              mOffsetV(0.0),
+              mScaleU(1.0),
+              mScaleV(1.0),
+              mRotation(0.0),
+              mMapMode(aiTextureMapMode_Wrap),
+              bPrivate(),
+              iUVSrc(0) {
+        mTextureBlend = get_qnan();
+    }
+
+    Texture(const Texture &other) :
+            mTextureBlend(other.mTextureBlend),
+            mMapName(other.mMapName),
+            mOffsetU(other.mOffsetU),
+            mOffsetV(other.mOffsetV),
+            mScaleU(other.mScaleU),
+            mScaleV(other.mScaleV),
+            mRotation(other.mRotation),
+            mMapMode(other.mMapMode),
+            bPrivate(other.bPrivate),
+            iUVSrc(other.iUVSrc) {
+        // empty
+    }
+
+    Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(other.mTextureBlend),
+                                            mMapName(std::move(other.mMapName)),
+                                            mOffsetU(other.mOffsetU),
+                                            mOffsetV(other.mOffsetV),
+                                            mScaleU(other.mScaleU),
+                                            mScaleV(other.mScaleV),
+                                            mRotation(other.mRotation),
+                                            mMapMode(other.mMapMode),
+                                            bPrivate(other.bPrivate),
+                                            iUVSrc(other.iUVSrc) {
+        // empty
+    }
+
+    Texture &operator=(Texture &&other) AI_NO_EXCEPT {
+        if (this == &other) {
+            return *this;
+        }
+
+        mTextureBlend = other.mTextureBlend;
+        mMapName = std::move(other.mMapName);
+        mOffsetU = other.mOffsetU;
+        mOffsetV = other.mOffsetV;
+        mScaleU = other.mScaleU;
+        mScaleV = other.mScaleV;
+        mRotation = other.mRotation;
+        mMapMode = other.mMapMode;
+        bPrivate = other.bPrivate;
+        iUVSrc = other.iUVSrc;
+
+        return *this;
+    }
+
+    //! Specifies the blend factor for the texture
+    ai_real mTextureBlend;
+
+    //! Specifies the filename of the texture
+    std::string mMapName;
+
+    //! Specifies texture coordinate offsets/scaling/rotations
+    ai_real mOffsetU;
+    ai_real mOffsetV;
+    ai_real mScaleU;
+    ai_real mScaleV;
+    ai_real mRotation;
+
+    //! Specifies the mapping mode to be used for the texture
+    aiTextureMapMode mMapMode;
+
+    //! Used internally
+    bool bPrivate;
+    int iUVSrc;
+};
+
+#include <assimp/Compiler/poppack1.h>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds material */
+struct Material {
+    //! Default constructor has been deleted
+    Material() :
+            mName(),
+            mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+            mSpecularExponent(ai_real(0.0)),
+            mShininessStrength(ai_real(1.0)),
+            mShading(Discreet3DS::Gouraud),
+            mTransparency(ai_real(1.0)),
+            mBumpHeight(ai_real(1.0)),
+            mTwoSided(false) {
+        // empty
+    }
+
+    //! Constructor with explicit name
+    explicit Material(const std::string &name) :
+            mName(name),
+            mDiffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+            mSpecularExponent(ai_real(0.0)),
+            mShininessStrength(ai_real(1.0)),
+            mShading(Discreet3DS::Gouraud),
+            mTransparency(ai_real(1.0)),
+            mBumpHeight(ai_real(1.0)),
+            mTwoSided(false) {
+        // empty
+    }
+
+    Material(const Material &other) :
+            mName(other.mName),
+            mDiffuse(other.mDiffuse),
+            mSpecularExponent(other.mSpecularExponent),
+            mShininessStrength(other.mShininessStrength),
+            mSpecular(other.mSpecular),
+            mAmbient(other.mAmbient),
+            mShading(other.mShading),
+            mTransparency(other.mTransparency),
+            sTexDiffuse(other.sTexDiffuse),
+            sTexOpacity(other.sTexOpacity),
+            sTexSpecular(other.sTexSpecular),
+            sTexReflective(other.sTexReflective),
+            sTexBump(other.sTexBump),
+            sTexEmissive(other.sTexEmissive),
+            sTexShininess(other.sTexShininess),
+            mBumpHeight(other.mBumpHeight),
+            mEmissive(other.mEmissive),
+            sTexAmbient(other.sTexAmbient),
+            mTwoSided(other.mTwoSided) {
+        // empty
+    }
+
+    //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
+    Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)),
+                                              mDiffuse(other.mDiffuse),
+                                              mSpecularExponent(other.mSpecularExponent),
+                                              mShininessStrength(other.mShininessStrength),
+                                              mSpecular(other.mSpecular),
+                                              mAmbient(other.mAmbient),
+                                              mShading(other.mShading),
+                                              mTransparency(other.mTransparency),
+                                              sTexDiffuse(std::move(other.sTexDiffuse)),
+                                              sTexOpacity(std::move(other.sTexOpacity)),
+                                              sTexSpecular(std::move(other.sTexSpecular)),
+                                              sTexReflective(std::move(other.sTexReflective)),
+                                              sTexBump(std::move(other.sTexBump)),
+                                              sTexEmissive(std::move(other.sTexEmissive)),
+                                              sTexShininess(std::move(other.sTexShininess)),
+                                              mBumpHeight(other.mBumpHeight),
+                                              mEmissive(other.mEmissive),
+                                              sTexAmbient(std::move(other.sTexAmbient)),
+                                              mTwoSided(other.mTwoSided) {
+        // empty
+    }
+
+    Material &operator=(Material &&other) AI_NO_EXCEPT {
+        if (this == &other) {
+            return *this;
+        }
+
+        mName = std::move(other.mName);
+        mDiffuse = other.mDiffuse;
+        mSpecularExponent = other.mSpecularExponent;
+        mShininessStrength = other.mShininessStrength,
+        mSpecular = other.mSpecular;
+        mAmbient = other.mAmbient;
+        mShading = other.mShading;
+        mTransparency = other.mTransparency;
+        sTexDiffuse = std::move(other.sTexDiffuse);
+        sTexOpacity = std::move(other.sTexOpacity);
+        sTexSpecular = std::move(other.sTexSpecular);
+        sTexReflective = std::move(other.sTexReflective);
+        sTexBump = std::move(other.sTexBump);
+        sTexEmissive = std::move(other.sTexEmissive);
+        sTexShininess = std::move(other.sTexShininess);
+        mBumpHeight = other.mBumpHeight;
+        mEmissive = other.mEmissive;
+        sTexAmbient = std::move(other.sTexAmbient);
+        mTwoSided = other.mTwoSided;
+
+        return *this;
+    }
+
+    virtual ~Material() {
+        // empty
+    }
+
+    //! Name of the material
+    std::string mName;
+    //! Diffuse color of the material
+    aiColor3D mDiffuse;
+    //! Specular exponent
+    ai_real mSpecularExponent;
+    //! Shininess strength, in percent
+    ai_real mShininessStrength;
+    //! Specular color of the material
+    aiColor3D mSpecular;
+    //! Ambient color of the material
+    aiColor3D mAmbient;
+    //! Shading type to be used
+    Discreet3DS::shadetype3ds mShading;
+    //! Opacity of the material
+    ai_real mTransparency;
+    //! Diffuse texture channel
+    Texture sTexDiffuse;
+    //! Opacity texture channel
+    Texture sTexOpacity;
+    //! Specular texture channel
+    Texture sTexSpecular;
+    //! Reflective texture channel
+    Texture sTexReflective;
+    //! Bump texture channel
+    Texture sTexBump;
+    //! Emissive texture channel
+    Texture sTexEmissive;
+    //! Shininess texture channel
+    Texture sTexShininess;
+    //! Scaling factor for the bump values
+    ai_real mBumpHeight;
+    //! Emissive color
+    aiColor3D mEmissive;
+    //! Ambient texture channel
+    //! (used by the ASE format)
+    Texture sTexAmbient;
+    //! True if the material must be rendered from two sides
+    bool mTwoSided;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file mesh */
+struct Mesh : public MeshWithSmoothingGroups<D3DS::Face> {
+    //! Default constructor has been deleted
+    Mesh() = delete;
+
+    //! Constructor with explicit name
+    explicit Mesh(const std::string &name) :
+            mName(name) {
+    }
+
+    //! Name of the mesh
+    std::string mName;
+
+    //! Texture coordinates
+    std::vector<aiVector3D> mTexCoords;
+
+    //! Face materials
+    std::vector<unsigned int> mFaceMaterials;
+
+    //! Local transformation matrix
+    aiMatrix4x4 mMat;
+};
+
+// ---------------------------------------------------------------------------
+/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the
+    C-API, so it would be difficult to make them a template. */
+struct aiFloatKey {
+    double mTime; ///< The time of this key
+    ai_real mValue; ///< The value of this key
+
+#ifdef __cplusplus
+
+    // time is not compared
+    bool operator==(const aiFloatKey &o) const { return o.mValue == this->mValue; }
+
+    bool operator!=(const aiFloatKey &o) const { return o.mValue != this->mValue; }
+
+    // Only time is compared. This operator is defined
+    // for use with std::sort
+    bool operator<(const aiFloatKey &o) const { return mTime < o.mTime; }
+
+    bool operator>(const aiFloatKey &o) const { return mTime > o.mTime; }
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file node */
+struct Node {
+    Node() = delete;
+
+    explicit Node(const std::string &name) :
+            mParent(nullptr),
+            mName(name),
+            mInstanceNumber(0),
+            mHierarchyPos(0),
+            mHierarchyIndex(0),
+            mInstanceCount(1) {
+        aRotationKeys.reserve(20);
+        aPositionKeys.reserve(20);
+        aScalingKeys.reserve(20);
+    }
+
+    ~Node() {
+        for (unsigned int i = 0; i < mChildren.size(); ++i)
+            delete mChildren[i];
+    }
+
+    //! Pointer to the parent node
+    Node *mParent;
+
+    //! Holds all child nodes
+    std::vector<Node *> mChildren;
+
+    //! Name of the node
+    std::string mName;
+
+    //! InstanceNumber of the node
+    int32_t mInstanceNumber;
+
+    //! Dummy nodes: real name to be combined with the $$$DUMMY
+    std::string mDummyName;
+
+    //! Position of the node in the hierarchy (tree depth)
+    int16_t mHierarchyPos;
+
+    //! Index of the node
+    int16_t mHierarchyIndex;
+
+    //! Rotation keys loaded from the file
+    std::vector<aiQuatKey> aRotationKeys;
+
+    //! Position keys loaded from the file
+    std::vector<aiVectorKey> aPositionKeys;
+
+    //! Scaling keys loaded from the file
+    std::vector<aiVectorKey> aScalingKeys;
+
+    // For target lights (spot lights and directional lights):
+    // The position of the target
+    std::vector<aiVectorKey> aTargetPositionKeys;
+
+    // For cameras: the camera roll angle
+    std::vector<aiFloatKey> aCameraRollKeys;
+
+    //! Pivot position loaded from the file
+    aiVector3D vPivot;
+
+    //instance count, will be kept only for the first node
+    int32_t mInstanceCount;
+
+    //! Add a child node, setup the right parent node for it
+    //! \param pc Node to be 'adopted'
+    inline Node &push_back(Node *pc) {
+        mChildren.push_back(pc);
+        pc->mParent = this;
+        return *this;
+    }
+};
+// ---------------------------------------------------------------------------
+/** Helper structure analogue to aiScene */
+struct Scene {
+    //! List of all materials loaded
+    //! NOTE: 3ds references materials globally
+    std::vector<Material> mMaterials;
+
+    //! List of all meshes loaded
+    std::vector<Mesh> mMeshes;
+
+    //! List of all cameras loaded
+    std::vector<aiCamera *> mCameras;
+
+    //! List of all lights loaded
+    std::vector<aiLight *> mLights;
+
+    //! Pointer to the root node of the scene
+    // --- moved to main class
+    // Node* pcRootNode;
+};
+
+} // end of namespace D3DS
+} // end of namespace Assimp
+
+#endif // AI_XFILEHELPER_H_INC

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 270 - 336
Engine/lib/assimp/code/AssetLib/3DS/3DSLoader.cpp


+ 16 - 11
Engine/lib/assimp/code/3DS/3DSLoader.h → Engine/lib/assimp/code/AssetLib/3DS/3DSLoader.h

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -46,11 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #ifndef AI_3DSIMPORTER_H_INC
 #define AI_3DSIMPORTER_H_INC
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 #include <assimp/BaseImporter.h>
 #include <assimp/types.h>
 
-#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 #include "3DSHelper.h"
 #include <assimp/StreamReader.h>
@@ -65,28 +65,24 @@ using namespace D3DS;
 // ---------------------------------------------------------------------------------
 /** Importer class for 3D Studio r3 and r4 3DS files
  */
-class Discreet3DSImporter : public BaseImporter
-{
+class Discreet3DSImporter : public BaseImporter {
 public:
-
     Discreet3DSImporter();
     ~Discreet3DSImporter();
 
-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 checkSig) const override;
 
     // -------------------------------------------------------------------
     /** 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) override;
 
 protected:
 
@@ -94,14 +90,14 @@ protected:
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
      */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      */
     void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
+        IOSystem* pIOHandler) override;
 
     // -------------------------------------------------------------------
     /** Converts a temporary material to the outer representation
@@ -212,6 +208,15 @@ protected:
     */
     void ReplaceDefaultMaterial();
 
+    bool ContainsTextures(unsigned int i) const {
+        return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexBump.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexOpacity.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexEmissive.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexSpecular.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexShininess.mMapName.empty() ;
+    }
+
     // -------------------------------------------------------------------
     /** Convert the whole scene
     */

+ 165 - 0
Engine/lib/assimp/code/AssetLib/3MF/3MFTypes.h

@@ -0,0 +1,165 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <assimp/vector3.h>
+#include <assimp/matrix4x4.h>
+#include <assimp/ParsingUtils.h>
+#include <vector>
+#include <string>
+
+struct aiMaterial;
+struct aiMesh;
+
+namespace Assimp {
+namespace D3MF {
+
+enum class ResourceType {
+    RT_Object,
+    RT_BaseMaterials,
+    RT_EmbeddedTexture2D,
+    RT_Texture2DGroup,
+    RT_Unknown
+}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
+
+class Resource {
+public:
+    int mId;
+
+    Resource(int id) :
+            mId(id) {
+        // empty
+    }
+
+    virtual ~Resource() {
+        // empty
+    }
+
+    virtual ResourceType getType() const {
+        return ResourceType::RT_Unknown;
+    }
+};
+
+class EmbeddedTexture : public Resource {
+public:
+    std::string mPath;
+    std::string mContentType;
+    std::string mTilestyleU;
+    std::string mTilestyleV;
+    std::vector<char> mBuffer;
+
+    EmbeddedTexture(int id) :
+            Resource(id),
+            mPath(),
+            mContentType(),
+            mTilestyleU(),
+            mTilestyleV() {
+        // empty
+    }
+
+    ~EmbeddedTexture() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_EmbeddedTexture2D;
+    }
+};
+
+class Texture2DGroup : public Resource {
+public:
+    std::vector<aiVector2D> mTex2dCoords;
+    int mTexId;
+    Texture2DGroup(int id) :
+            Resource(id),
+            mTexId(-1) {
+        // empty
+    }
+
+    ~Texture2DGroup() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_Texture2DGroup;
+    }
+};
+
+class BaseMaterials : public Resource {
+public:
+    std::vector<unsigned int> mMaterialIndex;
+
+    BaseMaterials(int id) :
+            Resource(id),
+            mMaterialIndex() {
+        // empty
+    }
+
+    ~BaseMaterials() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_BaseMaterials;
+    }
+};
+
+struct Component {
+    int mObjectId;
+    aiMatrix4x4 mTransformation;
+};
+
+class Object : public Resource {
+public:
+    std::vector<aiMesh *> mMeshes;
+    std::vector<unsigned int> mMeshIndex;
+    std::vector<Component> mComponents;
+    std::string mName;
+
+    Object(int id) :
+            Resource(id),
+            mName(std::string("Object_") + ai_to_string(id)) {
+        // empty
+    }
+
+    ~Object() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_Object;
+    }
+};
+
+} // namespace D3MF
+} // namespace Assimp

+ 117 - 0
Engine/lib/assimp/code/AssetLib/3MF/3MFXmlTags.h

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

+ 145 - 144
Engine/lib/assimp/code/3MF/D3MFExporter.cpp → Engine/lib/assimp/code/AssetLib/3MF/D3MFExporter.cpp

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -44,81 +43,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 +118,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 +137,7 @@ bool D3MFExporter::exportContentTypes() {
     mContentOutput << std::endl;
     mContentOutput << "</Types>";
     mContentOutput << std::endl;
-    exportContentTyp( XmlTag::CONTENT_TYPES_ARCHIVE );
+    zipContentType(XmlTag::CONTENT_TYPES_ARCHIVE);
 
     return true;
 }
@@ -157,20 +149,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" );
+    zipRelInfo("_rels", ".rels");
     mRelOutput.flush();
 
     return true;
@@ -181,8 +173,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 +184,6 @@ bool D3MFExporter::export3DModel() {
 
     writeObjects();
 
-
     mModelOutput << "</" << XmlTag::resources << ">";
     mModelOutput << std::endl;
     writeBuild();
@@ -203,36 +194,36 @@ 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" );
+    zipModel("3D", "3DModel.model");
     mModelOutput.flush();
 
     return true;
 }
 
 void D3MFExporter::writeHeader() {
-    mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF - 8\"?>";
+    mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
     mModelOutput << std::endl;
 }
 
 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,160 +232,170 @@ 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_" + ai_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();
-            hexDiffuseColor = "#";
-            
-            tmp = DecimalToHexa( color.r );
-            hexDiffuseColor += tmp;
-            tmp = DecimalToHexa( color.g );
-            hexDiffuseColor += tmp;
-            tmp = DecimalToHexa( color.b );
-            hexDiffuseColor += tmp;
-            tmp = DecimalToHexa( color.a );
-            hexDiffuseColor += tmp;
+            // rgbs %
+            if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) {
+
+                hexDiffuseColor = ai_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 = ai_decimal_to_hexa((ai_real)color.r);
+                hexDiffuseColor += tmp;
+                tmp = ai_decimal_to_hexa((ai_real)color.g);
+                hexDiffuseColor += tmp;
+                tmp = ai_decimal_to_hexa((ai_real)color.b);
+                hexDiffuseColor += tmp;
+                tmp = ai_decimal_to_hexa((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=\"" << currentNode->mName.C_Str() << "\" type=\"model\">";
+        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 ] );
+    mModelOutput << "<"
+                 << XmlTag::mesh
+                 << ">" << "\n";
+    mModelOutput << "<"
+                 << XmlTag::vertices
+                 << ">" << "\n";
+    for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+        writeVertex(mesh->mVertices[i]);
     }
-    mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
+    mModelOutput << "</"
+                 << XmlTag::vertices << ">"
+                 << "\n";
 
-    const unsigned int matIdx( mesh->mMaterialIndex );
+    const unsigned int matIdx(mesh->mMaterialIndex);
 
-    writeFaces( mesh, matIdx );
+    writeFaces(mesh, matIdx);
 
-    mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
+    mModelOutput << "</"
+                 << XmlTag::mesh << ">"
+                 << "\n";
 }
 
-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)+"\" />";
-        mModelOutput << std::endl;
+    mModelOutput << "<"
+                 << XmlTag::triangles << ">"
+                 << "\n";
+    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=\"" + ai_to_string(matIdx) + "\" />";
+        mModelOutput << "\n";
     }
-    mModelOutput << "</" << XmlTag::triangles << ">";
-    mModelOutput << std::endl;
+    mModelOutput << "</"
+                 << XmlTag::triangles
+                 << ">";
+    mModelOutput << "\n";
 }
 
 void D3MFExporter::writeBuild() {
-    mModelOutput << "<" << XmlTag::build << ">" << std::endl;
-
-    for ( size_t i = 0; i < mBuildItems.size(); ++i ) {
-        mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 1 << "\"/>";
-        mModelOutput << std::endl;
+    mModelOutput << "<"
+                 << XmlTag::build
+                 << ">"
+                 << "\n";
+
+    for (size_t i = 0; i < mBuildItems.size(); ++i) {
+        mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 2 << "\"/>";
+        mModelOutput << "\n";
     }
     mModelOutput << "</" << XmlTag::build << ">";
-    mModelOutput << std::endl;
+    mModelOutput << "\n";
 }
 
-void D3MFExporter::exportContentTyp( const std::string &filename ) {
-    if ( nullptr == m_zipArchive ) {
-        throw DeadlyExportError( "3MF-Export: Zip archive not valid, nullptr." );
-    }
-    const std::string entry = filename;
-    zip_entry_open( m_zipArchive, entry.c_str() );
-
-    const std::string &exportTxt( mContentOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
-
-    zip_entry_close( m_zipArchive );
+void D3MFExporter::zipContentType(const std::string &filename) {
+    addFileInZip(filename, mContentOutput.str());
 }
 
-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::zipModel(const std::string &folder, const std::string &modelName) {
     const std::string entry = folder + "/" + modelName;
-    zip_entry_open( m_zipArchive, entry.c_str() );
-
-    const std::string &exportTxt( mModelOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
-
-    zip_entry_close( m_zipArchive );
+    addFileInZip(entry, mModelOutput.str());
 }
 
-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::zipRelInfo(const std::string &folder, const std::string &relName) {
     const std::string entry = folder + "/" + relName;
-    zip_entry_open( m_zipArchive, entry.c_str() );
+    addFileInZip(entry, mRelOutput.str());
+}
 
-    const std::string &exportTxt( mRelOutput.str() );
-    zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
+void D3MFExporter::addFileInZip(const std::string& entry, const std::string& content) {
+    if (nullptr == m_zipArchive) {
+        throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
+    }
 
-    zip_entry_close( m_zipArchive );
+    zip_entry_open(m_zipArchive, entry.c_str());
+    zip_entry_write(m_zipArchive, content.c_str(), content.size());
+    zip_entry_close(m_zipArchive);
 }
 
-
 } // Namespace D3MF
 } // Namespace Assimp
 

+ 14 - 9
Engine/lib/assimp/code/3MF/D3MFExporter.h → Engine/lib/assimp/code/AssetLib/3MF/D3MFExporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -41,6 +40,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 #pragma once
 
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
+
 #include <memory>
 #include <sstream>
 #include <vector>
@@ -59,8 +61,6 @@ class IOStream;
 
 namespace D3MF {
 
-#ifndef ASSIMP_BUILD_NO_EXPORT
-#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
 
 struct OpcPackageRelationship;
 
@@ -83,9 +83,12 @@ protected:
     void writeVertex( const aiVector3D &pos );
     void writeFaces( aiMesh *mesh, unsigned int matIdx );
     void writeBuild();
-    void exportContentTyp( const std::string &filename );
-    void writeModelToArchive( const std::string &folder, const std::string &modelName );
-    void writeRelInfoToFile( const std::string &folder, const std::string &relName );
+
+    // Zip the data
+    void zipContentType( const std::string &filename );
+    void zipModel( const std::string &folder, const std::string &modelName );
+    void zipRelInfo( const std::string &folder, const std::string &relName );
+    void addFileInZip( const std::string &entry, const std::string &content );
 
 private:
     std::string mArchiveName;
@@ -98,9 +101,11 @@ private:
     std::vector<OpcPackageRelationship*> mRelations;
 };
 
-#endif // ASSIMP_BUILD_NO_3MF_EXPORTER
-#endif // ASSIMP_BUILD_NO_EXPORT
 
 } // Namespace D3MF
 } // Namespace Assimp
 
+#endif // ASSIMP_BUILD_NO_3MF_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
+
+

+ 130 - 0
Engine/lib/assimp/code/AssetLib/3MF/D3MFImporter.cpp

@@ -0,0 +1,130 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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_3MF_IMPORTER
+
+#include "D3MFImporter.h"
+#include "3MFXmlTags.h"
+#include "D3MFOpcPackage.h"
+#include "XmlSerializer.h"
+
+#include <assimp/StringComparison.h>
+#include <assimp/StringUtils.h>
+#include <assimp/XmlParser.h>
+#include <assimp/ZipArchiveIOSystem.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/fast_atof.h>
+
+#include <cassert>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+#include <iomanip>
+#include <cstring>
+
+namespace Assimp {
+
+using namespace D3MF;
+
+static const aiImporterDesc desc = {
+    "3mf Importer",
+    "",
+    "",
+    "http://3mf.io/",
+    aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour,
+    0,
+    0,
+    0,
+    0,
+    "3mf"
+};
+
+D3MFImporter::D3MFImporter() :
+        BaseImporter() {
+    // empty
+}
+
+D3MFImporter::~D3MFImporter() {
+    // empty
+}
+
+bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
+        return false;
+    }
+    D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
+    return opcPackage.validate();
+}
+
+void D3MFImporter::SetupProperties(const Importer*) {
+    // empty
+}
+
+const aiImporterDesc *D3MFImporter::GetInfo() const {
+    return &desc;
+}
+
+void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, IOSystem *pIOHandler) {
+    D3MFOpcPackage opcPackage(pIOHandler, filename);
+
+    XmlParser xmlParser;
+    if (xmlParser.parse(opcPackage.RootStream())) {
+        XmlSerializer xmlSerializer(&xmlParser);
+        xmlSerializer.ImportXml(pScene);
+
+        const std::vector<aiTexture*> &tex =  opcPackage.GetEmbeddedTextures();
+        if (!tex.empty()) {
+            pScene->mNumTextures = static_cast<unsigned int>(tex.size());
+            pScene->mTextures = new aiTexture *[pScene->mNumTextures];
+            for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {
+                pScene->mTextures[i] = tex[i];
+            }
+        }
+    }
+}
+
+} // Namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_3MF_IMPORTER

+ 91 - 0
Engine/lib/assimp/code/AssetLib/3MF/D3MFImporter.h

@@ -0,0 +1,91 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef AI_D3MFLOADER_H_INCLUDED
+#define AI_D3MFLOADER_H_INCLUDED
+
+#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
+
+#include <assimp/BaseImporter.h>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/// @brief  The 3MF-importer class.
+///
+/// Implements the basic topology import and embedded textures.
+// ---------------------------------------------------------------------------
+class D3MFImporter : public BaseImporter {
+public:
+    /// @brief The default class constructor.
+    D3MFImporter();
+
+    ///	@brief  The class destructor.
+    ~D3MFImporter() override;
+
+    /// @brief Performs the data format detection.
+    /// @param pFile        The filename to check.
+    /// @param pIOHandler   The used IO-System.
+    /// @param checkSig     true for signature checking.
+    /// @return true for can be loaded, false for not.
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
+
+    /// @brief  Not used
+    /// @param pImp Not used
+    void SetupProperties(const Importer *pImp) override;
+
+    /// @brief The importer description getter.
+    /// @return The info
+    const aiImporterDesc *GetInfo() const override;
+
+protected:
+    /// @brief Internal read function, performs the file parsing.
+    /// @param pFile        The filename
+    /// @param pScene       The scene to load in.
+    /// @param pIOHandler   The io-system
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+};
+
+} // Namespace Assimp
+
+#endif // #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
+
+#endif // AI_D3MFLOADER_H_INCLUDED

+ 256 - 0
Engine/lib/assimp/code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -0,0 +1,256 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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_3MF_IMPORTER
+
+#include "D3MFOpcPackage.h"
+#include <assimp/Exceptional.h>
+#include <assimp/XmlParser.h>
+#include <assimp/ZipArchiveIOSystem.h>
+#include <assimp/ai_assert.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/texture.h>
+#include "3MFXmlTags.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <map>
+#include <vector>
+
+namespace Assimp {
+
+namespace D3MF {
+// ------------------------------------------------------------------------------------------------
+
+using OpcPackageRelationshipPtr = std::shared_ptr<OpcPackageRelationship>;
+
+class OpcPackageRelationshipReader {
+public:
+    OpcPackageRelationshipReader(XmlParser &parser) :
+            m_relationShips() {
+        XmlNode root = parser.getRootNode();
+        ParseRootNode(root);
+    }
+
+    void ParseRootNode(XmlNode &node) {
+        ParseAttributes(node);
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            std::string name = currentNode.name();
+            if (name == "Relationships") {
+                ParseRelationsNode(currentNode);
+            }
+        }
+    }
+
+    void ParseAttributes(XmlNode & /*node*/) {
+        // empty
+    }
+
+    bool validateRels(OpcPackageRelationshipPtr &relPtr) {
+        if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    void ParseRelationsNode(XmlNode &node) {
+        if (node.empty()) {
+            return;
+        }
+
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            const std::string name = currentNode.name();
+            if (name == "Relationship") {
+                OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
+                relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string();
+                relPtr->type = currentNode.attribute(XmlTag::RELS_ATTRIB_TYPE).as_string();
+                relPtr->target = currentNode.attribute(XmlTag::RELS_ATTRIB_TARGET).as_string();
+                if (validateRels(relPtr)) {
+                    m_relationShips.push_back(relPtr);
+                }
+            }
+        }
+    }
+
+    std::vector<OpcPackageRelationshipPtr> m_relationShips;
+};
+
+static bool IsEmbeddedTexture( const std::string &filename ) {
+    const std::string extension = BaseImporter::GetExtension(filename);
+    if (extension == "jpg" || extension == "png") {
+        std::string::size_type pos = filename.find("thumbnail");
+        if (pos == std::string::npos) {
+            return false;
+        }
+        return true;
+    }
+
+    return false;
+}
+// ------------------------------------------------------------------------------------------------
+D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
+        mRootStream(nullptr),
+        mZipArchive() {
+    mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile);
+    if (!mZipArchive->isOpen()) {
+        throw DeadlyImportError("Failed to open file ", rFile, ".");
+    }
+
+    std::vector<std::string> fileList;
+    mZipArchive->getFileList(fileList);
+
+    for (auto &file : fileList) {
+        if (file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) {
+            if (!mZipArchive->Exists(file.c_str())) {
+                continue;
+            }
+
+            IOStream *fileStream = mZipArchive->Open(file.c_str());
+            if (nullptr == fileStream) {
+                ASSIMP_LOG_ERROR("Filestream is nullptr.");
+                continue;
+            }
+
+            std::string rootFile = ReadPackageRootRelationship(fileStream);
+            if (!rootFile.empty() && rootFile[0] == '/') {
+                rootFile = rootFile.substr(1);
+                if (rootFile[0] == '/') {
+                    // deal with zip-bug
+                    rootFile = rootFile.substr(1);
+                }
+            } 
+
+            ASSIMP_LOG_VERBOSE_DEBUG(rootFile);
+
+            mZipArchive->Close(fileStream);
+
+            mRootStream = mZipArchive->Open(rootFile.c_str());
+            ai_assert(mRootStream != nullptr);
+            if (nullptr == mRootStream) {
+                throw DeadlyImportError("Cannot open root-file in archive : " + rootFile);
+            }
+        } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
+            ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file);
+        } else if (IsEmbeddedTexture(file)) {
+            IOStream *fileStream = mZipArchive->Open(file.c_str());
+            LoadEmbeddedTextures(fileStream, file);
+            mZipArchive->Close(fileStream);
+        } else {
+            ASSIMP_LOG_WARN("Ignored file of unknown type: ", file);
+        }
+    }
+}
+
+D3MFOpcPackage::~D3MFOpcPackage() {
+    mZipArchive->Close(mRootStream);
+    delete mZipArchive;
+}
+
+IOStream *D3MFOpcPackage::RootStream() const {
+    return mRootStream;
+}
+
+const std::vector<aiTexture *> &D3MFOpcPackage::GetEmbeddedTextures() const {
+    return mEmbeddedTextures;
+}
+
+static const char *const ModelRef = "3D/3dmodel.model";
+
+bool D3MFOpcPackage::validate() {
+    if (nullptr == mRootStream || nullptr == mZipArchive) {
+        return false;
+    }
+
+    return mZipArchive->Exists(ModelRef);
+}
+
+std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
+    XmlParser xmlParser;
+    if (!xmlParser.parse(stream)) {
+        return std::string();
+    }
+
+    OpcPackageRelationshipReader reader(xmlParser);
+
+    auto itr = std::find_if(reader.m_relationShips.begin(), reader.m_relationShips.end(), [](const OpcPackageRelationshipPtr &rel) {
+        return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
+    });
+
+    if (itr == reader.m_relationShips.end()) {
+        throw DeadlyImportError("Cannot find ", XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
+    }
+
+    return (*itr)->target;
+}
+
+void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) {
+    if (nullptr == fileStream) {
+        return;
+    }
+
+    const size_t size = fileStream->FileSize();
+    if (0 == size) {
+        return;
+    }
+
+    unsigned char *data = new unsigned char[size];
+    fileStream->Read(data, 1, size);
+    aiTexture *texture = new aiTexture;
+    std::string embName = "*" + filename;
+    texture->mFilename.Set(embName.c_str());
+    texture->mWidth = static_cast<unsigned int>(size);
+    texture->mHeight = 0;
+    texture->achFormatHint[0] = 'p';
+    texture->achFormatHint[1] = 'n';
+    texture->achFormatHint[2] = 'g';
+    texture->achFormatHint[3] = '\0';
+    texture->pcData = (aiTexel*) data;
+    mEmbeddedTextures.emplace_back(texture);
+}
+
+} // Namespace D3MF
+} // Namespace Assimp
+
+#endif //ASSIMP_BUILD_NO_3MF_IMPORTER

+ 13 - 12
Engine/lib/assimp/code/3MF/D3MFOpcPackage.h → Engine/lib/assimp/code/AssetLib/3MF/D3MFOpcPackage.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,18 +42,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef D3MFOPCPACKAGE_H
 #define D3MFOPCPACKAGE_H
 
+#include <assimp/IOSystem.hpp>
 #include <memory>
+#include <string>
 
-#include <assimp/IOSystem.hpp>
-#include <assimp/irrXMLWrapper.h>
+struct aiTexture;
 
 namespace Assimp {
-    class ZipArchiveIOSystem;
 
-namespace D3MF {
+class ZipArchiveIOSystem;
 
-using XmlReader = irr::io::IrrXMLReader ;
-using XmlReaderPtr = std::shared_ptr<XmlReader> ;
+namespace D3MF {
 
 struct OpcPackageRelationship {
     std::string id;
@@ -64,20 +62,23 @@ struct OpcPackageRelationship {
 
 class D3MFOpcPackage {
 public:
-    D3MFOpcPackage( IOSystem* pIOHandler, const std::string& rFile );
+    D3MFOpcPackage( IOSystem* pIOHandler, const std::string& file );
     ~D3MFOpcPackage();
     IOStream* RootStream() const;
     bool validate();
+    const std::vector<aiTexture*> &GetEmbeddedTextures() const;
 
 protected:
     std::string ReadPackageRootRelationship(IOStream* stream);
+    void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename);
 
 private:
     IOStream* mRootStream;
-    std::unique_ptr<ZipArchiveIOSystem> mZipArchive;
+    ZipArchiveIOSystem *mZipArchive;
+    std::vector<aiTexture *> mEmbeddedTextures;
 };
 
-} // Namespace D3MF
-} // Namespace Assimp
+} // namespace D3MF
+} // namespace Assimp
 
 #endif // D3MFOPCPACKAGE_H

+ 593 - 0
Engine/lib/assimp/code/AssetLib/3MF/XmlSerializer.cpp

@@ -0,0 +1,593 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "XmlSerializer.h"
+#include "D3MFOpcPackage.h"
+#include "3MFXmlTags.h"
+#include "3MFTypes.h"
+#include <assimp/scene.h>
+
+namespace Assimp {
+namespace D3MF {
+
+static const int IdNotSet = -1;
+
+namespace {
+
+static const size_t ColRGBA_Len = 9;
+static const size_t ColRGB_Len = 7;
+
+// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
+bool validateColorString(const char *color) {
+    const size_t len = strlen(color);
+    if (ColRGBA_Len != len && ColRGB_Len != len) {
+        return false;
+    }
+
+    return true;
+}
+
+aiFace ReadTriangle(XmlNode &node) {
+    aiFace face;
+
+    face.mNumIndices = 3;
+    face.mIndices = new unsigned int[face.mNumIndices];
+    face.mIndices[0] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v1).as_string()));
+    face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
+    face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
+
+    return face;
+}
+
+aiVector3D ReadVertex(XmlNode &node) {
+    aiVector3D vertex;
+    vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr);
+    vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr);
+    vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr);
+
+    return vertex;
+}
+
+bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) {
+    pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str());
+    if (!objectAttribute.empty()) {
+        value = objectAttribute.as_string();
+        return true;
+    }
+
+    return false;
+}
+
+bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) {
+    std::string strValue;
+    const bool ret = getNodeAttribute(node, attribute, strValue);
+    if (ret) {
+        value = std::atoi(strValue.c_str());
+        return true;
+    }
+
+    return false;
+}
+
+aiMatrix4x4 parseTransformMatrix(std::string matrixStr) {
+    // split the string
+    std::vector<float> numbers;
+    std::string currentNumber;
+    for (char c : matrixStr) {
+        if (c == ' ') {
+            if (!currentNumber.empty()) {
+                float f = std::stof(currentNumber);
+                numbers.push_back(f);
+                currentNumber.clear();
+            }
+        } else {
+            currentNumber.push_back(c);
+        }
+    }
+    if (!currentNumber.empty()) {
+        const float f = std::stof(currentNumber);
+        numbers.push_back(f);
+    }
+
+    aiMatrix4x4 transformMatrix;
+    transformMatrix.a1 = numbers[0];
+    transformMatrix.b1 = numbers[1];
+    transformMatrix.c1 = numbers[2];
+    transformMatrix.d1 = 0;
+
+    transformMatrix.a2 = numbers[3];
+    transformMatrix.b2 = numbers[4];
+    transformMatrix.c2 = numbers[5];
+    transformMatrix.d2 = 0;
+
+    transformMatrix.a3 = numbers[6];
+    transformMatrix.b3 = numbers[7];
+    transformMatrix.c3 = numbers[8];
+    transformMatrix.d3 = 0;
+
+    transformMatrix.a4 = numbers[9];
+    transformMatrix.b4 = numbers[10];
+    transformMatrix.c4 = numbers[11];
+    transformMatrix.d4 = 1;
+
+    return transformMatrix;
+}
+
+bool parseColor(const char *color, aiColor4D &diffuse) {
+    if (nullptr == color) {
+        return false;
+    }
+
+    if (!validateColorString(color)) {
+        return false;
+    }
+
+    if ('#' != color[0]) {
+        return false;
+    }
+
+    char r[3] = { color[1], color[2], '\0' };
+    diffuse.r = static_cast<ai_real>(strtol(r, nullptr, 16)) / ai_real(255.0);
+
+    char g[3] = { color[3], color[4], '\0' };
+    diffuse.g = static_cast<ai_real>(strtol(g, nullptr, 16)) / ai_real(255.0);
+
+    char b[3] = { color[5], color[6], '\0' };
+    diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
+    const size_t len = strlen(color);
+    if (ColRGB_Len == len) {
+        return true;
+    }
+
+    char a[3] = { color[7], color[8], '\0' };
+    diffuse.a = static_cast<ai_real>(strtol(a, nullptr, 16)) / ai_real(255.0);
+
+    return true;
+}
+
+void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
+    const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string();
+    aiColor4D diffuse;
+    if (parseColor(color, diffuse)) {
+        mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+    }
+}
+
+} // namespace
+
+XmlSerializer::XmlSerializer(XmlParser *xmlParser) :
+        mResourcesDictionnary(),
+        mMeshCount(0),
+        mXmlParser(xmlParser) {
+    ai_assert(nullptr != xmlParser);
+}
+
+XmlSerializer::~XmlSerializer() {
+    for (auto &it : mResourcesDictionnary) {
+        delete it.second;
+    }
+}
+
+void XmlSerializer::ImportXml(aiScene *scene) {
+    if (nullptr == scene) {
+        return;
+    }
+    
+    scene->mRootNode = new aiNode(XmlTag::RootTag);
+    XmlNode node = mXmlParser->getRootNode().child(XmlTag::model);
+    if (node.empty()) {
+        return;
+    }
+
+    XmlNode resNode = node.child(XmlTag::resources);
+    for (auto &currentNode : resNode.children()) {
+        const std::string currentNodeName = currentNode.name();
+        if (currentNodeName == XmlTag::texture_2d) {
+            ReadEmbeddecTexture(currentNode);
+        } else if (currentNodeName == XmlTag::texture_group) {
+            ReadTextureGroup(currentNode);
+        } else if (currentNodeName == XmlTag::object) {
+            ReadObject(currentNode);
+        } else if (currentNodeName == XmlTag::basematerials) {
+            ReadBaseMaterials(currentNode);
+        } else if (currentNodeName == XmlTag::meta) {
+            ReadMetadata(currentNode);
+        }
+    }
+    StoreMaterialsInScene(scene);
+    XmlNode buildNode = node.child(XmlTag::build);
+    if (buildNode.empty()) {
+        return;
+    }
+
+    for (auto &currentNode : buildNode.children()) {
+        const std::string currentNodeName = currentNode.name();
+        if (currentNodeName == XmlTag::item) {
+            int objectId = IdNotSet;
+            std::string transformationMatrixStr;
+            aiMatrix4x4 transformationMatrix;
+            getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId);
+            bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr);
+
+            auto it = mResourcesDictionnary.find(objectId);
+            if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
+                Object *obj = static_cast<Object *>(it->second);
+                if (hasTransform) {
+                    transformationMatrix = parseTransformMatrix(transformationMatrixStr);
+                }
+
+                addObjectToNode(scene->mRootNode, obj, transformationMatrix);
+            }
+        }
+    }
+
+    // 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);
+        }
+    }
+
+    // import the meshes, materials are already stored
+    scene->mNumMeshes = static_cast<unsigned int>(mMeshCount);
+    if (scene->mNumMeshes != 0) {
+        scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
+        for (auto &it : mResourcesDictionnary) {
+            if (it.second->getType() == ResourceType::RT_Object) {
+                Object *obj = static_cast<Object *>(it.second);
+                ai_assert(nullptr != obj);
+                for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) {
+                    scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i];
+                }
+            }
+        }
+    }
+}
+
+void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) {
+    ai_assert(nullptr != obj);
+
+    aiNode *sceneNode = new aiNode(obj->mName);
+    sceneNode->mNumMeshes = static_cast<unsigned int>(obj->mMeshes.size());
+    sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes];
+    std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes);
+
+    sceneNode->mTransformation = nodeTransform;
+    if (nullptr != parent) {
+        parent->addChildren(1, &sceneNode);
+    }
+
+    for (Assimp::D3MF::Component c : obj->mComponents) {
+        auto it = mResourcesDictionnary.find(c.mObjectId);
+        if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
+            addObjectToNode(sceneNode, static_cast<Object *>(it->second), c.mTransformation);
+        }
+    }
+}
+
+void XmlSerializer::ReadObject(XmlNode &node) {
+    int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet;
+    bool hasId = getNodeAttribute(node, XmlTag::id, id);
+    if (!hasId) {
+        return;
+    }
+
+    bool hasPid = getNodeAttribute(node, XmlTag::pid, pid);
+    bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex);
+
+    Object *obj = new Object(id);
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == D3MF::XmlTag::mesh) {
+            auto mesh = ReadMesh(currentNode);
+            mesh->mName.Set(ai_to_string(id));
+
+            if (hasPid) {
+                auto it = mResourcesDictionnary.find(pid);
+                if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) {
+                    BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
+                    mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
+                }
+            }
+
+            obj->mMeshes.push_back(mesh);
+            obj->mMeshIndex.push_back(mMeshCount);
+            mMeshCount++;
+        } else if (currentName == D3MF::XmlTag::components) {
+            for (XmlNode &currentSubNode : currentNode.children()) {
+                const std::string subNodeName = currentSubNode.name();
+                if (subNodeName == D3MF::XmlTag::component) {
+                    int objectId = IdNotSet;
+                    std::string componentTransformStr;
+                    aiMatrix4x4 componentTransform;
+                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) {
+                        componentTransform = parseTransformMatrix(componentTransformStr);
+                    }
+
+                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) {
+                        obj->mComponents.push_back({ objectId, componentTransform });
+                    }
+                }
+            }
+        }
+    }
+
+    mResourcesDictionnary.insert(std::make_pair(id, obj));
+}
+
+aiMesh *XmlSerializer::ReadMesh(XmlNode &node) {
+    if (node.empty()) {
+        return nullptr;
+    }
+
+    aiMesh *mesh = new aiMesh();
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::vertices) {
+            ImportVertices(currentNode, mesh);
+        } else if (currentName == XmlTag::triangles) {
+            ImportTriangles(currentNode, mesh);
+        }
+    }
+
+    return mesh;
+}
+
+void XmlSerializer::ReadMetadata(XmlNode &node) {
+    pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name);
+    const std::string name = attribute.as_string();
+    const std::string value = node.value();
+    if (name.empty()) {
+        return;
+    }
+
+    MetaEntry entry;
+    entry.name = name;
+    entry.value = value;
+    mMetaData.push_back(entry);
+}
+
+void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) {
+    ai_assert(nullptr != mesh);
+
+    std::vector<aiVector3D> vertices;
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::vertex) {
+            vertices.push_back(ReadVertex(currentNode));
+        }
+    }
+
+    mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+    mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+    std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
+}
+
+void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
+    std::vector<aiFace> faces;
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::triangle) {
+            int pid = IdNotSet, p1 = IdNotSet;
+            bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
+            bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
+
+            if (hasPid && hasP1) {
+                auto it = mResourcesDictionnary.find(pid);
+                if (it != mResourcesDictionnary.end()) {
+                    if (it->second->getType() == ResourceType::RT_BaseMaterials) {
+                        BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
+                        mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
+                    } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
+                        if (mesh->mTextureCoords[0] == nullptr) {
+                            Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second);
+                            const std::string name = ai_to_string(group->mTexId);
+                            for (size_t i = 0; i < mMaterials.size(); ++i) {
+                                if (name == mMaterials[i]->GetName().C_Str()) {
+                                    mesh->mMaterialIndex = static_cast<unsigned int>(i);
+                                }
+                            }
+                            mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()];
+                            for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) {
+                                mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0);
+                            }
+                        }
+                    } 
+                }
+            }
+
+            aiFace face = ReadTriangle(currentNode);
+            faces.push_back(face);
+        }
+    }
+
+    mesh->mNumFaces = static_cast<unsigned int>(faces.size());
+    mesh->mFaces = new aiFace[mesh->mNumFaces];
+    mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+    std::copy(faces.begin(), faces.end(), mesh->mFaces);
+}
+
+void XmlSerializer::ReadBaseMaterials(XmlNode &node) {
+    int id = IdNotSet;
+    if (getNodeAttribute(node, D3MF::XmlTag::id, id)) {
+        BaseMaterials *baseMaterials = new BaseMaterials(id);
+
+        for (XmlNode &currentNode : node.children()) {
+            const std::string currentName = currentNode.name();
+            if (currentName == XmlTag::basematerials_base) {
+                baseMaterials->mMaterialIndex.push_back(static_cast<unsigned int>(mMaterials.size()));
+                mMaterials.push_back(readMaterialDef(currentNode, id));
+            }
+        }
+
+        mResourcesDictionnary.insert(std::make_pair(id, baseMaterials));
+    }
+}
+
+void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    std::string value;
+    EmbeddedTexture *tex2D = nullptr;
+    if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) {
+        tex2D = new EmbeddedTexture(atoi(value.c_str()));
+    }
+    if (nullptr == tex2D) {
+        return;
+    }
+
+    if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) {
+        tex2D->mPath = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) {
+        tex2D->mContentType = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) {
+        tex2D->mTilestyleU = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) {
+        tex2D->mTilestyleV = value;
+    }
+    mEmbeddedTextures.emplace_back(tex2D);
+    StoreEmbeddedTexture(tex2D);
+}
+
+void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) {
+    aiMaterial *mat = new aiMaterial;
+    aiString s;
+    s.Set(ai_to_string(tex->mId).c_str());
+    mat->AddProperty(&s, AI_MATKEY_NAME);
+    const std::string name = "*" + tex->mPath;
+    s.Set(name);
+    mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+    aiColor3D col;
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_DIFFUSE);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_AMBIENT);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_EMISSIVE);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_SPECULAR);
+    mMaterials.emplace_back(mat);
+}
+
+void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) {
+    if (node.empty() || nullptr == tex2DGroup) {
+        return;
+    }
+
+    int id = IdNotSet;
+    if (XmlParser::getIntAttribute(node, "texid", id)) {
+        tex2DGroup->mTexId = id;
+    }
+
+    double value = 0.0;
+    for (XmlNode currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        aiVector2D texCoord;
+        if (currentName == XmlTag::texture_2d_coord) {
+            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value);
+            texCoord.x = (ai_real)value;
+            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value);
+            texCoord.y = (ai_real)value;
+            tex2DGroup->mTex2dCoords.push_back(texCoord);
+        }
+    }
+}
+
+void XmlSerializer::ReadTextureGroup(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    int id = IdNotSet;
+    if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) {
+        return;
+    }
+
+    Texture2DGroup *group = new Texture2DGroup(id);
+    ReadTextureCoords2D(node, group);
+    mResourcesDictionnary.insert(std::make_pair(id, group));
+}
+
+aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) {
+    aiMaterial *material = new aiMaterial();
+    material->mNumProperties = 0;
+    std::string name;
+    bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name);
+
+    std::string stdMaterialName;
+    const std::string strId(ai_to_string(basematerialsId));
+    stdMaterialName += "id";
+    stdMaterialName += strId;
+    stdMaterialName += "_";
+    if (hasName) {
+        stdMaterialName += std::string(name);
+    } else {
+        stdMaterialName += "basemat_";
+        stdMaterialName += ai_to_string(mMaterials.size());
+    }
+
+    aiString assimpMaterialName(stdMaterialName);
+    material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME);
+
+    assignDiffuseColor(node, material);
+
+    return material;
+}
+
+void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
+    if (nullptr == scene || mMaterials.empty()) {
+        return;
+    }
+
+    scene->mNumMaterials = static_cast<unsigned int>(mMaterials.size());
+    scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
+    for (size_t i = 0; i < mMaterials.size(); ++i) {
+        scene->mMaterials[i] = mMaterials[i];
+    }
+}
+
+} // namespace D3MF
+} // namespace Assimp

+ 96 - 0
Engine/lib/assimp/code/AssetLib/3MF/XmlSerializer.h

@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <assimp/XmlParser.h>
+#include <assimp/mesh.h>
+#include <vector>
+#include <map>
+
+struct aiNode;
+struct aiMesh;
+struct aiMaterial;
+
+namespace Assimp {
+namespace D3MF {
+
+class Resource;
+class D3MFOpcPackage;
+class Object;
+class Texture2DGroup;
+class EmbeddedTexture;
+
+class XmlSerializer {
+public:
+    XmlSerializer(XmlParser *xmlParser);
+    ~XmlSerializer();
+    void ImportXml(aiScene *scene);
+
+private:
+    void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform);
+    void ReadObject(XmlNode &node);
+    aiMesh *ReadMesh(XmlNode &node);
+    void ReadMetadata(XmlNode &node);
+    void ImportVertices(XmlNode &node, aiMesh *mesh);
+    void ImportTriangles(XmlNode &node, aiMesh *mesh);
+    void ReadBaseMaterials(XmlNode &node);
+    void ReadEmbeddecTexture(XmlNode &node);
+    void StoreEmbeddedTexture(EmbeddedTexture *tex);
+    void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup);
+    void ReadTextureGroup(XmlNode &node);
+    aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId);
+    void StoreMaterialsInScene(aiScene *scene);
+
+private:
+    struct MetaEntry {
+        std::string name;
+        std::string value;
+    };
+    std::vector<MetaEntry> mMetaData;
+    std::vector<EmbeddedTexture *> mEmbeddedTextures;
+    std::vector<aiMaterial *> mMaterials;
+    std::map<unsigned int, Resource *> mResourcesDictionnary;
+    unsigned int mMeshCount;
+    XmlParser *mXmlParser;
+};
+
+} // namespace D3MF
+} // namespace Assimp

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 308 - 347
Engine/lib/assimp/code/AssetLib/AC/ACLoader.cpp


+ 64 - 70
Engine/lib/assimp/code/AC/ACLoader.h → Engine/lib/assimp/code/AssetLib/AC/ACLoader.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -56,29 +55,23 @@ 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();
-
-
+    ~AC3DImporter() override;
 
     // 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;
@@ -103,43 +96,50 @@ public:
     };
 
     // Represents an AC3D surface
-    struct Surface
-    {
-        Surface()
-            :   mat     (0)
-            ,   flags   (0)
-        {}
+    struct Surface {
+        Surface() :
+                mat(0),
+                flags(0) {}
+
+        unsigned int mat, flags;
+
+        typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
+        std::vector<SurfaceEntry> entries;
+
+        // Type is low nibble of flags
+        enum Type : uint8_t {
+            Polygon = 0x0,
+            ClosedLine = 0x1,
+            OpenLine = 0x2,
+            TriangleStrip = 0x4, // ACC extension (TORCS and Speed Dreams)
 
-        unsigned int mat,flags;
+            Mask = 0xf,
+        };
 
-        typedef std::pair<unsigned int, aiVector2D > SurfaceEntry;
-        std::vector< SurfaceEntry > entries;
+        inline uint8_t GetType() const { return (flags & Mask); }
     };
 
     // 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;
@@ -179,37 +179,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 override;
 
 protected:
-
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc *GetInfo() const override;
 
     // -------------------------------------------------------------------
     /** 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) override;
 
     // -------------------------------------------------------------------
     /** 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) override;
 
 private:
-
     // -------------------------------------------------------------------
     /** Get the next line from the file.
      *  @return false if the end of the file was reached*/
@@ -220,7 +216,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.
@@ -229,26 +225,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
@@ -265,10 +259,10 @@ private:
     unsigned int mNumMeshes;
 
     // current list of light sources
-    std::vector<aiLight*>* mLights;
+    std::vector<aiLight *> *mLights;
 
     // name counters
-    unsigned int lights, groups, polys, worlds;
+    unsigned int mLightsCounter, mGroupsCounter, mPolysCounter, mWorldsCounter;
 };
 
 } // end of namespace Assimp

+ 524 - 0
Engine/lib/assimp/code/AssetLib/AMF/AMFImporter.cpp

@@ -0,0 +1,524 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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_AMF_IMPORTER
+
+// Header files, Assimp.
+#include "AMFImporter.hpp"
+
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/fast_atof.h>
+#include <assimp/StringUtils.h>
+
+// Header files, stdlib.
+#include <memory>
+
+namespace Assimp {
+
+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 (AMFNodeElementBase *ne : mNodeElement_List) {
+            delete ne;
+        }
+
+        mNodeElement_List.clear();
+    }
+}
+
+AMFImporter::AMFImporter() AI_NO_EXCEPT :
+        mNodeElement_Cur(nullptr),
+        mXmlParser(nullptr),
+        mUnit(),
+        mVersion(),
+        mMaterial_Converted(),
+        mTexture_Converted() {
+    // empty
+}
+
+AMFImporter::~AMFImporter() {
+    delete mXmlParser;
+    // 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 AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const {
+    for (AMFNodeElementBase *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, NodeArray &nodeArray, aiNode **pNode) const {
+    aiString node_name(pID.c_str());
+    for (aiNode *node : nodeArray) {
+        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 &nodeName) {
+    throw DeadlyImportError("Close tag for node <" + nodeName + "> not found. Seems file is corrupt.");
+}
+
+void AMFImporter::Throw_IncorrectAttr(const std::string &nodeName, const std::string &attrName) {
+    throw DeadlyImportError("Node <" + nodeName + "> has incorrect attribute \"" + attrName + "\".");
+}
+
+void AMFImporter::Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &attrName) {
+    throw DeadlyImportError("Attribute \"" + attrName + "\" in node <" + nodeName + "> has incorrect value.");
+}
+
+void AMFImporter::Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) {
+    throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + nodeName + ". 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(pugi::xml_node &node) {
+    if (node.children().begin() == node.children().end()) {
+        throw DeadlyImportError(std::string("Node <") + node.name() + "> must have children.");
+    }
+}
+
+bool AMFImporter::XML_SearchNode(const std::string &nodeName) {
+    return nullptr != mXmlParser->findNode(nodeName);
+}
+
+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((unsigned char)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) {
+    std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
+
+    // Check whether we can read from the file
+    if (file.get() == nullptr) {
+        throw DeadlyImportError("Failed to open AMF file ", pFile, ".");
+    }
+
+    mXmlParser = new XmlParser();
+    if (!mXmlParser->parse(file.get())) {
+        delete mXmlParser;
+        mXmlParser = nullptr;
+        throw DeadlyImportError("Failed to create XML reader for file ", pFile, ".");
+    }
+
+    // Start reading, search for root tag <amf>
+    if (!mXmlParser->hasNode("amf")) {
+        throw DeadlyImportError("Root node \"amf\" not found.");
+    }
+    ParseNode_Root();
+} // namespace Assimp
+
+void AMFImporter::ParseHelper_Node_Enter(AMFNodeElementBase *node) {
+    mNodeElement_Cur->Child.push_back(node); // add new element to current element child list.
+    mNodeElement_Cur = node;
+}
+
+void AMFImporter::ParseHelper_Node_Exit() {
+    if (mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
+}
+
+// <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() {
+    AMFNodeElementBase *ne = nullptr;
+    XmlNode *root = mXmlParser->findNode("amf");
+    if (nullptr == root) {
+        throw DeadlyImportError("Root node \"amf\" not found.");
+    }
+    XmlNode node = *root;
+    mUnit = ai_tolower(std::string(node.attribute("unit").as_string()));
+
+    mVersion = node.attribute("version").as_string();
+
+    // Read attributes for node <amf>.
+    // Check attributes
+    if (!mUnit.empty()) {
+        if ((mUnit != "inch") && (mUnit != "millimeters") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) {
+            Throw_IncorrectAttrValue("unit", mUnit);
+        }
+    }
+
+    // create root node element.
+    ne = new AMFRoot(nullptr);
+
+    mNodeElement_Cur = ne; // set first "current" element
+    // and assign attribute's values
+    ((AMFRoot *)ne)->Unit = mUnit;
+    ((AMFRoot *)ne)->Version = mVersion;
+
+    // Check for child nodes
+    for (XmlNode &currentNode : node.children() ) {
+        const std::string currentName = currentNode.name();
+        if (currentName == "object") {
+            ParseNode_Object(currentNode);
+        } else if (currentName == "material") {
+            ParseNode_Material(currentNode);
+        } else if (currentName == "texture") {
+            ParseNode_Texture(currentNode);
+        } else if (currentName == "constellation") {
+            ParseNode_Constellation(currentNode);
+        } else if (currentName == "metadata") {
+            ParseNode_Metadata(currentNode);
+        }
+        mNodeElement_Cur = ne;
+    }
+    mNodeElement_Cur = ne; // force restore "current" element
+    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(XmlNode &node) {
+    std::string id;
+    id = node.attribute("id").as_string();
+
+    // create and if needed - define new grouping object.
+    AMFNodeElementBase *ne = new AMFConstellation(mNodeElement_Cur);
+
+    AMFConstellation &als = *((AMFConstellation *)ne); // alias for convenience
+
+    if (!id.empty()) {
+        als.ID = id;
+    }
+
+    // Check for child nodes
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+            std::string name = currentNode.name();
+            if (name == "instance") {
+                ParseNode_Instance(currentNode);
+            } else if (name == "metadata") {
+                ParseNode_Metadata(currentNode);
+            }
+        }
+        ParseHelper_Node_Exit();
+    } else {
+        mNodeElement_Cur->Child.push_back(ne);
+    }
+    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(XmlNode &node) {
+    AMFNodeElementBase *ne(nullptr);
+
+    // Read attributes for node <constellation>.
+    std::string objectid = node.attribute("objectid").as_string();
+
+    // 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 AMFInstance(mNodeElement_Cur);
+    AMFInstance &als = *((AMFInstance *)ne);
+    als.ObjectID = objectid;
+
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (auto &currentNode : node.children()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == "deltax") {
+                XmlParser::getValueAsFloat(currentNode, als.Delta.x);
+            } else if (currentName == "deltay") {
+                XmlParser::getValueAsFloat(currentNode, als.Delta.y);
+            } else if (currentName == "deltaz") {
+                XmlParser::getValueAsFloat(currentNode, als.Delta.z);
+            } else if (currentName == "rx") {
+                XmlParser::getValueAsFloat(currentNode, als.Delta.x);
+            } else if (currentName == "ry") {
+                XmlParser::getValueAsFloat(currentNode, als.Delta.y);
+            } else if (currentName == "rz") {
+                XmlParser::getValueAsFloat(currentNode, als.Delta.z);
+            }
+        }
+        ParseHelper_Node_Exit();
+    } else {
+        mNodeElement_Cur->Child.push_back(ne);
+    }
+
+    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(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // Read attributes for node <object>.
+    std::string id = node.attribute("id").as_string();
+
+    // create and if needed - define new geometry object.
+    ne = new AMFObject(mNodeElement_Cur);
+
+    AMFObject &als = *((AMFObject *)ne); // alias for convenience
+
+    if (!id.empty()) {
+        als.ID = id;
+    }
+
+    // Check for child nodes
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (auto &currentNode : node.children()) {
+            const std::string &currentName = currentNode.name();
+            if (currentName == "color") {
+                ParseNode_Color(currentNode);
+            } else if (currentName == "mesh") {
+                ParseNode_Mesh(currentNode);
+            } else if (currentName == "metadata") {
+                ParseNode_Metadata(currentNode);
+            }
+        }
+        ParseHelper_Node_Exit();
+    } else {
+        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.
+}
+
+// <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(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    std::string type = node.attribute("type").as_string(), value;
+    XmlParser::getValueAsString(node, value);
+
+    // read attribute
+    ne = new AMFMetadata(mNodeElement_Cur);
+    ((AMFMetadata *)ne)->Type = type;
+    ((AMFMetadata *)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.
+}
+
+bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*pCheckSig*/) const {
+    static const char *tokens[] = { "<amf" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+}
+
+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

+ 310 - 0
Engine/lib/assimp/code/AssetLib/AMF/AMFImporter.hpp

@@ -0,0 +1,310 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.hpp
+/// \brief AMF-format files importer for Assimp.
+/// \date 2016
+/// \author [email protected]
+// Thanks to acorn89 for support.
+
+#pragma once
+#ifndef INCLUDED_AI_AMF_IMPORTER_H
+#define INCLUDED_AI_AMF_IMPORTER_H
+
+#include "AMFImporter_Node.hpp"
+
+// Header files, Assimp.
+#include "assimp/types.h"
+#include <assimp/BaseImporter.h>
+#include <assimp/XmlParser.h>
+#include <assimp/importerdesc.h>
+#include <assimp/DefaultLogger.hpp>
+
+// Header files, stdlib.
+#include <set>
+
+namespace Assimp {
+
+/// \class AMFImporter
+/// Class that holding scene graph which include: geometry, metadata, materials etc.
+///
+/// Implementing features.
+///
+/// Limitations.
+///
+/// 1. When for texture mapping used set of source textures (r, g, b, a) not only one then attribute "tiled" for all set will be true if it true in any of
+///    source textures.
+///    Example. Triangle use for texture mapping three textures. Two of them has "tiled" set to false and one - set to true. In scene all three textures
+///    will be tiled.
+///
+/// Unsupported features:
+/// 1. Node <composite>, formulas in <composite> and <color>. For implementing this feature can be used expression parser "muParser" like in project
+///    "amf_tools".
+/// 2. Attribute "profile" in node <color>.
+/// 3. Curved geometry: <edge>, <normal> and children nodes of them.
+/// 4. Attributes: "unit" and "version" in <amf> read but do nothing.
+/// 5. <metadata> stored only for root node <amf>.
+/// 6. Color averaging of vertices for which <triangle>'s set different colors.
+///
+/// Supported nodes:
+///    General:
+///        <amf>; <constellation>; <instance> and children <deltax>, <deltay>, <deltaz>, <rx>, <ry>, <rz>; <metadata>;
+///
+///    Geometry:
+///        <object>; <mesh>; <vertices>; <vertex>; <coordinates> and children <x>, <y>, <z>; <volume>; <triangle> and children <v1>, <v2>, <v3>;
+///
+///    Material:
+///        <color> and children <r>, <g>, <b>, <a>; <texture>; <material>;
+///        two variants of texture coordinates:
+///            new - <texmap> and children <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>
+///            old - <map> and children <u1>, <u2>, <u3>, <v1>, <v2>, <v3>
+///
+class AMFImporter : public BaseImporter {
+private:
+    struct SPP_Material; // forward declaration
+
+    /// Data type for post-processing step. More suitable container for part of material's composition.
+    struct SPP_Composite {
+        SPP_Material *Material; ///< Pointer to material - part of composition.
+        std::string Formula; ///< Formula for calculating ratio of \ref Material.
+    };
+
+    /// \struct SPP_Material
+    /// Data type for post-processing step. More suitable container for material.
+    struct SPP_Material {
+        std::string ID; ///< Material ID.
+        std::list<AMFMetadata *> Metadata; ///< Metadata of material.
+        AMFColor *Color; ///< Color of material.
+        std::list<SPP_Composite> Composition; ///< List of child materials if current material is composition of few another.
+
+        /// Return color calculated for specified coordinate.
+        /// \param [in] pX - "x" coordinate.
+        /// \param [in] pY - "y" coordinate.
+        /// \param [in] pZ - "z" coordinate.
+        /// \return calculated color.
+        aiColor4D GetColor(const float pX, const float pY, const float pZ) const;
+    };
+
+    /// Data type for post-processing step. More suitable container for texture.
+    struct SPP_Texture {
+        std::string ID;
+        size_t Width, Height, Depth;
+        bool Tiled;
+        char FormatHint[9]; // 8 for string + 1 for terminator.
+        uint8_t *Data;
+    };
+
+    /// Data type for post-processing step. Contain face data.
+    struct SComplexFace {
+        aiFace Face; ///< Face vertices.
+        const AMFColor *Color; ///< Face color. Equal to nullptr if color is not set for the face.
+        const AMFTexMap *TexMap; ///< Face texture mapping data. Equal to nullptr if texture mapping is not set for the face.
+    };
+
+    using AMFMetaDataArray = std::vector<AMFMetadata*>;
+    using MeshArray = std::vector<aiMesh*>;
+    using NodeArray = std::vector<aiNode*>;
+
+    /// Clear all temporary data.
+    void Clear();
+
+    /// Get data stored in <vertices> and place it to arrays.
+    /// \param [in] pNodeElement - reference to node element which kept <object> data.
+    /// \param [in] pVertexCoordinateArray - reference to vertices coordinates kept in <vertices>.
+    /// \param [in] pVertexColorArray - reference to vertices colors for all <vertex's. If color for vertex is not set then corresponding member of array
+    /// contain nullptr.
+    void PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElement, std::vector<aiVector3D> &pVertexCoordinateArray,
+            std::vector<AMFColor *> &pVertexColorArray) const;
+
+    /// Return converted texture ID which related to specified source textures ID's. If converted texture does not exist then it will be created and ID on new
+    /// converted texture will be returned. Conversion: set of textures from \ref CAMFImporter_NodeElement_Texture to one \ref SPP_Texture and place it
+    /// to converted textures list.
+    /// Any of source ID's can be absent(empty string) or even one ID only specified. But at least one ID must be specified.
+    /// \param [in] pID_R - ID of source "red" texture.
+    /// \param [in] pID_G - ID of source "green" texture.
+    /// \param [in] pID_B - ID of source "blue" texture.
+    /// \param [in] pID_A - ID of source "alpha" texture.
+    /// \return index of the texture in array of the converted textures.
+    size_t PostprocessHelper_GetTextureID_Or_Create(const std::string &pID_R, const std::string &pID_G, const std::string &pID_B, const std::string &pID_A);
+
+    /// Separate input list by texture IDs. This step is needed because aiMesh can contain mesh which is use only one texture (or set: diffuse, bump etc).
+    /// \param [in] pInputList - input list with faces. Some of them can contain color or texture mapping, or both of them, or nothing. Will be cleared after
+    /// processing.
+    /// \param [out] pOutputList_Separated - output list of the faces lists. Separated faces list by used texture IDs. Will be cleared before processing.
+    void PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace> &pInputList, std::list<std::list<SComplexFace>> &pOutputList_Separated);
+
+    /// Check if child elements of node element is metadata and add it to scene node.
+    /// \param [in] pMetadataList - reference to list with collected metadata.
+    /// \param [out] pSceneNode - scene node in which metadata will be added.
+    void Postprocess_AddMetadata(const AMFMetaDataArray &pMetadataList, aiNode &pSceneNode) const;
+
+    /// To create aiMesh and aiNode for it from <object>.
+    /// \param [in] pNodeElement - reference to node element which kept <object> data.
+    /// \param [out] meshList    - reference to a list with all aiMesh of the scene.
+    /// \param [out] pSceneNode  - pointer to place where new aiNode will be created.
+    void Postprocess_BuildNodeAndObject(const AMFObject &pNodeElement, MeshArray &meshList, aiNode **pSceneNode);
+
+    /// Create mesh for every <volume> in <mesh>.
+    /// \param [in] pNodeElement - reference to node element which kept <mesh> data.
+    /// \param [in] pVertexCoordinateArray - reference to vertices coordinates for all <volume>'s.
+    /// \param [in] pVertexColorArray - reference to vertices colors for all <volume>'s. If color for vertex is not set then corresponding member of array
+    /// contain nullptr.
+    /// \param [in] pObjectColor - pointer to colors for <object>. If color is not set then argument contain nullptr.
+    /// \param [in] pMaterialList - reference to a list with defined materials.
+    /// \param [out] pMeshList - reference to a list with all aiMesh of the scene.
+    /// \param [out] pSceneNode - reference to aiNode which will own new aiMesh's.
+    void Postprocess_BuildMeshSet(const AMFMesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray,
+            const std::vector<AMFColor *> &pVertexColorArray, const AMFColor *pObjectColor,
+            MeshArray &pMeshList, aiNode &pSceneNode);
+
+    /// Convert material from \ref CAMFImporter_NodeElement_Material to \ref SPP_Material.
+    /// \param [in] pMaterial - source CAMFImporter_NodeElement_Material.
+    void Postprocess_BuildMaterial(const AMFMaterial &pMaterial);
+
+    /// Create and add to aiNode's list new part of scene graph defined by <constellation>.
+    /// \param [in] pConstellation - reference to <constellation> node.
+    /// \param [out] nodeArray     - reference to aiNode's list.
+    void Postprocess_BuildConstellation(AMFConstellation &pConstellation, NodeArray &nodeArray) const;
+
+    /// Build Assimp scene graph in aiScene from collected data.
+    /// \param [out] pScene - pointer to aiScene where tree will be built.
+    void Postprocess_BuildScene(aiScene *pScene);
+
+    /// Decode Base64-encoded data.
+    /// \param [in] pInputBase64 - reference to input Base64-encoded string.
+    /// \param [out] pOutputData - reference to output array for decoded data.
+    void ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector<uint8_t> &pOutputData) const;
+
+    /// Parse <AMF> node of the file.
+    void ParseNode_Root();
+
+    /// Parse <constellation> node of the file.
+    void ParseNode_Constellation(XmlNode &node);
+
+    /// Parse <instance> node of the file.
+    void ParseNode_Instance(XmlNode &node);
+
+    /// Parse <material> node of the file.
+    void ParseNode_Material(XmlNode &node);
+
+    /// Parse <metadata> node.
+    void ParseNode_Metadata(XmlNode &node);
+
+    /// Parse <object> node of the file.
+    void ParseNode_Object(XmlNode &node);
+
+    /// Parse <texture> node of the file.
+    void ParseNode_Texture(XmlNode &node);
+
+    /// Parse <coordinates> node of the file.
+    void ParseNode_Coordinates(XmlNode &node);
+
+    /// Parse <edge> node of the file.
+    void ParseNode_Edge(XmlNode &node);
+
+    /// Parse <mesh> node of the file.
+    void ParseNode_Mesh(XmlNode &node);
+
+    /// Parse <triangle> node of the file.
+    void ParseNode_Triangle(XmlNode &node);
+
+    /// Parse <vertex> node of the file.
+    void ParseNode_Vertex(XmlNode &node);
+
+    /// Parse <vertices> node of the file.
+    void ParseNode_Vertices(XmlNode &node);
+
+    /// Parse <volume> node of the file.
+    void ParseNode_Volume(XmlNode &node);
+
+    /// Parse <color> node of the file.
+    void ParseNode_Color(XmlNode &node);
+
+    /// Parse <texmap> of <map> node of the file.
+    /// \param [in] pUseOldName - if true then use old name of node(and children) - <map>, instead of new name - <texmap>.
+    void ParseNode_TexMap(XmlNode &node, const bool pUseOldName = false);
+
+public:
+    /// Default constructor.
+    AMFImporter() AI_NO_EXCEPT;
+
+    /// Default destructor.
+    ~AMFImporter() override;
+
+    /// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
+    /// Also exception can be thrown if trouble will found.
+    /// \param [in] pFile - name of file to be parsed.
+    /// \param [in] pIOHandler - pointer to IO helper object.
+    void ParseFile(const std::string &pFile, IOSystem *pIOHandler);
+    void ParseHelper_Node_Enter(AMFNodeElementBase *child);
+    void ParseHelper_Node_Exit();
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const override;
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+    const aiImporterDesc *GetInfo() const override;
+    bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const;
+    bool Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const;
+    bool Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const;
+    void Throw_CloseNotFound(const std::string &nodeName);
+    void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName);
+    void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName);
+    void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription);
+    void Throw_ID_NotFound(const std::string &pID) const;
+    void XML_CheckNode_MustHaveChildren(pugi::xml_node &node);
+    bool XML_SearchNode(const std::string &nodeName);
+    void ParseHelper_FixTruncatedFloatString(const char *pInStr, std::string &pOutString);
+    AMFImporter(const AMFImporter &pScene) = delete;
+    AMFImporter &operator=(const AMFImporter &pScene) = delete;
+
+private:
+    static const aiImporterDesc Description;
+
+    AMFNodeElementBase *mNodeElement_Cur; ///< Current element.
+    std::list<AMFNodeElementBase *> mNodeElement_List; ///< All elements of scene graph.
+    XmlParser *mXmlParser;
+    std::string mUnit;
+    std::string mVersion;
+    std::list<SPP_Material> mMaterial_Converted; ///< List of converted materials for postprocessing step.
+    std::list<SPP_Texture> mTexture_Converted; ///< List of converted textures for postprocessing step.
+};
+
+} // namespace Assimp
+
+#endif // INCLUDED_AI_AMF_IMPORTER_H

+ 284 - 0
Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Geometry.cpp

@@ -0,0 +1,284 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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_AMF_IMPORTER
+
+#include "AMFImporter.hpp"
+#include <assimp/ParsingUtils.h>
+
+namespace Assimp {
+
+// <mesh>
+// </mesh>
+// A 3D mesh hull.
+// Multi elements - Yes.
+// Parent element - <object>.
+void AMFImporter::ParseNode_Mesh(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // Check for child nodes
+    if (0 != ASSIMP_stricmp(node.name(), "mesh")) {
+        return;
+    }
+    // create new mesh object.
+    ne = new AMFMesh(mNodeElement_Cur);
+    bool found_verts = false, found_volumes = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        pugi::xml_node vertNode = node.child("vertices");
+        if (!vertNode.empty()) {
+            ParseNode_Vertices(vertNode);
+            found_verts = true;
+        }
+
+        pugi::xml_node volumeNode = node.child("volume");
+        if (!volumeNode.empty()) {
+            ParseNode_Volume(volumeNode);
+            found_volumes = true;
+        }
+        ParseHelper_Node_Exit();
+    }
+
+    if (!found_verts && !found_volumes) {
+        mNodeElement_Cur->Child.push_back(ne);
+    } // if(!mReader->isEmptyElement()) else
+
+    // and to node element list because its a new object in graph.
+    mNodeElement_List.push_back(ne);
+}
+
+// <vertices>
+// </vertices>
+// The list of vertices to be used in defining triangles.
+// Multi elements - No.
+// Parent element - <mesh>.
+void AMFImporter::ParseNode_Vertices(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // create new mesh object.
+    ne = new AMFVertices(mNodeElement_Cur);
+    // Check for child nodes
+    if (node.empty()) {
+        mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
+        return;
+    }
+    ParseHelper_Node_Enter(ne);
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "vertex") {
+            ParseNode_Vertex(currentNode);
+        }
+    }
+    ParseHelper_Node_Exit();
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <vertex>
+// </vertex>
+// A vertex to be referenced in triangles.
+// Multi elements - Yes.
+// Parent element - <vertices>.
+void AMFImporter::ParseNode_Vertex(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+
+    // create new mesh object.
+    ne = new AMFVertex(mNodeElement_Cur);
+
+    // Check for child nodes
+    pugi::xml_node colorNode = node.child("color");
+    bool col_read = false;
+    bool coord_read = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        if (!colorNode.empty()) {
+            ParseNode_Color(colorNode);
+            col_read = true;
+        }
+        pugi::xml_node coordNode = node.child("coordinates");
+        if (!coordNode.empty()) {
+            ParseNode_Coordinates(coordNode);
+            coord_read = true;
+        }
+        ParseHelper_Node_Exit();
+    }
+
+    if (!coord_read && !col_read) {
+        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.
+}
+
+// <coordinates>
+// </coordinates>
+// Specifies the 3D location of this vertex.
+// Multi elements - No.
+// Parent element - <vertex>.
+//
+// Children elements:
+//   <x>, <y>, <z>
+//   Multi elements - No.
+//   X, Y, or Z coordinate, respectively, of a vertex position in space.
+void AMFImporter::ParseNode_Coordinates(XmlNode &node) {
+    AMFNodeElementBase *ne = nullptr;
+    if (!node.empty()) {
+        ne = new AMFCoordinates(mNodeElement_Cur);
+        ParseHelper_Node_Enter(ne);
+        for (XmlNode &currentNode : node.children()) {
+            // create new color object.
+            AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
+            const std::string &currentName = ai_tolower(currentNode.name());
+            if (currentName == "x") {
+                XmlParser::getValueAsFloat(currentNode, als.Coordinate.x);
+            } else if (currentName == "y") {
+                XmlParser::getValueAsFloat(currentNode, als.Coordinate.y);
+            } else if (currentName == "z") {
+                XmlParser::getValueAsFloat(currentNode, als.Coordinate.z);
+            }
+        }
+        ParseHelper_Node_Exit();
+
+    } else {
+        mNodeElement_Cur->Child.push_back(new AMFCoordinates(mNodeElement_Cur));
+    }
+
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+}
+
+// <volume
+// materialid="" - Which material to use.
+// type=""       - What this volume describes can be "region" or "support". If none specified, "object" is assumed. If support, then the geometric
+//                 requirements 1-8 listed in section 5 do not need to be maintained.
+// >
+// </volume>
+// Defines a volume from the established vertex list.
+// Multi elements - Yes.
+// Parent element - <mesh>.
+void AMFImporter::ParseNode_Volume(XmlNode &node) {
+    std::string materialid;
+    std::string type;
+    AMFNodeElementBase *ne = new AMFVolume(mNodeElement_Cur);
+
+    // Read attributes for node <color>.
+    // and assign read data
+
+    ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string();
+
+    ((AMFVolume *)ne)->Type = type;
+    // Check for child nodes
+    bool col_read = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (auto &currentNode : node.children()) {
+            const std::string currentName = currentNode.name();
+            if (currentName == "color") {
+                if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for <volume>.");
+                ParseNode_Color(currentNode);
+                col_read = true;
+            } else if (currentName == "triangle") {
+                ParseNode_Triangle(currentNode);
+            } else if (currentName == "metadata") {
+                ParseNode_Metadata(currentNode);
+            } else if (currentName == "volume") {
+                ParseNode_Metadata(currentNode);
+            }
+        }
+        ParseHelper_Node_Exit();
+    } else {
+        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.
+}
+
+// <triangle>
+// </triangle>
+// Defines a 3D triangle from three vertices, according to the right-hand rule (counter-clockwise when looking from the outside).
+// Multi elements - Yes.
+// Parent element - <volume>.
+//
+// Children elements:
+//   <v1>, <v2>, <v3>
+//   Multi elements - No.
+//   Index of the desired vertices in a triangle or edge.
+void AMFImporter::ParseNode_Triangle(XmlNode &node) {
+    AMFNodeElementBase *ne = new AMFTriangle(mNodeElement_Cur);
+
+    // create new triangle object.
+
+    AMFTriangle &als = *((AMFTriangle *)ne); // alias for convenience
+
+    bool col_read = false;
+    if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        std::string v;
+        for (auto &currentNode : node.children()) {
+            const std::string currentName = currentNode.name();
+            if (currentName == "color") {
+                if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for <triangle>.");
+                ParseNode_Color(currentNode);
+                col_read = true;
+            } else if (currentName == "texmap") {
+                ParseNode_TexMap(currentNode);
+            } else if (currentName == "map") {
+                ParseNode_TexMap(currentNode, true);
+            } else if (currentName == "v1") {
+                XmlParser::getValueAsString(currentNode, v);
+                als.V[0] = std::atoi(v.c_str());
+            } else if (currentName == "v2") {
+                XmlParser::getValueAsString(currentNode, v);
+                als.V[1] = std::atoi(v.c_str());
+            } else if (currentName == "v3") {
+                XmlParser::getValueAsString(currentNode, v);
+                als.V[2] = std::atoi(v.c_str());
+            }
+        }
+        ParseHelper_Node_Exit();
+    } else {
+        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.
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_AMF_IMPORTER

+ 157 - 159
Engine/lib/assimp/code/AMF/AMFImporter_Material.cpp → Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Material.cpp

@@ -1,11 +1,9 @@
-/*
+/*
 ---------------------------------------------------------------------------
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -49,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
 
 #include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
 
-namespace Assimp
-{
+namespace Assimp {
 
 // <color
 // profile="" - The ICC color space used to interpret the three color channels <r>, <g> and <b>.
@@ -68,51 +64,46 @@ namespace Assimp
 //   Multi elements - No.
 //   Red, Greed, Blue and Alpha (transparency) component of a color in sRGB space, values ranging from 0 to 1. The
 //   values can be specified as constants, or as a formula depending on the coordinates.
-void AMFImporter::ParseNode_Color() {
-    std::string profile;
-    CAMFImporter_NodeElement* ne;
+void AMFImporter::ParseNode_Color(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
 
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("profile", profile, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new color object.
-	ne = new CAMFImporter_NodeElement_Color(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Color& als = *((CAMFImporter_NodeElement_Color*)ne);// alias for convenience
-
-	als.Profile = profile;
-	// Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool read_flag[4] = { false, false, false, false };
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("color");
-			MACRO_NODECHECK_READCOMP_F("r", read_flag[0], als.Color.r);
-			MACRO_NODECHECK_READCOMP_F("g", read_flag[1], als.Color.g);
-			MACRO_NODECHECK_READCOMP_F("b", read_flag[2], als.Color.b);
-			MACRO_NODECHECK_READCOMP_F("a", read_flag[3], als.Color.a);
-		MACRO_NODECHECK_LOOPEND("color");
-		ParseHelper_Node_Exit();
-		// check that all components was defined
-        if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
-            throw DeadlyImportError("Not all color components are defined.");
+    const std::string &profile = node.attribute("profile").as_string();
+    bool read_flag[4] = { false, false, false, false };
+    AMFNodeElementBase *ne = new AMFColor(mNodeElement_Cur);
+    AMFColor &als = *((AMFColor *)ne); // alias for convenience
+    ParseHelper_Node_Enter(ne);
+    for (pugi::xml_node &child : node.children()) {
+        // create new color object.
+        als.Profile = profile;
+
+        const std::string &name = child.name();
+        if ( name == "r") {
+			read_flag[0] = true;
+            XmlParser::getValueAsFloat(child, als.Color.r);
+        } else if (name == "g") {
+			read_flag[1] = true;
+            XmlParser::getValueAsFloat(child, als.Color.g);
+        } else if (name == "b") {
+			read_flag[2] = true;
+            XmlParser::getValueAsFloat(child, als.Color.b);
+        } else if (name == "a") {
+			read_flag[3] = true;
+            XmlParser::getValueAsFloat(child, als.Color.a);
         }
-
         // check if <a> is absent. Then manually add "a == 1".
         if (!read_flag[3]) {
             als.Color.a = 1;
         }
+    }
+    als.Composed = false;
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+    ParseHelper_Node_Exit();
+    // check that all components was defined
+	if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
+		throw DeadlyImportError("Not all color components are defined.");
 	}
-	else
-	{
-		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
-	}
-
-	als.Composed = false;
-	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
 }
 
 // <material
@@ -122,45 +113,25 @@ void AMFImporter::ParseNode_Color() {
 // An available material.
 // Multi elements - Yes.
 // Parent element - <amf>.
-void AMFImporter::ParseNode_Material() {
-    std::string id;
-    CAMFImporter_NodeElement* ne;
-
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new object.
-	ne = new CAMFImporter_NodeElement_Material(mNodeElement_Cur);
-
-    // and assign read data
-	((CAMFImporter_NodeElement_Material*)ne)->ID = id;
+void AMFImporter::ParseNode_Material(XmlNode &node) {
+    // create new object and assign read data
+	std::string id = node.attribute("id").as_string();
+	AMFNodeElementBase *ne = new AMFMaterial(mNodeElement_Cur);
+	((AMFMaterial*)ne)->ID = id;
 
     // Check for child nodes
-	if(!mReader->isEmptyElement())
-	{
-		bool col_read = false;
-
-		ParseHelper_Node_Enter(ne);
-		MACRO_NODECHECK_LOOPBEGIN("material");
-			if(XML_CheckNode_NameEqual("color"))
-			{
-				// Check if data already defined.
-				if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <material>.");
-				// read data and set flag about it
-				ParseNode_Color();
-				col_read = true;
-
-				continue;
+	if (!node.empty()) {
+        ParseHelper_Node_Enter(ne);
+        for (pugi::xml_node &child : node.children()) {
+            const std::string name = child.name();
+            if (name == "color") {
+				ParseNode_Color(child);
+            } else if (name == "metadata") {
+				ParseNode_Metadata(child);
 			}
-
-			if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
-		MACRO_NODECHECK_LOOPEND("material");
-		ParseHelper_Node_Exit();
-	}
-	else
-	{
+		}
+        ParseHelper_Node_Exit();
+	} else {
 		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
 	}
 
@@ -183,51 +154,39 @@ void AMFImporter::ParseNode_Material() {
 // then layer by layer.
 // Multi elements - Yes.
 // Parent element - <amf>.
-void AMFImporter::ParseNode_Texture()
-{
-    std::string id;
-    uint32_t width = 0;
-    uint32_t height = 0;
-    uint32_t depth = 1;
-    std::string type;
-    bool tiled = false;
-    std::string enc64_data;
-
-	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("width", width, XML_ReadNode_GetAttrVal_AsU32);
-		MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsU32);
-		MACRO_ATTRREAD_CHECK_RET("depth", depth, XML_ReadNode_GetAttrVal_AsU32);
-		MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("tiled", tiled, XML_ReadNode_GetAttrVal_AsBool);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new texture object.
-    CAMFImporter_NodeElement *ne = new CAMFImporter_NodeElement_Texture(mNodeElement_Cur);
-
-	CAMFImporter_NodeElement_Texture& als = *((CAMFImporter_NodeElement_Texture*)ne);// alias for convenience
-
-	// Check for child nodes
-    if (!mReader->isEmptyElement()) {
-        XML_ReadNode_GetVal_AsString(enc64_data);
+void AMFImporter::ParseNode_Texture(XmlNode &node) {
+    const std::string id = node.attribute("id").as_string();
+	const uint32_t width = node.attribute("width").as_uint();
+    const uint32_t height = node.attribute("height").as_uint();
+    uint32_t depth = node.attribute("depth").as_uint();
+    const std::string type = node.attribute("type").as_string();
+	bool tiled = node.attribute("tiled").as_bool();
+
+    if (node.empty()) {
+		return;
     }
 
+    // create new texture object.
+    AMFNodeElementBase *ne = new AMFTexture(mNodeElement_Cur);
+
+	AMFTexture& als = *((AMFTexture*)ne);// alias for convenience
+
+    std::string enc64_data;
+    XmlParser::getValueAsString(node, enc64_data);
+    // Check for child nodes
+
 	// check that all components was defined
     if (id.empty()) {
-        throw DeadlyImportError("ID for texture must be defined.");
+		throw DeadlyImportError("ID for texture must be defined.");
     }
     if (width < 1) {
-        Throw_IncorrectAttrValue("width");
+		throw DeadlyImportError("Invalid width for texture.");
     }
     if (height < 1) {
-        Throw_IncorrectAttrValue("height");
-    }
-    if (depth < 1) {
-        Throw_IncorrectAttrValue("depth");
-    }
+		throw DeadlyImportError("Invalid height for texture.");
+	}
     if (type != "grayscale") {
-        Throw_IncorrectAttrValue("type");
+		throw DeadlyImportError("Invalid type for texture.");
     }
     if (enc64_data.empty()) {
         throw DeadlyImportError("Texture data not defined.");
@@ -239,7 +198,9 @@ void AMFImporter::ParseNode_Texture()
 	als.Depth = depth;
 	als.Tiled = tiled;
 	ParseHelper_Decode_Base64(enc64_data, als.Data);
-
+    if (depth == 0) {
+        depth = (uint32_t)(als.Data.size() / (width * height));
+    }
     // check data size
     if ((width * height * depth) != als.Data.size()) {
         throw DeadlyImportError("Texture has incorrect data size.");
@@ -263,57 +224,94 @@ void AMFImporter::ParseNode_Texture()
 //   <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>. Old name: <u1>, <u2>, <u3>, <v1>, <v2>, <v3>.
 //   Multi elements - No.
 //   Texture coordinates for every vertex of triangle.
-void AMFImporter::ParseNode_TexMap(const bool pUseOldName) {
-    std::string rtexid, gtexid, btexid, atexid;
-
+void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
 	// Read attributes for node <color>.
-	MACRO_ATTRREAD_LOOPBEG;
-		MACRO_ATTRREAD_CHECK_RET("rtexid", rtexid, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("gtexid", gtexid, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("btexid", btexid, mReader->getAttributeValue);
-		MACRO_ATTRREAD_CHECK_RET("atexid", atexid, mReader->getAttributeValue);
-	MACRO_ATTRREAD_LOOPEND;
-
-	// create new texture coordinates object.
-    CAMFImporter_NodeElement *ne = new CAMFImporter_NodeElement_TexMap(mNodeElement_Cur);
+    AMFNodeElementBase *ne = new AMFTexMap(mNodeElement_Cur);
+    AMFTexMap &als = *((AMFTexMap *)ne); //
+    std::string rtexid, gtexid, btexid, atexid;
+    if (!node.empty()) {
+        for (pugi::xml_attribute &attr : node.attributes()) {
+            const std::string &currentAttr = attr.name();
+            if (currentAttr == "rtexid") {
+                rtexid = attr.as_string();
+            } else if (currentAttr == "gtexid") {
+                gtexid = attr.as_string();
+            } else if (currentAttr == "btexid") {
+                btexid = attr.as_string();
+            } else if (currentAttr == "atexid") {
+                atexid = attr.as_string();
+            }
+        }
+    }
 
-	CAMFImporter_NodeElement_TexMap& als = *((CAMFImporter_NodeElement_TexMap*)ne);// alias for convenience
+	// create new texture coordinates object, alias for convenience
 	// check data
-	if(rtexid.empty() && gtexid.empty() && btexid.empty()) throw DeadlyImportError("ParseNode_TexMap. At least one texture ID must be defined.");
+	if (rtexid.empty() && gtexid.empty() && btexid.empty()) {
+		throw DeadlyImportError("ParseNode_TexMap. At least one texture ID must be defined.");
+	}
+
 	// Check for children nodes
-	XML_CheckNode_MustHaveChildren();
+	if (node.children().begin() == node.children().end()) {
+		throw DeadlyImportError("Invalid children definition.");
+	}
 	// read children nodes
 	bool read_flag[6] = { false, false, false, false, false, false };
 
-	ParseHelper_Node_Enter(ne);
-	if(!pUseOldName)
-	{
-		MACRO_NODECHECK_LOOPBEGIN("texmap");
-			MACRO_NODECHECK_READCOMP_F("utex1", read_flag[0], als.TextureCoordinate[0].x);
-			MACRO_NODECHECK_READCOMP_F("utex2", read_flag[1], als.TextureCoordinate[1].x);
-			MACRO_NODECHECK_READCOMP_F("utex3", read_flag[2], als.TextureCoordinate[2].x);
-			MACRO_NODECHECK_READCOMP_F("vtex1", read_flag[3], als.TextureCoordinate[0].y);
-			MACRO_NODECHECK_READCOMP_F("vtex2", read_flag[4], als.TextureCoordinate[1].y);
-			MACRO_NODECHECK_READCOMP_F("vtex3", read_flag[5], als.TextureCoordinate[2].y);
-		MACRO_NODECHECK_LOOPEND("texmap");
+	if (!pUseOldName) {
+        ParseHelper_Node_Enter(ne);
+        for ( XmlNode &currentNode : node.children()) {
+            const std::string &name = currentNode.name();
+            if (name == "utex1") {
+				read_flag[0] = true;
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].x);
+            } else if (name == "utex2") {
+				read_flag[1] = true;
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].x);
+            } else if (name == "utex3") {
+				read_flag[2] = true;
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].x);
+            } else if (name == "vtex1") {
+				read_flag[3] = true;
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].y);
+            } else if (name == "vtex2") {
+				read_flag[4] = true;
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].y);
+            } else if (name == "vtex3") {
+				read_flag[5] = true;
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].y);
+			}
+		}
+        ParseHelper_Node_Exit();
+
+	} else {
+		for (pugi::xml_attribute &attr : node.attributes()) {
+            const std::string name = attr.name();
+            if (name == "u") {
+				read_flag[0] = true;
+				als.TextureCoordinate[0].x = attr.as_float();
+            } else if (name == "u2") {
+				read_flag[1] = true;
+				als.TextureCoordinate[1].x = attr.as_float();
+            } else if (name == "u3") {
+				read_flag[2] = true;
+				als.TextureCoordinate[2].x = attr.as_float();
+            } else if (name == "v1") {
+				read_flag[3] = true;
+				als.TextureCoordinate[0].y = attr.as_float();
+            } else if (name == "v2") {
+				read_flag[4] = true;
+				als.TextureCoordinate[1].y = attr.as_float();
+            } else if (name == "v3") {
+				read_flag[5] = true;
+				als.TextureCoordinate[0].y = attr.as_float();
+			}
+		}
 	}
-	else
-	{
-		MACRO_NODECHECK_LOOPBEGIN("map");
-			MACRO_NODECHECK_READCOMP_F("u1", read_flag[0], als.TextureCoordinate[0].x);
-			MACRO_NODECHECK_READCOMP_F("u2", read_flag[1], als.TextureCoordinate[1].x);
-			MACRO_NODECHECK_READCOMP_F("u3", read_flag[2], als.TextureCoordinate[2].x);
-			MACRO_NODECHECK_READCOMP_F("v1", read_flag[3], als.TextureCoordinate[0].y);
-			MACRO_NODECHECK_READCOMP_F("v2", read_flag[4], als.TextureCoordinate[1].y);
-			MACRO_NODECHECK_READCOMP_F("v3", read_flag[5], als.TextureCoordinate[2].y);
-		MACRO_NODECHECK_LOOPEND("map");
-	}// if(!pUseOldName) else
-
-	ParseHelper_Node_Exit();
 
 	// check that all components was defined
-	if(!(read_flag[0] && read_flag[1] && read_flag[2] && read_flag[3] && read_flag[4] && read_flag[5]))
+	if (!(read_flag[0] && read_flag[1] && read_flag[2] && read_flag[3] && read_flag[4] && read_flag[5])) {
 		throw DeadlyImportError("Not all texture coordinates are defined.");
+	}
 
 	// copy attributes data
 	als.TextureID_R = rtexid;
@@ -321,7 +319,7 @@ void AMFImporter::ParseNode_TexMap(const bool pUseOldName) {
 	als.TextureID_B = btexid;
 	als.TextureID_A = atexid;
 
-	mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
+	mNodeElement_List.push_back(ne);
 }
 
 }// namespace Assimp

+ 305 - 0
Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Node.hpp

@@ -0,0 +1,305 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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_Node.hpp
+/// \brief Elements of scene graph.
+/// \date 2016
+/// \author [email protected]
+
+#pragma once
+#ifndef INCLUDED_AI_AMF_IMPORTER_NODE_H
+#define INCLUDED_AI_AMF_IMPORTER_NODE_H
+
+// Header files, Assimp.
+#include <assimp/scene.h>
+#include <assimp/types.h>
+
+#include <list>
+#include <string>
+#include <vector>
+
+/// \class CAMFImporter_NodeElement
+/// Base class for elements of nodes.
+class AMFNodeElementBase {
+public:
+	/// Define what data type contain node element.
+	enum EType {
+		ENET_Color, ///< Color element: <color>.
+		ENET_Constellation, ///< Grouping element: <constellation>.
+		ENET_Coordinates, ///< Coordinates element: <coordinates>.
+		ENET_Edge, ///< Edge element: <edge>.
+		ENET_Instance, ///< Grouping element: <constellation>.
+		ENET_Material, ///< Material element: <material>.
+		ENET_Metadata, ///< Metadata element: <metadata>.
+		ENET_Mesh, ///< Metadata element: <mesh>.
+		ENET_Object, ///< Element which hold object: <object>.
+		ENET_Root, ///< Root element: <amf>.
+		ENET_Triangle, ///< Triangle element: <triangle>.
+		ENET_TexMap, ///< Texture coordinates element: <texmap> or <map>.
+		ENET_Texture, ///< Texture element: <texture>.
+		ENET_Vertex, ///< Vertex element: <vertex>.
+		ENET_Vertices, ///< Vertex element: <vertices>.
+		ENET_Volume, ///< Volume element: <volume>.
+
+		ENET_Invalid ///< Element has invalid type and possible contain invalid data.
+	};
+
+	const EType Type; ///< Type of element.
+	std::string ID; ///< ID of element.
+	AMFNodeElementBase *Parent; ///< Parent element. If nullptr then this node is root.
+	std::list<AMFNodeElementBase *> Child; ///< Child elements.
+
+public: /// Destructor, virtual..
+	virtual ~AMFNodeElementBase() {
+		// empty
+	}
+
+	/// Disabled copy constructor and co.
+	AMFNodeElementBase(const AMFNodeElementBase &pNodeElement) = delete;
+	AMFNodeElementBase(AMFNodeElementBase &&) = delete;
+	AMFNodeElementBase &operator=(const AMFNodeElementBase &pNodeElement) = delete;
+	AMFNodeElementBase() = delete;
+
+protected:
+	/// In constructor inheritor must set element type.
+	/// \param [in] pType - element type.
+	/// \param [in] pParent - parent element.
+	AMFNodeElementBase(const EType pType, AMFNodeElementBase *pParent) :
+			Type(pType), ID(), Parent(pParent), Child() {
+		// empty
+	}
+}; // class IAMFImporter_NodeElement
+
+/// \struct CAMFImporter_NodeElement_Constellation
+/// A collection of objects or constellations with specific relative locations.
+struct AMFConstellation : public AMFNodeElementBase {
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFConstellation(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Constellation, pParent) {}
+
+}; // struct CAMFImporter_NodeElement_Constellation
+
+/// \struct CAMFImporter_NodeElement_Instance
+/// Part of constellation.
+struct AMFInstance : public AMFNodeElementBase {
+
+	std::string ObjectID; ///< ID of object for instantiation.
+	/// \var Delta - The distance of translation in the x, y, or z direction, respectively, in the referenced object's coordinate system, to
+	/// create an instance of the object in the current constellation.
+	aiVector3D Delta;
+
+	/// \var Rotation - The rotation, in degrees, to rotate the referenced object about its x, y, and z axes, respectively, to create an
+	/// instance of the object in the current constellation. Rotations shall be executed in order of x first, then y, then z.
+	aiVector3D Rotation;
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFInstance(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Instance, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Metadata
+/// Structure that define metadata node.
+struct AMFMetadata : public AMFNodeElementBase {
+
+	std::string Type; ///< Type of "Value".
+	std::string Value; ///< Value.
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFMetadata(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Metadata, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Root
+/// Structure that define root node.
+struct AMFRoot : public AMFNodeElementBase {
+
+	std::string Unit; ///< The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
+	std::string Version; ///< Version of format.
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFRoot(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Root, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Color
+/// Structure that define object node.
+struct AMFColor : public AMFNodeElementBase {
+	bool Composed; ///< Type of color stored: if true then look for formula in \ref Color_Composed[4], else - in \ref Color.
+	std::string Color_Composed[4]; ///< By components formulas of composed color. [0..3] - RGBA.
+	aiColor4D Color; ///< Constant color.
+	std::string Profile; ///< The ICC color space used to interpret the three color channels r, g and b..
+
+	/// @brief  Constructor.
+	/// @param [in] pParent - pointer to parent node.
+	AMFColor(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Color, pParent), Composed(false), Color(), Profile() {
+		// empty
+	}
+};
+
+/// \struct CAMFImporter_NodeElement_Material
+/// Structure that define material node.
+struct AMFMaterial : public AMFNodeElementBase {
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFMaterial(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Material, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Object
+/// Structure that define object node.
+struct AMFObject : public AMFNodeElementBase {
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFObject(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Object, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Mesh
+/// Structure that define mesh node.
+struct AMFMesh : public AMFNodeElementBase {
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFMesh(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Mesh, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Vertex
+/// Structure that define vertex node.
+struct AMFVertex : public AMFNodeElementBase {
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFVertex(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Vertex, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Edge
+/// Structure that define edge node.
+struct AMFEdge : public AMFNodeElementBase {
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFEdge(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Edge, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Vertices
+/// Structure that define vertices node.
+struct AMFVertices : public AMFNodeElementBase {
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFVertices(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Vertices, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Volume
+/// Structure that define volume node.
+struct AMFVolume : public AMFNodeElementBase {
+	std::string MaterialID; ///< Which material to use.
+	std::string Type; ///< What this volume describes can be "region" or "support". If none specified, "object" is assumed.
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFVolume(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Volume, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_Coordinates
+/// Structure that define coordinates node.
+struct AMFCoordinates : public AMFNodeElementBase {
+	aiVector3D Coordinate; ///< Coordinate.
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFCoordinates(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Coordinates, pParent) {}
+};
+
+/// \struct CAMFImporter_NodeElement_TexMap
+/// Structure that define texture coordinates node.
+struct AMFTexMap : public AMFNodeElementBase {
+	aiVector3D TextureCoordinate[3]; ///< Texture coordinates.
+	std::string TextureID_R; ///< Texture ID for red color component.
+	std::string TextureID_G; ///< Texture ID for green color component.
+	std::string TextureID_B; ///< Texture ID for blue color component.
+	std::string TextureID_A; ///< Texture ID for alpha color component.
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFTexMap(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_TexMap, pParent), TextureCoordinate{}, TextureID_R(), TextureID_G(), TextureID_B(), TextureID_A() {
+		// empty
+	}
+};
+
+/// \struct CAMFImporter_NodeElement_Triangle
+/// Structure that define triangle node.
+struct AMFTriangle : public AMFNodeElementBase {
+	size_t V[3]; ///< Triangle vertices.
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFTriangle(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Triangle, pParent) {
+		// empty
+	}
+};
+
+/// Structure that define texture node.
+struct AMFTexture : public AMFNodeElementBase {
+	size_t Width, Height, Depth; ///< Size of the texture.
+	std::vector<uint8_t> Data; ///< Data of the texture.
+	bool Tiled;
+
+	/// Constructor.
+	/// \param [in] pParent - pointer to parent node.
+	AMFTexture(AMFNodeElementBase *pParent) :
+			AMFNodeElementBase(ENET_Texture, pParent), Width(0), Height(0), Depth(0), Data(), Tiled(false) {
+		// empty
+	}
+};
+
+#endif // INCLUDED_AI_AMF_IMPORTER_NODE_H

+ 894 - 0
Engine/lib/assimp/code/AssetLib/AMF/AMFImporter_Postprocess.cpp

@@ -0,0 +1,894 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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"
+
+#include <assimp/SceneCombiner.h>
+#include <assimp/StandardShapes.h>
+#include <assimp/StringUtils.h>
+
+#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");
+    }
+
+    if (Color->Composed) {
+        throw DeadlyImportError("IME. GetColor, composed color");
+    }
+
+    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 AMFMesh &nodeElement, std::vector<aiVector3D> &vertexCoordinateArray,
+        std::vector<AMFColor *> &pVertexColorArray) const {
+    AMFVertices  *vn = nullptr;
+    size_t col_idx;
+
+    // All data stored in "vertices", search for it.
+    for (AMFNodeElementBase *ne_child : nodeElement.Child) {
+        if (ne_child->Type == AMFNodeElementBase::ENET_Vertices) {
+            vn = (AMFVertices*)ne_child;
+        }
+    }
+
+    // If "vertices" not found then no work for us.
+    if (vn == nullptr) {
+        return;
+    }
+
+    // all coordinates stored as child and we need to reserve space for future push_back's.
+    vertexCoordinateArray.reserve(vn->Child.size());
+
+    // colors count equal vertices count.
+    pVertexColorArray.resize(vn->Child.size());
+    col_idx = 0;
+
+    // Inside vertices collect all data and place to arrays
+    for (AMFNodeElementBase *vn_child : vn->Child) {
+        // vertices, colors
+        if (vn_child->Type == AMFNodeElementBase::ENET_Vertex) {
+            // by default clear color for current vertex
+            pVertexColorArray[col_idx] = nullptr;
+
+            for (AMFNodeElementBase *vtx : vn_child->Child) {
+                if (vtx->Type == AMFNodeElementBase::ENET_Coordinates) {
+                    vertexCoordinateArray.push_back(((AMFCoordinates *)vtx)->Coordinate);
+                    continue;
+                }
+
+                if (vtx->Type == AMFNodeElementBase::ENET_Color) {
+                    pVertexColorArray[col_idx] = (AMFColor *)vtx;
+                    continue;
+                }
+            }
+
+            ++col_idx;
+        }
+    }
+}
+
+size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &r, const std::string &g, const std::string &b, const std::string &a) {
+    if (r.empty() && g.empty() && b.empty() && a.empty()) {
+        throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined.");
+    }
+
+    std::string TextureConverted_ID = r + "_" + g + "_" + b + "_" + a;
+    size_t 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.
+    AMFTexture *src_texture[4] {
+        nullptr
+    };
+    std::vector<AMFTexture *> src_texture_4check;
+    SPP_Texture converted_texture;
+
+    { // find all specified source textures
+        AMFNodeElementBase *t_tex = nullptr;
+
+        // R
+        if (!r.empty()) {
+            if (!Find_NodeElement(r, AMFNodeElementBase::EType::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(r);
+            }
+
+            src_texture[0] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)t_tex);
+        } else {
+            src_texture[0] = nullptr;
+        }
+
+        // G
+        if (!g.empty()) {
+            if (!Find_NodeElement(g, AMFNodeElementBase::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(g);
+            }
+
+            src_texture[1] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)t_tex);
+        } else {
+            src_texture[1] = nullptr;
+        }
+
+        // B
+        if (!b.empty()) {
+            if (!Find_NodeElement(b, AMFNodeElementBase::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(b);
+            }
+
+            src_texture[2] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)t_tex);
+        } else {
+            src_texture[2] = nullptr;
+        }
+
+        // A
+        if (!a.empty()) {
+            if (!Find_NodeElement(a, AMFNodeElementBase::ENET_Texture, &t_tex)) {
+                Throw_ID_NotFound(a);
+            }
+
+            src_texture[3] = (AMFTexture *)t_tex;
+            src_texture_4check.push_back((AMFTexture *)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 (!r.empty()) converted_texture.FormatHint[4] = '8';
+    if (!g.empty()) converted_texture.FormatHint[5] = '8';
+    if (!b.empty()) converted_texture.FormatHint[6] = '8';
+    if (!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 (!r.empty() && nullptr != src_texture[0]) {
+        tex_size += src_texture[0]->Data.size();
+        step++, off_g++, off_b++;
+    }
+    if (!g.empty() && nullptr != src_texture[1]) {
+        tex_size += src_texture[1]->Data.size();
+        step++, off_b++;
+    }
+    if (!b.empty() && nullptr != src_texture[2]) {
+        tex_size += src_texture[2]->Data.size();
+        step++;
+    }
+    if (!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++) {
+                AMFTexture *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(r, 0, step, 0);
+    CopyTextureData(g, off_g, step, 1);
+    CopyTextureData(b, off_b, step, 2);
+    CopyTextureData(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 AMFTexMap *pTexMap1, const AMFTexMap *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 AMFMetaDataArray &metadataList, aiNode &sceneNode) const {
+    if (metadataList.empty()) {
+        return;
+    }
+
+    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 AMFMetadata *metadata : metadataList) {
+        sceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx++), metadata->Type, aiString(metadata->Value));
+    }
+}
+
+void AMFImporter::Postprocess_BuildNodeAndObject(const AMFObject &pNodeElement, MeshArray &meshList, aiNode **pSceneNode) {
+    AMFColor *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 AMFNodeElementBase *ne_child : pNodeElement.Child) {
+        std::vector<aiVector3D> vertex_arr;
+        std::vector<AMFColor *> color_arr;
+
+        // color for object
+        if (ne_child->Type == AMFNodeElementBase::ENET_Color) {
+            object_color = (AMFColor *) ne_child;
+        }
+
+        if (ne_child->Type == AMFNodeElementBase::ENET_Mesh) {
+            // Create arrays from children of mesh: vertices.
+            PostprocessHelper_CreateMeshDataArray(*((AMFMesh *)ne_child), vertex_arr, color_arr);
+            // Use this arrays as a source when creating every aiMesh
+            Postprocess_BuildMeshSet(*((AMFMesh *)ne_child), vertex_arr, color_arr, object_color, meshList, **pSceneNode);
+        }
+    } // for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
+}
+
+void AMFImporter::Postprocess_BuildMeshSet(const AMFMesh &pNodeElement, const std::vector<aiVector3D> &pVertexCoordinateArray,
+        const std::vector<AMFColor *> &pVertexColorArray, const AMFColor *pObjectColor, MeshArray &pMeshList, aiNode &pSceneNode) {
+    std::list<unsigned int> mesh_idx;
+
+    // all data stored in "volume", search for it.
+    for (const AMFNodeElementBase *ne_child : pNodeElement.Child) {
+        const AMFColor *ne_volume_color = nullptr;
+        const SPP_Material *cur_mat = nullptr;
+
+        if (ne_child->Type == AMFNodeElementBase::ENET_Volume) {
+            /******************* Get faces *******************/
+            const AMFVolume *ne_volume = reinterpret_cast<const AMFVolume *>(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 AMFNodeElementBase *ne_volume_child : ne_volume->Child) {
+                // color for volume
+                if (ne_volume_child->Type == AMFNodeElementBase::ENET_Color) {
+                    ne_volume_color = reinterpret_cast<const AMFColor *>(ne_volume_child);
+                } else if (ne_volume_child->Type == AMFNodeElementBase::ENET_Triangle) // triangles, triangles colors
+                {
+                    const AMFTriangle &tri_al = *reinterpret_cast<const AMFTriangle *>(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 AMFNodeElementBase *ne_triangle_child : tri_al.Child) {
+                            if (ne_triangle_child->Type == AMFNodeElementBase::ENET_Color)
+                                complex_face.Color = reinterpret_cast<const AMFColor *>(ne_triangle_child);
+                            else if (ne_triangle_child->Type == AMFNodeElementBase::ENET_TexMap)
+                                complex_face.TexMap = reinterpret_cast<const AMFTexMap *>(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;
+                        const size_t biggerThan = *pBiggerThan;
+                        for (const SComplexFace &face : pFaceList) {
+                            for (size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++) {
+                                if (face.Face.mIndices[idx_vert] > biggerThan) {
+                                    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. Optimization 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, optimization step transform 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
+                    /// optimization.
+                    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 AMFMaterial &pMaterial) {
+    SPP_Material new_mat;
+
+    new_mat.ID = pMaterial.ID;
+    for (const AMFNodeElementBase *mat_child : pMaterial.Child) {
+        if (mat_child->Type == AMFNodeElementBase::ENET_Color) {
+            new_mat.Color = (AMFColor*)mat_child;
+        } else if (mat_child->Type == AMFNodeElementBase::ENET_Metadata) {
+            new_mat.Metadata.push_back((AMFMetadata *)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(AMFConstellation &pConstellation, NodeArray &nodeArray) 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 AMFNodeElementBase *ne : pConstellation.Child) {
+        aiMatrix4x4 tmat;
+        aiNode *t_node;
+        aiNode *found_node;
+
+        if (ne->Type == AMFNodeElementBase::ENET_Metadata) continue;
+        if (ne->Type != AMFNodeElementBase::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
+
+        // create alias for convenience
+        AMFInstance &als = *((AMFInstance *)ne);
+        // find referenced object
+        if (!Find_ConvertedNode(als.ObjectID, nodeArray, &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
+    nodeArray.push_back(con_node);
+}
+
+void AMFImporter::Postprocess_BuildScene(aiScene *pScene) {
+    NodeArray nodeArray;
+    MeshArray mesh_list;
+    AMFMetaDataArray 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
+    AMFNodeElementBase *root_el = nullptr;
+
+    for (AMFNodeElementBase *ne : mNodeElement_List) {
+        if (ne->Type != AMFNodeElementBase::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 AMFNodeElementBase *root_child : root_el->Child) {
+        if (root_child->Type == AMFNodeElementBase::ENET_Material) {
+            Postprocess_BuildMaterial(*((AMFMaterial *)root_child));
+        }
+    }
+
+    // After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
+    //
+    // 3. <object>
+    for (const AMFNodeElementBase *root_child : root_el->Child) {
+        if (root_child->Type == AMFNodeElementBase::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(*((AMFObject *)root_child), mesh_list, &tnode);
+            if (tnode != nullptr) {
+                nodeArray.push_back(tnode);
+            }
+        }
+    } // for(const CAMFImporter_NodeElement* root_child: root_el->Child)
+
+    // And finally read rest of nodes.
+    //
+    for (const AMFNodeElementBase *root_child : root_el->Child) {
+        // 4. <constellation>
+        if (root_child->Type == AMFNodeElementBase::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(*((AMFConstellation *)root_child), nodeArray);
+        }
+
+        // 5, <metadata>
+        if (root_child->Type == AMFNodeElementBase::ENET_Metadata) meta_list.push_back((AMFMetadata *)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 (nodeArray.size() > 1) {
+        // walk through all nodes
+        for (NodeArray::iterator nl_it = nodeArray.begin(); nl_it != nodeArray.end(); ++nl_it) {
+            // and try to find them in another top nodes.
+            NodeArray::const_iterator next_it = nl_it;
+
+            ++next_it;
+            for (; next_it != nodeArray.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.
+                    nodeArray.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 (!nodeArray.empty()) {
+        NodeArray::const_iterator nl_it = nodeArray.begin();
+
+        pScene->mRootNode->mNumChildren = static_cast<unsigned int>(nodeArray.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()) {
+        MeshArray::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 + ai_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

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 273 - 299
Engine/lib/assimp/code/AssetLib/ASE/ASELoader.cpp


+ 6 - 21
Engine/lib/assimp/code/ASE/ASELoader.h → Engine/lib/assimp/code/AssetLib/ASE/ASELoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -62,42 +62,37 @@ namespace Assimp {
 class ASEImporter : public BaseImporter {
 public:
     ASEImporter();
-    ~ASEImporter();
+    ~ASEImporter() override;
 
     // -------------------------------------------------------------------
     /** 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 checkSig) const override;
 
 protected:
-
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
      */
-    const aiImporterDesc* GetInfo () const;
-
+    const aiImporterDesc* GetInfo () const override;
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure.
     * See BaseImporter::InternReadFile() for details
     */
     void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
-
+        IOSystem* pIOHandler) override;
 
     // -------------------------------------------------------------------
     /** 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) override;
 
 private:
-
     // -------------------------------------------------------------------
     /** Generate normal vectors basing on smoothing groups
      * (in some cases the normal are already contained in the file)
@@ -106,7 +101,6 @@ private:
      */
     bool GenerateNormals(ASE::Mesh& mesh);
 
-
     // -------------------------------------------------------------------
     /** Create valid vertex/normal/UV/color/face lists.
      *  All elements are unique, faces have only one set of indices
@@ -115,51 +109,43 @@ private:
      */
     void BuildUniqueRepresentation(ASE::Mesh& mesh);
 
-
     /** Create one-material-per-mesh meshes ;-)
      * \param mesh Mesh to work with
      *  \param Receives the list of all created meshes
      */
     void ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOut);
 
-
     // -------------------------------------------------------------------
     /** Convert a material to a aiMaterial object
      * \param mat Input material
      */
     void ConvertMaterial(ASE::Material& mat);
 
-
     // -------------------------------------------------------------------
     /** Setup the final material indices for each mesh
      */
     void BuildMaterialIndices();
 
-
     // -------------------------------------------------------------------
     /** Build the node graph
      */
     void BuildNodes(std::vector<ASE::BaseNode*>& nodes);
 
-
     // -------------------------------------------------------------------
     /** Build output cameras
      */
     void BuildCameras();
 
-
     // -------------------------------------------------------------------
     /** Build output lights
      */
     void BuildLights();
 
-
     // -------------------------------------------------------------------
     /** Build output animations
      */
     void BuildAnimations(const std::vector<ASE::BaseNode*>& nodes);
 
-
     // -------------------------------------------------------------------
     /** Add sub nodes to a node
      *  \param pcParent parent node to be filled
@@ -183,7 +169,6 @@ private:
     void GenerateDefaultMaterial();
 
 protected:
-
     /** Parser instance */
     ASE::Parser* mParser;
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 253 - 376
Engine/lib/assimp/code/AssetLib/ASE/ASEParser.cpp


+ 109 - 126
Engine/lib/assimp/code/ASE/ASEParser.h → Engine/lib/assimp/code/AssetLib/ASE/ASEParser.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -40,15 +40,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-
 /** @file Defines the helper data structures for importing ASE files  */
 #ifndef AI_ASEFILEHELPER_H_INC
 #define AI_ASEFILEHELPER_H_INC
 
 // public ASSIMP headers
-#include <assimp/types.h>
-#include <assimp/mesh.h>
 #include <assimp/anim.h>
+#include <assimp/mesh.h>
+#include <assimp/types.h>
 
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
@@ -57,68 +56,73 @@ 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   {
+namespace Assimp {
+namespace ASE {
 
 using namespace D3DS;
 
 // ---------------------------------------------------------------------------
 /** Helper structure representing an ASE material */
-struct Material : public D3DS::Material
-{
+struct Material : public D3DS::Material {
     //! Default constructor has been deleted
     Material() = delete;
 
     //! Constructor with explicit name
-    explicit Material(const std::string &name)
-    : D3DS::Material(name)
-    , pcInstance(NULL)
-    , bNeed (false) {
+    explicit Material(const std::string &name) :
+            D3DS::Material(name),
+            pcInstance(nullptr),
+            bNeed(false) {
         // empty
     }
 
-    Material(const Material &other)            = default;
-    Material &operator=(const Material &other) = default;
+    Material(const Material &other) = default;
+
+    Material &operator=(const Material &other) {
+        if (this == &other) {
+            return *this;
+        }
 
+        avSubMaterials = other.avSubMaterials;
+        pcInstance = other.pcInstance;
+        bNeed = other.bNeed;
+
+        return *this;
+    }
 
     //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it
     Material(Material &&other) AI_NO_EXCEPT
-    : D3DS::Material(std::move(other))
-    , avSubMaterials(std::move(other.avSubMaterials))
-    , pcInstance(std::move(other.pcInstance))
-    , bNeed(std::move(other.bNeed))
-    {
+            : D3DS::Material(std::move(other)),
+              avSubMaterials(std::move(other.avSubMaterials)),
+              pcInstance(other.pcInstance),
+              bNeed(other.bNeed) {
         other.pcInstance = nullptr;
     }
 
-
     Material &operator=(Material &&other) AI_NO_EXCEPT {
         if (this == &other) {
             return *this;
         }
 
-        D3DS::Material::operator=(std::move(other));
+        //D3DS::Material::operator=(std::move(other));
 
         avSubMaterials = std::move(other.avSubMaterials);
-        pcInstance = std::move(other.pcInstance);
-        bNeed = std::move(other.bNeed);
+        pcInstance = other.pcInstance;
+        bNeed = other.bNeed;
 
         other.pcInstance = nullptr;
 
         return *this;
     }
 
-
     ~Material() {}
 
-
     //! Contains all sub materials of this material
     std::vector<Material> avSubMaterials;
 
     //! aiMaterial object
-    aiMaterial* pcInstance;
+    aiMaterial *pcInstance;
 
     //! Can we remove this material?
     bool bNeed;
@@ -129,8 +133,8 @@ struct Material : public D3DS::Material
 struct Face : public FaceWithSmoothingGroup {
     //! Default constructor. Initializes everything with 0
     Face() AI_NO_EXCEPT
-    : iMaterial(DEFAULT_MATINDEX)
-    , iFace(0) {
+            : iMaterial(DEFAULT_MATINDEX),
+              iFace(0) {
         // empty
     }
 
@@ -161,8 +165,8 @@ struct Bone {
     Bone() = delete;
 
     //! Construction from an existing name
-    explicit Bone( const std::string& name)
-    : mName(name) {
+    explicit Bone(const std::string &name) :
+            mName(name) {
         // empty
     }
 
@@ -175,33 +179,34 @@ struct Bone {
 struct BoneVertex {
     //! Bone and corresponding vertex weight.
     //! -1 for unrequired bones ....
-    std::vector<std::pair<int,float> > mBoneWeights;
+    std::vector<std::pair<int, float>> mBoneWeights;
 };
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE file animation */
 struct Animation {
     enum Type {
-        TRACK   = 0x0,
-        BEZIER  = 0x1,
-        TCB     = 0x2
-    } mRotationType, mScalingType, mPositionType;
+        TRACK = 0x0,
+        BEZIER = 0x1,
+        TCB = 0x2
+    } mRotationType,
+            mScalingType, mPositionType;
 
     Animation() AI_NO_EXCEPT
-    :   mRotationType   (TRACK)
-    ,   mScalingType    (TRACK)
-    ,   mPositionType   (TRACK) {
+            : mRotationType(TRACK),
+              mScalingType(TRACK),
+              mPositionType(TRACK) {
         // empty
     }
 
     //! List of track rotation keyframes
-    std::vector< aiQuatKey > akeyRotations;
+    std::vector<aiQuatKey> akeyRotations;
 
     //! List of track position keyframes
-    std::vector< aiVectorKey > akeyPositions;
+    std::vector<aiVectorKey> akeyPositions;
 
     //! List of track scaling keyframes
-    std::vector< aiVectorKey > akeyScaling;
+    std::vector<aiVectorKey> akeyScaling;
 };
 
 // ---------------------------------------------------------------------------
@@ -209,7 +214,7 @@ struct Animation {
 struct InheritanceInfo {
     //! Default constructor
     InheritanceInfo() AI_NO_EXCEPT {
-        for ( size_t i=0; i<3; ++i ) {
+        for (size_t i = 0; i < 3; ++i) {
             abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true;
         }
     }
@@ -228,17 +233,15 @@ struct InheritanceInfo {
 /** Represents an ASE file node. Base class for mesh, light and cameras */
 struct BaseNode {
     enum Type {
-        Light, 
-        Camera, 
-        Mesh, 
+        Light,
+        Camera,
+        Mesh,
         Dummy
     } mType;
 
     //! Construction from an existing name
-    BaseNode(Type _mType, const std::string &name)
-    : mType         (_mType)
-    , mName         (name)
-    , mProcessed    (false) {
+    BaseNode(Type _mType, const std::string &name) :
+            mType(_mType), mName(name), mProcessed(false) {
         // Set mTargetPosition to qnan
         const ai_real qnan = get_qnan();
         mTargetPosition.x = qnan;
@@ -278,14 +281,9 @@ struct Mesh : public MeshWithSmoothingGroups<ASE::Face>, public BaseNode {
     Mesh() = delete;
 
     //! Construction from an existing name
-    explicit Mesh(const std::string &name)
-    : BaseNode( BaseNode::Mesh, name )
-    , mVertexColors()
-    , mBoneVertices()
-    , mBones()
-    , iMaterialIndex(Face::DEFAULT_MATINDEX)
-    , bSkip     (false) {
-        for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+    explicit Mesh(const std::string &name) :
+            BaseNode(BaseNode::Mesh, name), mVertexColors(), mBoneVertices(), mBones(), iMaterialIndex(Face::DEFAULT_MATINDEX), bSkip(false) {
+        for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c) {
             this->mNumUVComponents[c] = 2;
         }
     }
@@ -314,10 +312,8 @@ struct Mesh : public MeshWithSmoothingGroups<ASE::Face>, public BaseNode {
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE light source */
-struct Light : public BaseNode
-{
-    enum LightType
-    {
+struct Light : public BaseNode {
+    enum LightType {
         OMNI,
         TARGET,
         FREE,
@@ -328,17 +324,13 @@ struct Light : public BaseNode
     Light() = delete;
 
     //! Construction from an existing name
-    explicit Light(const std::string &name)
-    : BaseNode   (BaseNode::Light, name)
-    , mLightType (OMNI)
-    , mColor     (1.f,1.f,1.f)
-    , mIntensity (1.f) // light is white by default
-    , mAngle     (45.f)
-    , mFalloff   (0.f)
-    {
+    explicit Light(const std::string &name) :
+            BaseNode(BaseNode::Light, name), mLightType(OMNI), mColor(1.f, 1.f, 1.f), mIntensity(1.f) // light is white by default
+            ,
+            mAngle(45.f),
+            mFalloff(0.f) {
     }
 
-
     LightType mLightType;
     aiColor3D mColor;
     ai_real mIntensity;
@@ -348,10 +340,8 @@ struct Light : public BaseNode
 
 // ---------------------------------------------------------------------------
 /** Helper structure to represent an ASE camera */
-struct Camera : public BaseNode
-{
-    enum CameraType
-    {
+struct Camera : public BaseNode {
+    enum CameraType {
         FREE,
         TARGET
     };
@@ -359,18 +349,16 @@ struct Camera : public BaseNode
     //! Default constructor has been deleted
     Camera() = delete;
 
-
     //! Construction from an existing name
-    explicit Camera(const std::string &name)
-    : BaseNode    (BaseNode::Camera, name)
-    , mFOV        (0.75f)   // in radians
-    , mNear       (0.1f)
-    , mFar        (1000.f)  // could be zero
-    , mCameraType (FREE)
-    {
+    explicit Camera(const std::string &name) :
+            BaseNode(BaseNode::Camera, name), mFOV(0.75f) // in radians
+            ,
+            mNear(0.1f),
+            mFar(1000.f) // could be zero
+            ,
+            mCameraType(FREE) {
     }
 
-
     ai_real mFOV, mNear, mFar;
     CameraType mCameraType;
 };
@@ -380,7 +368,7 @@ struct Camera : public BaseNode
 struct Dummy : public BaseNode {
     //! Constructor
     Dummy() AI_NO_EXCEPT
-    : BaseNode  (BaseNode::Dummy, "DUMMY") {
+            : BaseNode(BaseNode::Dummy, "DUMMY") {
         // empty
     }
 };
@@ -403,7 +391,6 @@ private:
     }
 
 public:
-
     // -------------------------------------------------------------------
     //! Construct a parser from a given input file which is
     //! guaranteed to be terminated with zero.
@@ -411,15 +398,13 @@ public:
     //! @param fileFormatDefault Assumed file format version. If the
     //!   file format is specified in the file the new value replaces
     //!   the default value.
-    Parser (const char* szFile, unsigned int fileFormatDefault);
+    Parser(const char *szFile, unsigned int fileFormatDefault);
 
     // -------------------------------------------------------------------
     //! Parses the file into the parsers internal representation
     void Parse();
 
-
 private:
-
     // -------------------------------------------------------------------
     //! Parse the *SCENE block in a file
     void ParseLV1SceneBlock();
@@ -435,45 +420,45 @@ private:
     // -------------------------------------------------------------------
     //! Parse a *<xxx>OBJECT block in a file
     //! \param mesh Node to be filled
-    void ParseLV1ObjectBlock(BaseNode& mesh);
+    void ParseLV1ObjectBlock(BaseNode &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MATERIAL blocks in a material list
     //! \param mat Material structure to be filled
-    void ParseLV2MaterialBlock(Material& mat);
+    void ParseLV2MaterialBlock(Material &mat);
 
     // -------------------------------------------------------------------
     //! Parse a *NODE_TM block in a file
     //! \param mesh Node (!) object to be filled
-    void ParseLV2NodeTransformBlock(BaseNode& mesh);
+    void ParseLV2NodeTransformBlock(BaseNode &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *TM_ANIMATION block in a file
     //! \param mesh Mesh object to be filled
-    void ParseLV2AnimationBlock(BaseNode& mesh);
-    void ParseLV3PosAnimationBlock(ASE::Animation& anim);
-    void ParseLV3ScaleAnimationBlock(ASE::Animation& anim);
-    void ParseLV3RotAnimationBlock(ASE::Animation& anim);
+    void ParseLV2AnimationBlock(BaseNode &mesh);
+    void ParseLV3PosAnimationBlock(ASE::Animation &anim);
+    void ParseLV3ScaleAnimationBlock(ASE::Animation &anim);
+    void ParseLV3RotAnimationBlock(ASE::Animation &anim);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH block in a file
     //! \param mesh Mesh object to be filled
-    void ParseLV2MeshBlock(Mesh& mesh);
+    void ParseLV2MeshBlock(Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *LIGHT_SETTINGS block in a file
     //! \param light Light object to be filled
-    void ParseLV2LightSettingsBlock(Light& light);
+    void ParseLV2LightSettingsBlock(Light &light);
 
     // -------------------------------------------------------------------
     //! Parse a *CAMERA_SETTINGS block in a file
     //! \param cam Camera object to be filled
-    void ParseLV2CameraSettingsBlock(Camera& cam);
+    void ParseLV2CameraSettingsBlock(Camera &cam);
 
     // -------------------------------------------------------------------
     //! Parse the *MAP_XXXXXX blocks in a material
     //! \param map Texture structure to be filled
-    void ParseLV3MapBlock(Texture& map);
+    void ParseLV3MapBlock(Texture &map);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_VERTEX_LIST block in a file
@@ -482,7 +467,7 @@ private:
     //! A warning is sent to the logger if the validations fails.
     //! \param mesh Mesh object to be filled
     void ParseLV3MeshVertexListBlock(
-        unsigned int iNumVertices,Mesh& mesh);
+            unsigned int iNumVertices, Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_FACE_LIST block in a file
@@ -491,7 +476,7 @@ private:
     //! A warning is sent to the logger if the validations fails.
     //! \param mesh Mesh object to be filled
     void ParseLV3MeshFaceListBlock(
-        unsigned int iNumFaces,Mesh& mesh);
+            unsigned int iNumFaces, Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_TVERT_LIST block in a file
@@ -501,7 +486,7 @@ private:
     //! \param mesh Mesh object to be filled
     //! \param iChannel Output UVW channel
     void ParseLV3MeshTListBlock(
-        unsigned int iNumVertices,Mesh& mesh, unsigned int iChannel = 0);
+            unsigned int iNumVertices, Mesh &mesh, unsigned int iChannel = 0);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_TFACELIST block in a file
@@ -511,7 +496,7 @@ private:
     //! \param mesh Mesh object to be filled
     //! \param iChannel Output UVW channel
     void ParseLV3MeshTFaceListBlock(
-        unsigned int iNumFaces,Mesh& mesh, unsigned int iChannel = 0);
+            unsigned int iNumFaces, Mesh &mesh, unsigned int iChannel = 0);
 
     // -------------------------------------------------------------------
     //! Parse an additional mapping channel
@@ -519,7 +504,7 @@ private:
     //! \param iChannel Channel index to be filled
     //! \param mesh Mesh object to be filled
     void ParseLV3MappingChannel(
-        unsigned int iChannel, Mesh& mesh);
+            unsigned int iChannel, Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_CVERTLIST block in a file
@@ -528,7 +513,7 @@ private:
     //! A warning is sent to the logger if the validations fails.
     //! \param mesh Mesh object to be filled
     void ParseLV3MeshCListBlock(
-        unsigned int iNumVertices, Mesh& mesh);
+            unsigned int iNumVertices, Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_CFACELIST block in a file
@@ -537,70 +522,70 @@ private:
     //! A warning is sent to the logger if the validations fails.
     //! \param mesh Mesh object to be filled
     void ParseLV3MeshCFaceListBlock(
-        unsigned int iNumFaces, Mesh& mesh);
+            unsigned int iNumFaces, Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_NORMALS block in a file
     //! \param mesh Mesh object to be filled
-    void ParseLV3MeshNormalListBlock(Mesh& mesh);
+    void ParseLV3MeshNormalListBlock(Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_WEIGHTSblock in a file
     //! \param mesh Mesh object to be filled
-    void ParseLV3MeshWeightsBlock(Mesh& mesh);
+    void ParseLV3MeshWeightsBlock(Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse the bone list of a file
     //! \param mesh Mesh object to be filled
     //! \param iNumBones Number of bones in the mesh
-    void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh);
+    void ParseLV4MeshBones(unsigned int iNumBones, Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse the bone vertices list of a file
     //! \param mesh Mesh object to be filled
     //! \param iNumVertices Number of vertices to be parsed
-    void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh);
+    void ParseLV4MeshBonesVertices(unsigned int iNumVertices, Mesh &mesh);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_FACE block in a file
     //! \param out receive the face data
-    void ParseLV4MeshFace(ASE::Face& out);
+    void ParseLV4MeshFace(ASE::Face &out);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_VERT block in a file
     //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL  ...)
     //! \param apOut Output buffer (3 floats)
     //! \param rIndexOut Output index
-    void ParseLV4MeshFloatTriple(ai_real* apOut, unsigned int& rIndexOut);
+    void ParseLV4MeshFloatTriple(ai_real *apOut, unsigned int &rIndexOut);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_VERT block in a file
     //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL  ...)
     //! \param apOut Output buffer (3 floats)
-    void ParseLV4MeshFloatTriple(ai_real* apOut);
+    void ParseLV4MeshFloatTriple(ai_real *apOut);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_TFACE block in a file
     //! (also works for MESH_CFACE)
     //! \param apOut Output buffer (3 ints)
     //! \param rIndexOut Output index
-    void ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut);
+    void ParseLV4MeshLongTriple(unsigned int *apOut, unsigned int &rIndexOut);
 
     // -------------------------------------------------------------------
     //! Parse a *MESH_TFACE block in a file
     //! (also works for MESH_CFACE)
     //! \param apOut Output buffer (3 ints)
-    void ParseLV4MeshLongTriple(unsigned int* apOut);
+    void ParseLV4MeshLongTriple(unsigned int *apOut);
 
     // -------------------------------------------------------------------
     //! Parse a single float element
     //! \param fOut Output float
-    void ParseLV4MeshFloat(ai_real& fOut);
+    void ParseLV4MeshFloat(ai_real &fOut);
 
     // -------------------------------------------------------------------
     //! Parse a single int element
     //! \param iOut Output integer
-    void ParseLV4MeshLong(unsigned int& iOut);
+    void ParseLV4MeshLong(unsigned int &iOut);
 
     // -------------------------------------------------------------------
     //! Skip everything to the next: '*' or '\0'
@@ -614,17 +599,17 @@ private:
     // -------------------------------------------------------------------
     //! Output a warning to the logger
     //! \param szWarn Warn message
-    void LogWarning(const char* szWarn);
+    void LogWarning(const char *szWarn);
 
     // -------------------------------------------------------------------
     //! Output a message to the logger
     //! \param szWarn Message
-    void LogInfo(const char* szWarn);
+    void LogInfo(const char *szWarn);
 
     // -------------------------------------------------------------------
     //! Output an error to the logger
     //! \param szWarn Error message
-    AI_WONT_RETURN void LogError(const char* szWarn) AI_WONT_RETURN_SUFFIX;
+    AI_WONT_RETURN void LogError(const char *szWarn) AI_WONT_RETURN_SUFFIX;
 
     // -------------------------------------------------------------------
     //! Parse a string, enclosed in double quotation marks
@@ -632,12 +617,11 @@ private:
     //! \param szName Name of the enclosing element -> used in error
     //! messages.
     //! \return false if an error occurred
-    bool ParseString(std::string& out,const char* szName);
+    bool ParseString(std::string &out, const char *szName);
 
 public:
-
     //! Pointer to current data
-    const char* filePtr;
+    const char *filePtr;
 
     //! background color to be passed to the viewer
     //! QNAN if none was found
@@ -684,9 +668,8 @@ public:
     unsigned int iFileFormat;
 };
 
-
 } // Namespace ASE
-} // Namespace ASSIMP
+} // namespace Assimp
 
 #endif // ASSIMP_BUILD_NO_3DS_IMPORTER
 

+ 68 - 0
Engine/lib/assimp/code/AssetLib/Assbin/AssbinExporter.cpp

@@ -0,0 +1,68 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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  AssbinExporter.cpp
+ *  ASSBIN exporter main code
+ */
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
+
+#include "AssbinFileWriter.h"
+
+#include <assimp/scene.h>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp {
+
+void ExportSceneAssbin(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
+    DumpSceneToAssbin(
+            pFile,
+            "\0", // no command(s).
+            pIOSystem,
+            pScene,
+            false, // shortened?
+            false); // compressed?
+}
+} // end of namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_ASSBIN_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 5 - 3
Engine/lib/assimp/code/Assbin/AssbinExporter.h → Engine/lib/assimp/code/AssetLib/Assbin/AssbinExporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,16 +42,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file AssbinExporter.h
  * ASSBIN Exporter Main Header
  */
+#pragma once
 #ifndef AI_ASSBINEXPORTER_H_INC
 #define AI_ASSBINEXPORTER_H_INC
 
 #include <assimp/defs.h>
 
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
 // nothing really needed here - reserved for future use like properties
 namespace Assimp {
 
 void ASSIMP_API ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/);
 
 }
-
+#endif 
 #endif // AI_ASSBINEXPORTER_H_INC

+ 833 - 0
Engine/lib/assimp/code/AssetLib/Assbin/AssbinFileWriter.cpp

@@ -0,0 +1,833 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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  AssbinFileWriter.cpp
+ *  @brief Implementation of Assbin file writer.
+ */
+
+#include "AssbinFileWriter.h"
+#include "Common/assbin_chunks.h"
+#include "PostProcessing/ProcessHelper.h"
+
+#include <assimp/Exceptional.h>
+#include <assimp/version.h>
+#include <assimp/IOStream.hpp>
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#include <zlib.h>
+#else
+#include "../contrib/zlib/zlib.h"
+#endif
+
+#include <ctime>
+
+#if _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4706)
+#endif // _MSC_VER
+
+namespace Assimp {
+
+template <typename T>
+size_t Write(IOStream *stream, const T &v) {
+    return stream->Write(&v, sizeof(T), 1);
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiString
+template <>
+inline size_t Write<aiString>(IOStream *stream, const aiString &s) {
+    const size_t s2 = (uint32_t)s.length;
+    stream->Write(&s, 4, 1);
+    stream->Write(s.data, s2, 1);
+
+    return s2 + 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an unsigned int as uint32_t
+template <>
+inline size_t Write<unsigned int>(IOStream *stream, const unsigned int &w) {
+    const uint32_t t = (uint32_t)w;
+    if (w > t) {
+        // this shouldn't happen, integers in Assimp data structures never exceed 2^32
+        throw DeadlyExportError("loss of data due to 64 -> 32 bit integer conversion");
+    }
+
+    stream->Write(&t, 4, 1);
+
+    return 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an unsigned int as uint16_t
+template <>
+inline size_t Write<uint16_t>(IOStream *stream, const uint16_t &w) {
+    static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t)==2");
+    stream->Write(&w, 2, 1);
+
+    return 2;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a float
+template <>
+inline size_t Write<float>(IOStream *stream, const float &f) {
+    static_assert(sizeof(float) == 4, "sizeof(float)==4");
+    stream->Write(&f, 4, 1);
+
+    return 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a double
+template <>
+inline size_t Write<double>(IOStream *stream, const double &f) {
+    static_assert(sizeof(double) == 8, "sizeof(double)==8");
+    stream->Write(&f, 8, 1);
+
+    return 8;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a vec3
+template <>
+inline size_t Write<aiVector3D>(IOStream *stream, const aiVector3D &v) {
+    size_t t = Write<float>(stream, v.x);
+    t += Write<float>(stream, v.y);
+    t += Write<float>(stream, v.z);
+
+    return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a color value
+template <>
+inline size_t Write<aiColor3D>(IOStream *stream, const aiColor3D &v) {
+    size_t t = Write<float>(stream, v.r);
+    t += Write<float>(stream, v.g);
+    t += Write<float>(stream, v.b);
+
+    return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a color value
+template <>
+inline size_t Write<aiColor4D>(IOStream *stream, const aiColor4D &v) {
+    size_t t = Write<float>(stream, v.r);
+    t += Write<float>(stream, v.g);
+    t += Write<float>(stream, v.b);
+    t += Write<float>(stream, v.a);
+
+    return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a quaternion
+template <>
+inline size_t Write<aiQuaternion>(IOStream *stream, const aiQuaternion &v) {
+    size_t t = Write<float>(stream, v.w);
+    t += Write<float>(stream, v.x);
+    t += Write<float>(stream, v.y);
+    t += Write<float>(stream, v.z);
+    ai_assert(t == 16);
+
+    return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a vertex weight
+template <>
+inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) {
+    size_t t = Write<unsigned int>(stream, v.mVertexId);
+
+    return t + Write<float>(stream, v.mWeight);
+}
+
+constexpr size_t MatrixSize = 64;
+
+// -----------------------------------------------------------------------------------
+// Serialize a mat4x4
+template <>
+inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) {
+    for (unsigned int i = 0; i < 4; ++i) {
+        for (unsigned int i2 = 0; i2 < 4; ++i2) {
+            Write<float>(stream, m[i][i2]);
+        }
+    }
+
+    return MatrixSize;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiVectorKey
+template <>
+inline size_t Write<aiVectorKey>(IOStream *stream, const aiVectorKey &v) {
+    const size_t t = Write<double>(stream, v.mTime);
+    return t + Write<aiVector3D>(stream, v.mValue);
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiQuatKey
+template <>
+inline size_t Write<aiQuatKey>(IOStream *stream, const aiQuatKey &v) {
+    const size_t t = Write<double>(stream, v.mTime);
+    return t + Write<aiQuaternion>(stream, v.mValue);
+}
+
+template <typename T>
+inline size_t WriteBounds(IOStream *stream, const T *in, unsigned int size) {
+    T minc, maxc;
+    ArrayBounds(in, size, minc, maxc);
+
+    const size_t t = Write<T>(stream, minc);
+    return t + Write<T>(stream, maxc);
+}
+
+// We use this to write out non-byte arrays so that we write using the specializations.
+// This way we avoid writing out extra bytes that potentially come from struct alignment.
+template <typename T>
+inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
+    size_t n = 0;
+    for (unsigned int i = 0; i < size; i++)
+        n += Write<T>(stream, in[i]);
+
+    return n;
+}
+
+// ----------------------------------------------------------------------------------
+/** @class  AssbinChunkWriter
+ *  @brief  Chunk writer mechanism for the .assbin file structure
+ *
+ *  This is a standard in-memory IOStream (most of the code is based on BlobIOStream),
+ *  the difference being that this takes another IOStream as a "container" in the
+ *  constructor, and when it is destroyed, it appends the magic number, the chunk size,
+ *  and the chunk contents to the container stream. This allows relatively easy chunk
+ *  chunk construction, even recursively.
+ */
+class AssbinChunkWriter : public IOStream {
+private:
+    uint8_t *buffer;
+    uint32_t magic;
+    IOStream *container;
+    size_t cur_size, cursor, initial;
+
+private:
+    // -------------------------------------------------------------------
+    void Grow(size_t need = 0) {
+        size_t new_size = std::max(initial, std::max(need, cur_size + (cur_size >> 1)));
+
+        const uint8_t *const old = buffer;
+        buffer = new uint8_t[new_size];
+
+        if (old) {
+            memcpy(buffer, old, cur_size);
+            delete[] old;
+        }
+
+        cur_size = new_size;
+    }
+
+public:
+    AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) :
+            buffer(nullptr),
+            magic(magic),
+            container(container),
+            cur_size(0),
+            cursor(0),
+            initial(initial) {
+        // empty
+    }
+
+    ~AssbinChunkWriter() override {
+        if (container) {
+            container->Write(&magic, sizeof(uint32_t), 1);
+            container->Write(&cursor, sizeof(uint32_t), 1);
+            container->Write(buffer, 1, cursor);
+        }
+        if (buffer) delete[] buffer;
+    }
+
+    void *GetBufferPointer() { return buffer; }
+
+    size_t Read(void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override {
+        return 0;
+    }
+    
+    aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) override {
+        return aiReturn_FAILURE;
+    }
+    
+    size_t Tell() const override {
+        return cursor;
+    }
+    
+    void Flush() override {
+        // not implemented
+    }
+
+    size_t FileSize() const override {
+        return cursor;
+    }
+
+    size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override {
+        pSize *= pCount;
+        if (cursor + pSize > cur_size) {
+            Grow(cursor + pSize);
+        }
+
+        memcpy(buffer + cursor, pvBuffer, pSize);
+        cursor += pSize;
+
+        return pCount;
+    }
+};
+
+// ----------------------------------------------------------------------------------
+/** @class  AssbinFileWriter
+ *  @brief  Assbin file writer class
+ *
+ *  This class writes an .assbin file, and is responsible for the file layout.
+ */
+class AssbinFileWriter {
+private:
+    bool shortened;
+    bool compressed;
+
+protected:
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryNode(IOStream *container, const aiNode *node) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODE);
+
+        unsigned int nb_metadata = (node->mMetaData != nullptr ? node->mMetaData->mNumProperties : 0);
+
+        Write<aiString>(&chunk, node->mName);
+        Write<aiMatrix4x4>(&chunk, node->mTransformation);
+        Write<unsigned int>(&chunk, node->mNumChildren);
+        Write<unsigned int>(&chunk, node->mNumMeshes);
+        Write<unsigned int>(&chunk, nb_metadata);
+
+        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+            Write<unsigned int>(&chunk, node->mMeshes[i]);
+        }
+
+        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+            WriteBinaryNode(&chunk, node->mChildren[i]);
+        }
+
+        for (unsigned int i = 0; i < nb_metadata; ++i) {
+            const aiString &key = node->mMetaData->mKeys[i];
+            aiMetadataType type = node->mMetaData->mValues[i].mType;
+            void *value = node->mMetaData->mValues[i].mData;
+
+            Write<aiString>(&chunk, key);
+            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;
+#ifdef SWIG
+                case FORCE_32BIT:
+#endif // SWIG
+            default:
+                break;
+            }
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryTexture(IOStream *container, const aiTexture *tex) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AITEXTURE);
+
+        Write<unsigned int>(&chunk, tex->mWidth);
+        Write<unsigned int>(&chunk, tex->mHeight);
+        // Write the texture format, but don't include the null terminator.
+        chunk.Write(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1);
+
+        if (!shortened) {
+            if (!tex->mHeight) {
+                chunk.Write(tex->pcData, 1, tex->mWidth);
+            } else {
+                chunk.Write(tex->pcData, 1, tex->mWidth * tex->mHeight * 4);
+            }
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryBone(IOStream *container, const aiBone *b) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIBONE);
+
+        Write<aiString>(&chunk, b->mName);
+        Write<unsigned int>(&chunk, b->mNumWeights);
+        Write<aiMatrix4x4>(&chunk, b->mOffsetMatrix);
+
+        // for the moment we write dumb min/max values for the bones, too.
+        // maybe I'll add a better, hash-like solution later
+        if (shortened) {
+            WriteBounds(&chunk, b->mWeights, b->mNumWeights);
+        } // else write as usual
+        else
+            WriteArray<aiVertexWeight>(&chunk, b->mWeights, b->mNumWeights);
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryMesh(IOStream *container, const aiMesh *mesh) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMESH);
+
+        Write<unsigned int>(&chunk, mesh->mPrimitiveTypes);
+        Write<unsigned int>(&chunk, mesh->mNumVertices);
+        Write<unsigned int>(&chunk, mesh->mNumFaces);
+        Write<unsigned int>(&chunk, mesh->mNumBones);
+        Write<unsigned int>(&chunk, mesh->mMaterialIndex);
+
+        // first of all, write bits for all existent vertex components
+        unsigned int c = 0;
+        if (mesh->mVertices) {
+            c |= ASSBIN_MESH_HAS_POSITIONS;
+        }
+        if (mesh->mNormals) {
+            c |= ASSBIN_MESH_HAS_NORMALS;
+        }
+        if (mesh->mTangents && mesh->mBitangents) {
+            c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS;
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
+            if (!mesh->mTextureCoords[n]) {
+                break;
+            }
+            c |= ASSBIN_MESH_HAS_TEXCOORD(n);
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
+            if (!mesh->mColors[n]) {
+                break;
+            }
+            c |= ASSBIN_MESH_HAS_COLOR(n);
+        }
+        Write<unsigned int>(&chunk, c);
+
+        aiVector3D minVec, maxVec;
+        if (mesh->mVertices) {
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mVertices, mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiVector3D>(&chunk, mesh->mVertices, mesh->mNumVertices);
+        }
+        if (mesh->mNormals) {
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mNormals, mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiVector3D>(&chunk, mesh->mNormals, mesh->mNumVertices);
+        }
+        if (mesh->mTangents && mesh->mBitangents) {
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mTangents, mesh->mNumVertices);
+                WriteBounds(&chunk, mesh->mBitangents, mesh->mNumVertices);
+            } // else write as usual
+            else {
+                WriteArray<aiVector3D>(&chunk, mesh->mTangents, mesh->mNumVertices);
+                WriteArray<aiVector3D>(&chunk, mesh->mBitangents, mesh->mNumVertices);
+            }
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
+            if (!mesh->mColors[n])
+                break;
+
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mColors[n], mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiColor4D>(&chunk, mesh->mColors[n], mesh->mNumVertices);
+        }
+        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
+            if (!mesh->mTextureCoords[n])
+                break;
+
+            // write number of UV components
+            Write<unsigned int>(&chunk, mesh->mNumUVComponents[n]);
+
+            if (shortened) {
+                WriteBounds(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
+            } // else write as usual
+            else
+                WriteArray<aiVector3D>(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
+        }
+
+        // write faces. There are no floating-point calculations involved
+        // in these, so we can write a simple hash over the face data
+        // to the dump file. We generate a single 32 Bit hash for 512 faces
+        // using Assimp's standard hashing function.
+        if (shortened) {
+            unsigned int processed = 0;
+            for (unsigned int job; (job = std::min(mesh->mNumFaces - processed, 512u)); processed += job) {
+                uint32_t hash = 0;
+                for (unsigned int a = 0; a < job; ++a) {
+
+                    const aiFace &f = mesh->mFaces[processed + a];
+                    uint32_t tmp = f.mNumIndices;
+                    hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash);
+                    for (unsigned int i = 0; i < f.mNumIndices; ++i) {
+                        static_assert(AI_MAX_VERTICES <= 0xffffffff, "AI_MAX_VERTICES <= 0xffffffff");
+                        tmp = static_cast<uint32_t>(f.mIndices[i]);
+                        hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash);
+                    }
+                }
+                Write<unsigned int>(&chunk, hash);
+            }
+        } else // else write as usual
+        {
+            // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
+            for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+                const aiFace &f = mesh->mFaces[i];
+
+                static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
+                Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mNumIndices));
+
+                for (unsigned int a = 0; a < f.mNumIndices; ++a) {
+                    if (mesh->mNumVertices < (1u << 16)) {
+                        Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mIndices[a]));
+                    } else {
+                        Write<unsigned int>(&chunk, f.mIndices[a]);
+                    }
+                }
+            }
+        }
+
+        // write bones
+        if (mesh->mNumBones) {
+            for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
+                const aiBone *b = mesh->mBones[a];
+                WriteBinaryBone(&chunk, b);
+            }
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryMaterialProperty(IOStream *container, const aiMaterialProperty *prop) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIALPROPERTY);
+
+        Write<aiString>(&chunk, prop->mKey);
+        Write<unsigned int>(&chunk, prop->mSemantic);
+        Write<unsigned int>(&chunk, prop->mIndex);
+
+        Write<unsigned int>(&chunk, prop->mDataLength);
+        Write<unsigned int>(&chunk, (unsigned int)prop->mType);
+        chunk.Write(prop->mData, 1, prop->mDataLength);
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryMaterial(IOStream *container, const aiMaterial *mat) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIAL);
+
+        Write<unsigned int>(&chunk, mat->mNumProperties);
+        for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
+            WriteBinaryMaterialProperty(&chunk, mat->mProperties[i]);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryNodeAnim(IOStream *container, const aiNodeAnim *nd) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODEANIM);
+
+        Write<aiString>(&chunk, nd->mNodeName);
+        Write<unsigned int>(&chunk, nd->mNumPositionKeys);
+        Write<unsigned int>(&chunk, nd->mNumRotationKeys);
+        Write<unsigned int>(&chunk, nd->mNumScalingKeys);
+        Write<unsigned int>(&chunk, nd->mPreState);
+        Write<unsigned int>(&chunk, nd->mPostState);
+
+        if (nd->mPositionKeys) {
+            if (shortened) {
+                WriteBounds(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
+
+            } // else write as usual
+            else
+                WriteArray<aiVectorKey>(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
+        }
+        if (nd->mRotationKeys) {
+            if (shortened) {
+                WriteBounds(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
+
+            } // else write as usual
+            else
+                WriteArray<aiQuatKey>(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
+        }
+        if (nd->mScalingKeys) {
+            if (shortened) {
+                WriteBounds(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
+
+            } // else write as usual
+            else
+                WriteArray<aiVectorKey>(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryAnim(IOStream *container, const aiAnimation *anim) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIANIMATION);
+
+        Write<aiString>(&chunk, anim->mName);
+        Write<double>(&chunk, anim->mDuration);
+        Write<double>(&chunk, anim->mTicksPerSecond);
+        Write<unsigned int>(&chunk, anim->mNumChannels);
+
+        for (unsigned int a = 0; a < anim->mNumChannels; ++a) {
+            const aiNodeAnim *nd = anim->mChannels[a];
+            WriteBinaryNodeAnim(&chunk, nd);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryLight(IOStream *container, const aiLight *l) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AILIGHT);
+
+        Write<aiString>(&chunk, l->mName);
+        Write<unsigned int>(&chunk, l->mType);
+
+        if (l->mType != aiLightSource_DIRECTIONAL) {
+            Write<float>(&chunk, l->mAttenuationConstant);
+            Write<float>(&chunk, l->mAttenuationLinear);
+            Write<float>(&chunk, l->mAttenuationQuadratic);
+        }
+
+        Write<aiColor3D>(&chunk, l->mColorDiffuse);
+        Write<aiColor3D>(&chunk, l->mColorSpecular);
+        Write<aiColor3D>(&chunk, l->mColorAmbient);
+
+        if (l->mType == aiLightSource_SPOT) {
+            Write<float>(&chunk, l->mAngleInnerCone);
+            Write<float>(&chunk, l->mAngleOuterCone);
+        }
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryCamera(IOStream *container, const aiCamera *cam) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AICAMERA);
+
+        Write<aiString>(&chunk, cam->mName);
+        Write<aiVector3D>(&chunk, cam->mPosition);
+        Write<aiVector3D>(&chunk, cam->mLookAt);
+        Write<aiVector3D>(&chunk, cam->mUp);
+        Write<float>(&chunk, cam->mHorizontalFOV);
+        Write<float>(&chunk, cam->mClipPlaneNear);
+        Write<float>(&chunk, cam->mClipPlaneFar);
+        Write<float>(&chunk, cam->mAspect);
+    }
+
+    // -----------------------------------------------------------------------------------
+    void WriteBinaryScene(IOStream *container, const aiScene *scene) {
+        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AISCENE);
+
+        // basic scene information
+        Write<unsigned int>(&chunk, scene->mFlags);
+        Write<unsigned int>(&chunk, scene->mNumMeshes);
+        Write<unsigned int>(&chunk, scene->mNumMaterials);
+        Write<unsigned int>(&chunk, scene->mNumAnimations);
+        Write<unsigned int>(&chunk, scene->mNumTextures);
+        Write<unsigned int>(&chunk, scene->mNumLights);
+        Write<unsigned int>(&chunk, scene->mNumCameras);
+
+        // write node graph
+        WriteBinaryNode(&chunk, scene->mRootNode);
+
+        // write all meshes
+        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+            const aiMesh *mesh = scene->mMeshes[i];
+            WriteBinaryMesh(&chunk, mesh);
+        }
+
+        // write materials
+        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+            const aiMaterial *mat = scene->mMaterials[i];
+            WriteBinaryMaterial(&chunk, mat);
+        }
+
+        // write all animations
+        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
+            const aiAnimation *anim = scene->mAnimations[i];
+            WriteBinaryAnim(&chunk, anim);
+        }
+
+        // write all textures
+        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
+            const aiTexture *mesh = scene->mTextures[i];
+            WriteBinaryTexture(&chunk, mesh);
+        }
+
+        // write lights
+        for (unsigned int i = 0; i < scene->mNumLights; ++i) {
+            const aiLight *l = scene->mLights[i];
+            WriteBinaryLight(&chunk, l);
+        }
+
+        // write cameras
+        for (unsigned int i = 0; i < scene->mNumCameras; ++i) {
+            const aiCamera *cam = scene->mCameras[i];
+            WriteBinaryCamera(&chunk, cam);
+        }
+    }
+
+public:
+    AssbinFileWriter(bool shortened, bool compressed) :
+            shortened(shortened), compressed(compressed) {
+    }
+
+    // -----------------------------------------------------------------------------------
+    // Write a binary model dump
+    void WriteBinaryDump(const char *pFile, const char *cmd, IOSystem *pIOSystem, const aiScene *pScene) {
+        IOStream *out = pIOSystem->Open(pFile, "wb");
+        if (!out)
+            throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n');
+
+        auto CloseIOStream = [&]() {
+            if (out) {
+                pIOSystem->Close(out);
+                out = nullptr; // Ensure this is only done once.
+            }
+        };
+
+        try {
+            time_t tt = time(nullptr);
+#if _WIN32
+            tm *p = gmtime(&tt);
+#else
+            struct tm now;
+            tm *p = gmtime_r(&tt, &now);
+#endif
+
+            // header
+            char s[64];
+            memset(s, 0, 64);
+#if _MSC_VER >= 1400
+            sprintf_s(s, "ASSIMP.binary-dump.%s", asctime(p));
+#else
+            ai_snprintf(s, 64, "ASSIMP.binary-dump.%s", asctime(p));
+#endif
+            out->Write(s, 44, 1);
+            // == 44 bytes
+
+            Write<unsigned int>(out, ASSBIN_VERSION_MAJOR);
+            Write<unsigned int>(out, ASSBIN_VERSION_MINOR);
+            Write<unsigned int>(out, aiGetVersionRevision());
+            Write<unsigned int>(out, aiGetCompileFlags());
+            Write<uint16_t>(out, shortened);
+            Write<uint16_t>(out, compressed);
+            // ==  20 bytes
+
+            char buff[256] = { 0 };
+            ai_snprintf(buff, 256, "%s", pFile);
+            out->Write(buff, sizeof(char), 256);
+
+            memset(buff, 0, sizeof(buff));
+            ai_snprintf(buff, 128, "%s", cmd);
+            out->Write(buff, sizeof(char), 128);
+
+            // leave 64 bytes free for future extensions
+            memset(buff, 0xcd, 64);
+            out->Write(buff, sizeof(char), 64);
+            // == 435 bytes
+
+            // ==== total header size: 512 bytes
+            ai_assert(out->Tell() == ASSBIN_HEADER_LENGTH);
+
+            // Up to here the data is uncompressed. For compressed files, the rest
+            // is compressed using standard DEFLATE from zlib.
+            if (compressed) {
+                AssbinChunkWriter uncompressedStream(nullptr, 0);
+                WriteBinaryScene(&uncompressedStream, pScene);
+
+                uLongf uncompressedSize = static_cast<uLongf>(uncompressedStream.Tell());
+                uLongf compressedSize = (uLongf)compressBound(uncompressedSize);
+                uint8_t *compressedBuffer = new uint8_t[compressedSize];
+
+                int res = compress2(compressedBuffer, &compressedSize, (const Bytef *)uncompressedStream.GetBufferPointer(), uncompressedSize, 9);
+                if (res != Z_OK) {
+                    delete[] compressedBuffer;
+                    throw DeadlyExportError("Compression failed.");
+                }
+
+                out->Write(&uncompressedSize, sizeof(uint32_t), 1);
+                out->Write(compressedBuffer, sizeof(char), compressedSize);
+
+                delete[] compressedBuffer;
+            } else {
+                WriteBinaryScene(out, pScene);
+            }
+
+            CloseIOStream();
+        } catch (...) {
+            CloseIOStream();
+            throw;
+        }
+    }
+};
+
+void DumpSceneToAssbin(
+        const char *pFile, const char *cmd, IOSystem *pIOSystem,
+        const aiScene *pScene, bool shortened, bool compressed) {
+    AssbinFileWriter fileWriter(shortened, compressed);
+    fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
+}
+#if _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+} // end of namespace Assimp

+ 19 - 19
Engine/lib/assimp/code/3MF/D3MFImporter.h → Engine/lib/assimp/code/AssetLib/Assbin/AssbinFileWriter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -40,26 +39,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-#ifndef AI_D3MFLOADER_H_INCLUDED
-#define AI_D3MFLOADER_H_INCLUDED
+/** @file AssbinFileWriter.h
+ *  @brief Declaration of Assbin file writer.
+ */
 
-#include <assimp/BaseImporter.h>
+#ifndef AI_ASSBINFILEWRITER_H_INC
+#define AI_ASSBINFILEWRITER_H_INC
 
-namespace Assimp {
+#include <assimp/defs.h>
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
 
-class D3MFImporter : public BaseImporter {
-public:
-    // BaseImporter interface
-    D3MFImporter();
-    ~D3MFImporter();
-    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
-    void SetupProperties(const Importer *pImp);
-    const aiImporterDesc *GetInfo() const;
+namespace Assimp {
 
-protected:
-    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
-};
+void ASSIMP_API DumpSceneToAssbin(
+        const char *pFile,
+        const char *cmd,
+        IOSystem *pIOSystem,
+        const aiScene *pScene,
+        bool shortened,
+        bool compressed);
 
-} // Namespace Assimp
+}
 
-#endif // AI_D3MFLOADER_H_INCLUDED
+#endif // AI_ASSBINFILEWRITER_H_INC

+ 228 - 231
Engine/lib/assimp/code/Assbin/AssbinLoader.cpp → Engine/lib/assimp/code/AssetLib/Assbin/AssbinLoader.cpp

@@ -3,9 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -50,19 +48,19 @@ 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/mesh.h>
 #include <assimp/anim.h>
-#include <assimp/scene.h>
 #include <assimp/importerdesc.h>
+#include <assimp/mesh.h>
+#include <assimp/scene.h>
 #include <memory>
 
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#   include <zlib.h>
+#include <zlib.h>
 #else
-#   include <contrib/zlib/zlib.h>
+#include <contrib/zlib/zlib.h>
 #endif
 
 using namespace Assimp;
@@ -81,94 +79,97 @@ static const aiImporterDesc desc = {
 };
 
 // -----------------------------------------------------------------------------------
-const aiImporterDesc* AssbinImporter::GetInfo() const {
+const aiImporterDesc *AssbinImporter::GetInfo() const {
     return &desc;
 }
 
 // -----------------------------------------------------------------------------------
-bool AssbinImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
-    IOStream * in = pIOHandler->Open(pFile);
+bool AssbinImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    IOStream *in = pIOHandler->Open(pFile);
     if (nullptr == in) {
         return false;
     }
 
     char s[32];
-    in->Read( s, sizeof(char), 32 );
+    in->Read(s, sizeof(char), 32);
 
     pIOHandler->Close(in);
 
-    return strncmp( s, "ASSIMP.binary-dump.", 19 ) == 0;
+    return strncmp(s, "ASSIMP.binary-dump.", 19) == 0;
 }
 
 // -----------------------------------------------------------------------------------
 template <typename T>
-T Read(IOStream * stream) {
+T Read(IOStream *stream) {
     T t;
-    size_t res = stream->Read( &t, sizeof(T), 1 );
-    if(res != 1)
+    size_t res = stream->Read(&t, sizeof(T), 1);
+    if (res != 1) {
         throw DeadlyImportError("Unexpected EOF");
+    }
     return t;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiVector3D Read<aiVector3D>(IOStream * stream) {
+aiVector3D Read<aiVector3D>(IOStream *stream) {
     aiVector3D v;
-    v.x = Read<float>(stream);
-    v.y = Read<float>(stream);
-    v.z = Read<float>(stream);
+    v.x = Read<ai_real>(stream);
+    v.y = Read<ai_real>(stream);
+    v.z = Read<ai_real>(stream);
     return v;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiColor4D Read<aiColor4D>(IOStream * stream) {
+aiColor4D Read<aiColor4D>(IOStream *stream) {
     aiColor4D c;
-    c.r = Read<float>(stream);
-    c.g = Read<float>(stream);
-    c.b = Read<float>(stream);
-    c.a = Read<float>(stream);
+    c.r = Read<ai_real>(stream);
+    c.g = Read<ai_real>(stream);
+    c.b = Read<ai_real>(stream);
+    c.a = Read<ai_real>(stream);
     return c;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiQuaternion Read<aiQuaternion>(IOStream * stream) {
+aiQuaternion Read<aiQuaternion>(IOStream *stream) {
     aiQuaternion v;
-    v.w = Read<float>(stream);
-    v.x = Read<float>(stream);
-    v.y = Read<float>(stream);
-    v.z = Read<float>(stream);
+    v.w = Read<ai_real>(stream);
+    v.x = Read<ai_real>(stream);
+    v.y = Read<ai_real>(stream);
+    v.z = Read<ai_real>(stream);
     return v;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiString Read<aiString>(IOStream * stream) {
+aiString Read<aiString>(IOStream *stream) {
     aiString s;
-    stream->Read(&s.length,4,1);
-    if(s.length)
-        stream->Read(s.data,s.length,1);
+    stream->Read(&s.length, 4, 1);
+    if (s.length) {
+        stream->Read(s.data, s.length, 1);
+    }
     s.data[s.length] = 0;
+
     return s;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiVertexWeight Read<aiVertexWeight>(IOStream * stream) {
+aiVertexWeight Read<aiVertexWeight>(IOStream *stream) {
     aiVertexWeight w;
     w.mVertexId = Read<unsigned int>(stream);
-    w.mWeight = Read<float>(stream);
+    w.mWeight = Read<ai_real>(stream);
     return w;
 }
 
 // -----------------------------------------------------------------------------------
 template <>
-aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream) {
+aiMatrix4x4 Read<aiMatrix4x4>(IOStream *stream) {
     aiMatrix4x4 m;
-    for (unsigned int i = 0; i < 4;++i) {
-        for (unsigned int i2 = 0; i2 < 4;++i2) {
-            m[i][i2] = Read<float>(stream);
+    for (unsigned int i = 0; i < 4; ++i) {
+        for (unsigned int i2 = 0; i2 < 4; ++i2) {
+            m[i][i2] = Read<ai_real>(stream);
         }
     }
     return m;
@@ -176,7 +177,7 @@ aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream) {
 
 // -----------------------------------------------------------------------------------
 template <>
-aiVectorKey Read<aiVectorKey>(IOStream * stream) {
+aiVectorKey Read<aiVectorKey>(IOStream *stream) {
     aiVectorKey v;
     v.mTime = Read<double>(stream);
     v.mValue = Read<aiVector3D>(stream);
@@ -185,7 +186,7 @@ aiVectorKey Read<aiVectorKey>(IOStream * stream) {
 
 // -----------------------------------------------------------------------------------
 template <>
-aiQuatKey Read<aiQuatKey>(IOStream * stream) {
+aiQuatKey Read<aiQuatKey>(IOStream *stream) {
     aiQuatKey v;
     v.mTime = Read<double>(stream);
     v.mValue = Read<aiQuaternion>(stream);
@@ -194,27 +195,27 @@ aiQuatKey Read<aiQuatKey>(IOStream * stream) {
 
 // -----------------------------------------------------------------------------------
 template <typename T>
-void ReadArray( IOStream *stream, T * out, unsigned int size) {
-    ai_assert( nullptr != stream );
-    ai_assert( nullptr != out );
+void ReadArray(IOStream *stream, T *out, unsigned int size) {
+    ai_assert(nullptr != stream);
+    ai_assert(nullptr != out);
 
-    for (unsigned int i=0; i<size; i++) {
+    for (unsigned int i = 0; i < size; i++) {
         out[i] = Read<T>(stream);
     }
 }
 
 // -----------------------------------------------------------------------------------
 template <typename T>
-void ReadBounds( IOStream * stream, T* /*p*/, unsigned int n ) {
+void ReadBounds(IOStream *stream, T * /*p*/, unsigned int n) {
     // not sure what to do here, the data isn't really useful.
-    stream->Seek( sizeof(T) * n, aiOrigin_CUR );
+    stream->Seek(sizeof(T) * n, aiOrigin_CUR);
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** onode, aiNode* parent ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODE)
+void AssbinImporter::ReadBinaryNode(IOStream *stream, aiNode **onode, aiNode *parent) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     std::unique_ptr<aiNode> node(new aiNode());
 
@@ -222,14 +223,13 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** onode, aiNode*
     node->mTransformation = Read<aiMatrix4x4>(stream);
     unsigned numChildren = Read<unsigned int>(stream);
     unsigned numMeshes = Read<unsigned int>(stream);
-	unsigned int nb_metadata = Read<unsigned int>(stream);
+    unsigned int nb_metadata = Read<unsigned int>(stream);
 
-    if(parent) {
+    if (parent) {
         node->mParent = parent;
     }
 
-    if (numMeshes)
-    {
+    if (numMeshes) {
         node->mMeshes = new unsigned int[numMeshes];
         for (unsigned int i = 0; i < numMeshes; ++i) {
             node->mMeshes[i] = Read<unsigned int>(stream);
@@ -238,60 +238,60 @@ void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** onode, aiNode*
     }
 
     if (numChildren) {
-        node->mChildren = new aiNode*[numChildren];
+        node->mChildren = new aiNode *[numChildren];
         for (unsigned int i = 0; i < numChildren; ++i) {
-            ReadBinaryNode( stream, &node->mChildren[i], node.get() );
+            ReadBinaryNode(stream, &node->mChildren[i], node.get());
             node->mNumChildren++;
         }
     }
 
-    if ( nb_metadata > 0 ) {
+    if (nb_metadata > 0) {
         node->mMetaData = aiMetadata::Alloc(nb_metadata);
         for (unsigned int i = 0; i < nb_metadata; ++i) {
             node->mMetaData->mKeys[i] = Read<aiString>(stream);
-            node->mMetaData->mValues[i].mType = (aiMetadataType) Read<uint16_t>(stream);
-            void* data = nullptr;
+            node->mMetaData->mValues[i].mType = (aiMetadataType)Read<uint16_t>(stream);
+            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 float(Read<float>(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;
-		}
-	}
+            node->mMetaData->mValues[i].mData = data;
+        }
+    }
     *onode = node.release();
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIBONE)
+void AssbinImporter::ReadBinaryBone(IOStream *stream, aiBone *b) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIBONE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     b->mName = Read<aiString>(stream);
     b->mNumWeights = Read<unsigned int>(stream);
@@ -300,23 +300,24 @@ void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b ) {
     // for the moment we write dumb min/max values for the bones, too.
     // maybe I'll add a better, hash-like solution later
     if (shortened) {
-        ReadBounds(stream,b->mWeights,b->mNumWeights);
+        ReadBounds(stream, b->mWeights, b->mNumWeights);
     } else {
         // else write as usual
         b->mWeights = new aiVertexWeight[b->mNumWeights];
-        ReadArray<aiVertexWeight>(stream,b->mWeights,b->mNumWeights);
+        ReadArray<aiVertexWeight>(stream, b->mWeights, b->mNumWeights);
     }
 }
 
 // -----------------------------------------------------------------------------------
 static bool fitsIntoUI16(unsigned int mNumVertices) {
-    return ( mNumVertices < (1u<<16) );
+    return (mNumVertices < (1u << 16));
 }
+
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMESH)
+void AssbinImporter::ReadBinaryMesh(IOStream *stream, aiMesh *mesh) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMESH)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     mesh->mPrimitiveTypes = Read<unsigned int>(stream);
     mesh->mNumVertices = Read<unsigned int>(stream);
@@ -329,48 +330,48 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
 
     if (c & ASSBIN_MESH_HAS_POSITIONS) {
         if (shortened) {
-            ReadBounds(stream,mesh->mVertices,mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mVertices, mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mVertices = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mVertices,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mVertices, mesh->mNumVertices);
         }
     }
     if (c & ASSBIN_MESH_HAS_NORMALS) {
         if (shortened) {
-            ReadBounds(stream,mesh->mNormals,mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mNormals, mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mNormals = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mNormals,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mNormals, mesh->mNumVertices);
         }
     }
     if (c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS) {
         if (shortened) {
-            ReadBounds(stream,mesh->mTangents,mesh->mNumVertices);
-            ReadBounds(stream,mesh->mBitangents,mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mTangents, mesh->mNumVertices);
+            ReadBounds(stream, mesh->mBitangents, mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mTangents = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mTangents,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mTangents, mesh->mNumVertices);
             mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mBitangents,mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mBitangents, mesh->mNumVertices);
         }
     }
-    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
+    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
         if (!(c & ASSBIN_MESH_HAS_COLOR(n))) {
             break;
         }
 
         if (shortened) {
-            ReadBounds(stream,mesh->mColors[n],mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mColors[n], mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mColors[n] = new aiColor4D[mesh->mNumVertices];
-            ReadArray<aiColor4D>(stream,mesh->mColors[n],mesh->mNumVertices);
+            ReadArray<aiColor4D>(stream, mesh->mColors[n], mesh->mNumVertices);
         }
     }
-    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
+    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
         if (!(c & ASSBIN_MESH_HAS_TEXCOORD(n))) {
             break;
         }
@@ -379,11 +380,11 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
         mesh->mNumUVComponents[n] = Read<unsigned int>(stream);
 
         if (shortened) {
-            ReadBounds(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
-        }  else {
+            ReadBounds(stream, mesh->mTextureCoords[n], mesh->mNumVertices);
+        } else {
             // else write as usual
             mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
-            ReadArray<aiVector3D>(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
+            ReadArray<aiVector3D>(stream, mesh->mTextureCoords[n], mesh->mNumVertices);
         }
     }
 
@@ -393,20 +394,20 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
     // using Assimp's standard hashing function.
     if (shortened) {
         Read<unsigned int>(stream);
-    } else  {
+    } else {
         // else write as usual
         // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
         mesh->mFaces = new aiFace[mesh->mNumFaces];
-        for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
-            aiFace& f = mesh->mFaces[i];
+        for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+            aiFace &f = mesh->mFaces[i];
 
             static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
             f.mNumIndices = Read<uint16_t>(stream);
             f.mIndices = new unsigned int[f.mNumIndices];
 
-            for (unsigned int a = 0; a < f.mNumIndices;++a) {
-                // Check if unsigned  short ( 16 bit  ) are big enought for the indices
-                if ( fitsIntoUI16( mesh->mNumVertices ) ) {
+            for (unsigned int a = 0; a < f.mNumIndices; ++a) {
+                // Check if unsigned  short ( 16 bit  ) are big enough for the indices
+                if (fitsIntoUI16(mesh->mNumVertices)) {
                     f.mIndices[a] = Read<uint16_t>(stream);
                 } else {
                     f.mIndices[a] = Read<unsigned int>(stream);
@@ -417,19 +418,19 @@ void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) {
 
     // write bones
     if (mesh->mNumBones) {
-        mesh->mBones = new C_STRUCT aiBone*[mesh->mNumBones];
-        for (unsigned int a = 0; a < mesh->mNumBones;++a) {
+        mesh->mBones = new C_STRUCT aiBone *[mesh->mNumBones];
+        for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
             mesh->mBones[a] = new aiBone();
-            ReadBinaryBone(stream,mesh->mBones[a]);
+            ReadBinaryBone(stream, mesh->mBones[a]);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIALPROPERTY)
+void AssbinImporter::ReadBinaryMaterialProperty(IOStream *stream, aiMaterialProperty *prop) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIALPROPERTY)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     prop->mKey = Read<aiString>(stream);
     prop->mSemantic = Read<unsigned int>(stream);
@@ -437,36 +438,34 @@ void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialPro
 
     prop->mDataLength = Read<unsigned int>(stream);
     prop->mType = (aiPropertyTypeInfo)Read<unsigned int>(stream);
-    prop->mData = new char [ prop->mDataLength ];
-    stream->Read(prop->mData,1,prop->mDataLength);
+    prop->mData = new char[prop->mDataLength];
+    stream->Read(prop->mData, 1, prop->mDataLength);
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIAL)
+void AssbinImporter::ReadBinaryMaterial(IOStream *stream, aiMaterial *mat) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIMATERIAL)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     mat->mNumAllocated = mat->mNumProperties = Read<unsigned int>(stream);
-    if (mat->mNumProperties)
-    {
-        if (mat->mProperties)
-        {
+    if (mat->mNumProperties) {
+        if (mat->mProperties) {
             delete[] mat->mProperties;
         }
-        mat->mProperties = new aiMaterialProperty*[mat->mNumProperties];
-        for (unsigned int i = 0; i < mat->mNumProperties;++i) {
+        mat->mProperties = new aiMaterialProperty *[mat->mNumProperties];
+        for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
             mat->mProperties[i] = new aiMaterialProperty();
-            ReadBinaryMaterialProperty( stream, mat->mProperties[i]);
+            ReadBinaryMaterialProperty(stream, mat->mProperties[i]);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODEANIM)
+void AssbinImporter::ReadBinaryNodeAnim(IOStream *stream, aiNodeAnim *nd) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AINODEANIM)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     nd->mNodeName = Read<aiString>(stream);
     nd->mNumPositionKeys = Read<unsigned int>(stream);
@@ -477,82 +476,82 @@ void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd) {
 
     if (nd->mNumPositionKeys) {
         if (shortened) {
-            ReadBounds(stream,nd->mPositionKeys,nd->mNumPositionKeys);
+            ReadBounds(stream, nd->mPositionKeys, nd->mNumPositionKeys);
 
         } // else write as usual
         else {
             nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
-            ReadArray<aiVectorKey>(stream,nd->mPositionKeys,nd->mNumPositionKeys);
+            ReadArray<aiVectorKey>(stream, nd->mPositionKeys, nd->mNumPositionKeys);
         }
     }
     if (nd->mNumRotationKeys) {
         if (shortened) {
-            ReadBounds(stream,nd->mRotationKeys,nd->mNumRotationKeys);
+            ReadBounds(stream, nd->mRotationKeys, nd->mNumRotationKeys);
 
-        }  else {
+        } else {
             // else write as usual
             nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
-            ReadArray<aiQuatKey>(stream,nd->mRotationKeys,nd->mNumRotationKeys);
+            ReadArray<aiQuatKey>(stream, nd->mRotationKeys, nd->mNumRotationKeys);
         }
     }
     if (nd->mNumScalingKeys) {
         if (shortened) {
-            ReadBounds(stream,nd->mScalingKeys,nd->mNumScalingKeys);
+            ReadBounds(stream, nd->mScalingKeys, nd->mNumScalingKeys);
 
-        }  else {
+        } else {
             // else write as usual
             nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys];
-            ReadArray<aiVectorKey>(stream,nd->mScalingKeys,nd->mNumScalingKeys);
+            ReadArray<aiVectorKey>(stream, nd->mScalingKeys, nd->mNumScalingKeys);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AIANIMATION)
+void AssbinImporter::ReadBinaryAnim(IOStream *stream, aiAnimation *anim) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AIANIMATION)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
-    anim->mName = Read<aiString> (stream);
-    anim->mDuration = Read<double> (stream);
-    anim->mTicksPerSecond = Read<double> (stream);
+    anim->mName = Read<aiString>(stream);
+    anim->mDuration = Read<double>(stream);
+    anim->mTicksPerSecond = Read<double>(stream);
     anim->mNumChannels = Read<unsigned int>(stream);
 
     if (anim->mNumChannels) {
-        anim->mChannels = new aiNodeAnim*[ anim->mNumChannels ];
-        for (unsigned int a = 0; a < anim->mNumChannels;++a) {
+        anim->mChannels = new aiNodeAnim *[anim->mNumChannels];
+        for (unsigned int a = 0; a < anim->mNumChannels; ++a) {
             anim->mChannels[a] = new aiNodeAnim();
-            ReadBinaryNodeAnim(stream,anim->mChannels[a]);
+            ReadBinaryNodeAnim(stream, anim->mChannels[a]);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AITEXTURE)
+void AssbinImporter::ReadBinaryTexture(IOStream *stream, aiTexture *tex) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AITEXTURE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     tex->mWidth = Read<unsigned int>(stream);
     tex->mHeight = Read<unsigned int>(stream);
-    stream->Read( tex->achFormatHint, sizeof(char), 4 );
+    stream->Read(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1);
 
-    if(!shortened) {
+    if (!shortened) {
         if (!tex->mHeight) {
-            tex->pcData = new aiTexel[ tex->mWidth ];
-            stream->Read(tex->pcData,1,tex->mWidth);
+            tex->pcData = new aiTexel[tex->mWidth];
+            stream->Read(tex->pcData, 1, tex->mWidth);
         } else {
-            tex->pcData = new aiTexel[ tex->mWidth*tex->mHeight ];
-            stream->Read(tex->pcData,1,tex->mWidth*tex->mHeight*4);
+            tex->pcData = new aiTexel[tex->mWidth * tex->mHeight];
+            stream->Read(tex->pcData, 1, tex->mWidth * tex->mHeight * 4);
         }
     }
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AILIGHT)
+void AssbinImporter::ReadBinaryLight(IOStream *stream, aiLight *l) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AILIGHT)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     l->mName = Read<aiString>(stream);
     l->mType = (aiLightSourceType)Read<unsigned int>(stream);
@@ -574,10 +573,10 @@ void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l ) {
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AICAMERA)
+void AssbinImporter::ReadBinaryCamera(IOStream *stream, aiCamera *cam) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AICAMERA)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
     cam->mName = Read<aiString>(stream);
     cam->mPosition = Read<aiVector3D>(stream);
@@ -590,141 +589,139 @@ void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) {
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene ) {
-    if(Read<uint32_t>(stream) != ASSBIN_CHUNK_AISCENE)
+void AssbinImporter::ReadBinaryScene(IOStream *stream, aiScene *scene) {
+    if (Read<uint32_t>(stream) != ASSBIN_CHUNK_AISCENE)
         throw DeadlyImportError("Magic chunk identifiers are wrong!");
-    /*uint32_t size =*/ Read<uint32_t>(stream);
+    /*uint32_t size =*/Read<uint32_t>(stream);
 
-    scene->mFlags         = Read<unsigned int>(stream);
-    scene->mNumMeshes     = Read<unsigned int>(stream);
-    scene->mNumMaterials  = Read<unsigned int>(stream);
+    scene->mFlags = Read<unsigned int>(stream);
+    scene->mNumMeshes = Read<unsigned int>(stream);
+    scene->mNumMaterials = Read<unsigned int>(stream);
     scene->mNumAnimations = Read<unsigned int>(stream);
-    scene->mNumTextures   = Read<unsigned int>(stream);
-    scene->mNumLights     = Read<unsigned int>(stream);
-    scene->mNumCameras    = Read<unsigned int>(stream);
+    scene->mNumTextures = Read<unsigned int>(stream);
+    scene->mNumLights = Read<unsigned int>(stream);
+    scene->mNumCameras = Read<unsigned int>(stream);
 
     // Read node graph
     //scene->mRootNode = new aiNode[1];
-    ReadBinaryNode( stream, &scene->mRootNode, (aiNode*)NULL );
+    ReadBinaryNode(stream, &scene->mRootNode, (aiNode *)nullptr);
 
     // Read all meshes
     if (scene->mNumMeshes) {
-        scene->mMeshes = new aiMesh*[scene->mNumMeshes];
-        memset(scene->mMeshes, 0, scene->mNumMeshes*sizeof(aiMesh*));
-        for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+        scene->mMeshes = new aiMesh *[scene->mNumMeshes];
+        memset(scene->mMeshes, 0, scene->mNumMeshes * sizeof(aiMesh *));
+        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
             scene->mMeshes[i] = new aiMesh();
-            ReadBinaryMesh( stream,scene->mMeshes[i]);
+            ReadBinaryMesh(stream, scene->mMeshes[i]);
         }
     }
 
     // Read materials
     if (scene->mNumMaterials) {
-        scene->mMaterials = new aiMaterial*[scene->mNumMaterials];
-        memset(scene->mMaterials, 0, scene->mNumMaterials*sizeof(aiMaterial*));
-        for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
+        scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
+        memset(scene->mMaterials, 0, scene->mNumMaterials * sizeof(aiMaterial *));
+        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
             scene->mMaterials[i] = new aiMaterial();
-            ReadBinaryMaterial(stream,scene->mMaterials[i]);
+            ReadBinaryMaterial(stream, scene->mMaterials[i]);
         }
     }
 
     // Read all animations
     if (scene->mNumAnimations) {
-        scene->mAnimations = new aiAnimation*[scene->mNumAnimations];
-        memset(scene->mAnimations, 0, scene->mNumAnimations*sizeof(aiAnimation*));
-        for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
+        scene->mAnimations = new aiAnimation *[scene->mNumAnimations];
+        memset(scene->mAnimations, 0, scene->mNumAnimations * sizeof(aiAnimation *));
+        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
             scene->mAnimations[i] = new aiAnimation();
-            ReadBinaryAnim(stream,scene->mAnimations[i]);
+            ReadBinaryAnim(stream, scene->mAnimations[i]);
         }
     }
 
     // Read all textures
     if (scene->mNumTextures) {
-        scene->mTextures = new aiTexture*[scene->mNumTextures];
-        memset(scene->mTextures, 0, scene->mNumTextures*sizeof(aiTexture*));
-        for (unsigned int i = 0; i < scene->mNumTextures;++i) {
+        scene->mTextures = new aiTexture *[scene->mNumTextures];
+        memset(scene->mTextures, 0, scene->mNumTextures * sizeof(aiTexture *));
+        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
             scene->mTextures[i] = new aiTexture();
-            ReadBinaryTexture(stream,scene->mTextures[i]);
+            ReadBinaryTexture(stream, scene->mTextures[i]);
         }
     }
 
     // Read lights
     if (scene->mNumLights) {
-        scene->mLights = new aiLight*[scene->mNumLights];
-        memset(scene->mLights, 0, scene->mNumLights*sizeof(aiLight*));
-        for (unsigned int i = 0; i < scene->mNumLights;++i) {
+        scene->mLights = new aiLight *[scene->mNumLights];
+        memset(scene->mLights, 0, scene->mNumLights * sizeof(aiLight *));
+        for (unsigned int i = 0; i < scene->mNumLights; ++i) {
             scene->mLights[i] = new aiLight();
-            ReadBinaryLight(stream,scene->mLights[i]);
+            ReadBinaryLight(stream, scene->mLights[i]);
         }
     }
 
     // Read cameras
     if (scene->mNumCameras) {
-        scene->mCameras = new aiCamera*[scene->mNumCameras];
-        memset(scene->mCameras, 0, scene->mNumCameras*sizeof(aiCamera*));
-        for (unsigned int i = 0; i < scene->mNumCameras;++i) {
+        scene->mCameras = new aiCamera *[scene->mNumCameras];
+        memset(scene->mCameras, 0, scene->mNumCameras * sizeof(aiCamera *));
+        for (unsigned int i = 0; i < scene->mNumCameras; ++i) {
             scene->mCameras[i] = new aiCamera();
-            ReadBinaryCamera(stream,scene->mCameras[i]);
+            ReadBinaryCamera(stream, scene->mCameras[i]);
         }
     }
-
 }
 
 // -----------------------------------------------------------------------------------
-void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler ) {
-    IOStream * stream = pIOHandler->Open(pFile,"rb");
+void AssbinImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    IOStream *stream = pIOHandler->Open(pFile, "rb");
     if (nullptr == stream) {
-        return;
+        throw DeadlyImportError("ASSBIN: Could not open ", pFile);
     }
 
     // signature
-    stream->Seek( 44, aiOrigin_CUR );
+    stream->Seek(44, aiOrigin_CUR);
 
     unsigned int versionMajor = Read<unsigned int>(stream);
     unsigned int versionMinor = Read<unsigned int>(stream);
     if (versionMinor != ASSBIN_VERSION_MINOR || versionMajor != ASSBIN_VERSION_MAJOR) {
-        throw DeadlyImportError( "Invalid version, data format not compatible!" );
+        throw DeadlyImportError("Invalid version, data format not compatible!");
     }
 
-    /*unsigned int versionRevision =*/ Read<unsigned int>(stream);
-    /*unsigned int compileFlags =*/ Read<unsigned int>(stream);
+    /*unsigned int versionRevision =*/Read<unsigned int>(stream);
+    /*unsigned int compileFlags =*/Read<unsigned int>(stream);
 
     shortened = Read<uint16_t>(stream) > 0;
     compressed = Read<uint16_t>(stream) > 0;
 
     if (shortened)
-        throw DeadlyImportError( "Shortened binaries are not supported!" );
+        throw DeadlyImportError("Shortened binaries are not supported!");
 
-    stream->Seek( 256, aiOrigin_CUR ); // original filename
-    stream->Seek( 128, aiOrigin_CUR ); // options
-    stream->Seek( 64, aiOrigin_CUR ); // padding
+    stream->Seek(256, aiOrigin_CUR); // original filename
+    stream->Seek(128, aiOrigin_CUR); // options
+    stream->Seek(64, aiOrigin_CUR); // padding
 
     if (compressed) {
         uLongf uncompressedSize = Read<uint32_t>(stream);
         uLongf compressedSize = static_cast<uLongf>(stream->FileSize() - stream->Tell());
 
-        unsigned char * compressedData = new unsigned char[ compressedSize ];
-        size_t len = stream->Read( compressedData, 1, compressedSize );
+        unsigned char *compressedData = new unsigned char[compressedSize];
+        size_t len = stream->Read(compressedData, 1, compressedSize);
         ai_assert(len == compressedSize);
 
-        unsigned char * uncompressedData = new unsigned char[ uncompressedSize ];
+        unsigned char *uncompressedData = new unsigned char[uncompressedSize];
 
-        int res = uncompress( uncompressedData, &uncompressedSize, compressedData, (uLong) len );
-        if(res != Z_OK)
-        {
-            delete [] uncompressedData;
-            delete [] compressedData;
+        int res = uncompress(uncompressedData, &uncompressedSize, compressedData, (uLong)len);
+        if (res != Z_OK) {
+            delete[] uncompressedData;
+            delete[] compressedData;
             pIOHandler->Close(stream);
             throw DeadlyImportError("Zlib decompression failed.");
         }
 
-        MemoryIOStream io( uncompressedData, uncompressedSize );
+        MemoryIOStream io(uncompressedData, uncompressedSize);
 
-        ReadBinaryScene(&io,pScene);
+        ReadBinaryScene(&io, pScene);
 
         delete[] uncompressedData;
         delete[] compressedData;
     } else {
-        ReadBinaryScene(stream,pScene);
+        ReadBinaryScene(stream, pScene);
     }
 
     pIOHandler->Close(stream);

+ 6 - 13
Engine/lib/assimp/code/Assbin/AssbinLoader.h → Engine/lib/assimp/code/AssetLib/Assbin/AssbinLoader.h

@@ -3,8 +3,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -74,17 +73,11 @@ private:
     bool compressed;
 
 public:
-    virtual bool CanRead(
-        const std::string& pFile,
-        IOSystem* pIOHandler,
-        bool checkSig
-    ) const;
-    virtual const aiImporterDesc* GetInfo() const;
-    virtual void InternReadFile(
-    const std::string& pFile,
-        aiScene* pScene,
-        IOSystem* pIOHandler
-    );
+    bool CanRead(const std::string& pFile,
+        IOSystem* pIOHandler, bool checkSig) const override;
+    const aiImporterDesc* GetInfo() const override;
+    void InternReadFile(
+    const std::string& pFile,aiScene* pScene,IOSystem* pIOHandler) override;
     void ReadHeader();
     void ReadBinaryScene( IOStream * stream, aiScene* pScene );
     void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent );

+ 18 - 10
Engine/lib/assimp/code/Assjson/cencode.c → Engine/lib/assimp/code/AssetLib/Assjson/cencode.c

@@ -9,6 +9,11 @@ For details, see http://sourceforge.net/projects/libb64
 
 const int CHARS_PER_LINE = 72;
 
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif // _MSC_VER
+
 void base64_init_encodestate(base64_encodestate* state_in)
 {
 	state_in->step = step_A;
@@ -30,9 +35,9 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
 	char* codechar = code_out;
 	char result;
 	char fragment;
-	
+
 	result = state_in->result;
-	
+
 	switch (state_in->step)
 	{
 		while (1)
@@ -42,7 +47,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
 			{
 				state_in->result = result;
 				state_in->step = step_A;
-				return codechar - code_out;
+				return (int)(codechar - code_out);
 			}
 			fragment = *plainchar++;
 			result = (fragment & 0x0fc) >> 2;
@@ -53,7 +58,7 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
 			{
 				state_in->result = result;
 				state_in->step = step_B;
-				return codechar - code_out;
+				return (int)(codechar - code_out);
 			}
 			fragment = *plainchar++;
 			result |= (fragment & 0x0f0) >> 4;
@@ -64,14 +69,14 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
 			{
 				state_in->result = result;
 				state_in->step = step_C;
-				return codechar - code_out;
+				return (int)(codechar - code_out);
 			}
 			fragment = *plainchar++;
 			result |= (fragment & 0x0c0) >> 6;
 			*codechar++ = base64_encode_value(result);
 			result  = (fragment & 0x03f) >> 0;
 			*codechar++ = base64_encode_value(result);
-			
+
 			++(state_in->stepcount);
 			if (state_in->stepcount == CHARS_PER_LINE/4)
 			{
@@ -81,13 +86,13 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
 		}
 	}
 	/* control should not reach here */
-	return codechar - code_out;
+	return (int)(codechar - code_out);
 }
 
 int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
 {
 	char* codechar = code_out;
-	
+
 	switch (state_in->step)
 	{
 	case step_B:
@@ -103,7 +108,10 @@ int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
 		break;
 	}
 	*codechar++ = '\n';
-	
-	return codechar - code_out;
+
+	return (int)(codechar - code_out);
 }
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER

+ 4 - 0
Engine/lib/assimp/code/Assjson/cencode.h → Engine/lib/assimp/code/AssetLib/Assjson/cencode.h

@@ -8,6 +8,10 @@ For details, see http://sourceforge.net/projects/libb64
 #ifndef BASE64_CENCODE_H
 #define BASE64_CENCODE_H
 
+#ifdef _MSC_VER
+#pragma warning(disable : 4127 )
+#endif // _MSC_VER
+
 typedef enum
 {
 	step_A, step_B, step_C

+ 112 - 111
Engine/lib/assimp/code/Assjson/json_exporter.cpp → Engine/lib/assimp/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 {
@@ -40,14 +41,17 @@ public:
     enum {
         Flag_DoNotIndent = 0x1,
         Flag_WriteSpecialFloats = 0x2,
+        Flag_SkipWhitespaces = 0x4
     };
-
-    JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u)
-    : out(out)
-    , first()
-    , flags(flags) {
+    
+    JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) :
+            out(out), indent (""), newline("\n"), space(" "), buff (), first(false), 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"));
+        if (flags & Flag_SkipWhitespaces) {
+            newline = "";
+            space = "";
+        }
     }
 
     ~JSONWriter() {
@@ -68,43 +72,43 @@ public:
         indent.erase(indent.end() - 1);
     }
 
-    void Key(const std::string& name) {
+    void Key(const std::string &name) {
         AddIndentation();
         Delimit();
-        buff << '\"' + name + "\": ";
+        buff << '\"' + name + "\":" << space;
     }
 
-    template<typename Literal>
-    void Element(const Literal& name) {
+    template <typename Literal>
+    void Element(const Literal &name) {
         AddIndentation();
         Delimit();
 
-        LiteralToString(buff, name) << '\n';
+        LiteralToString(buff, name) << newline;
     }
 
-    template<typename Literal>
-    void SimpleValue(const Literal& s) {
-        LiteralToString(buff, s) << '\n';
+    template <typename Literal>
+    void SimpleValue(const Literal &s) {
+        LiteralToString(buff, s) << newline;
     }
 
-    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 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), out, &s);
-        out[n + base64_encode_blockend(out + n, &s)] = '\0';
+        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';
 
         // base64 encoding may add newlines, but JSON strings may not contain 'real' newlines
         // (only escaped ones). Remove any newlines in out.
-        for (char* cur = out; *cur; ++cur) {
+        for (char *cur = cur_out; *cur; ++cur) {
             if (*cur == '\n') {
                 *cur = ' ';
             }
         }
 
-        buff << '\"' << out << "\"\n";
-        delete[] out;
+        buff << '\"' << cur_out << "\"" << newline;
+        delete[] cur_out;
     }
 
     void StartObj(bool is_element = false) {
@@ -116,7 +120,7 @@ public:
             }
         }
         first = true;
-        buff << "{\n";
+        buff << "{" << newline;
         PushIndent();
     }
 
@@ -124,7 +128,7 @@ public:
         PopIndent();
         AddIndentation();
         first = false;
-        buff << "}\n";
+        buff << "}" << newline;
     }
 
     void StartArray(bool is_element = false) {
@@ -136,19 +140,19 @@ public:
             }
         }
         first = true;
-        buff << "[\n";
+        buff << "[" << newline;
         PushIndent();
     }
 
     void EndArray() {
         PopIndent();
         AddIndentation();
-        buff << "]\n";
+        buff << "]" << newline;
         first = false;
     }
 
     void AddIndentation() {
-        if (!(flags & Flag_DoNotIndent)) {
+        if (!(flags & Flag_DoNotIndent) && !(flags & Flag_SkipWhitespaces)) {
             buff << indent;
         }
     }
@@ -156,21 +160,20 @@ public:
     void Delimit() {
         if (!first) {
             buff << ',';
-        }
-        else {
-            buff << ' ';
+        } else {
+            buff << space;
             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 +192,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,15 +231,17 @@ private:
     }
 
 private:
-    Assimp::IOStream& out;
-    std::string indent, newline;
+    Assimp::IOStream &out;
+    std::string indent;
+    std::string newline;
+    std::string space;
     std::stringstream buff;
     bool first;
 
     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 +249,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 +258,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 +266,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 +276,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 +297,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 +305,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 +416,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 +446,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 +466,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 i = 0; i < prop->mDataLength / sizeof(float); ++i) {
-                        out.Element(reinterpret_cast<float*>(prop->mData)[i]);
-                    }
-                    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 i = 0; i < prop->mDataLength / sizeof(int); ++i) {
-                        out.Element(reinterpret_cast<int*>(prop->mData)[i]);
-                    }
-                    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 +510,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 +525,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 +546,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 +584,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 +594,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 +610,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 +623,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 +636,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 +647,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 +668,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 +695,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 +704,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 +772,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 *pProperties) {
     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 {
@@ -792,18 +789,22 @@ void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* sc
         splitter.Execute(scenecopy_tmp);
 
         // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters
-        JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
+
+        unsigned int flags = JSONWriter::Flag_WriteSpecialFloats;
+        if (pProperties->GetPropertyBool("JSON_SKIP_WHITESPACES", false)) {
+            flags |= JSONWriter::Flag_SkipWhitespaces;
+        }
+        JSONWriter s(*str, flags);
         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

+ 10 - 11
Engine/lib/assimp/code/Assjson/mesh_splitter.cpp → Engine/lib/assimp/code/AssetLib/Assjson/mesh_splitter.cpp

@@ -69,13 +69,12 @@ void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh
 	for (unsigned int i = 0, end = pcNode->mNumChildren; i < end;++i)	{
 		UpdateNode ( pcNode->mChildren[i], source_mesh_map );
 	}
-	return;
 }
 
-#define WAS_NOT_COPIED 0xffffffff
+static const unsigned int WAS_NOT_COPIED = 0xffffffff;
 
-typedef std::pair <unsigned int,float> PerVertexWeight;
-typedef std::vector	<PerVertexWeight> VertexWeightTable;
+using PerVertexWeight = std::pair <unsigned int,float>;
+using VertexWeightTable = std::vector	<PerVertexWeight>;
 
 // ------------------------------------------------------------------------------------------------
 VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) {
@@ -89,7 +88,7 @@ VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) {
 		aiBone* bone = pMesh->mBones[i];
 		for (unsigned int a = 0; a < bone->mNumWeights;++a)	{
 			const aiVertexWeight& weight = bone->mWeights[a];
-			avPerVertexWeights[weight.mVertexId].push_back( std::make_pair(i,weight.mWeight) );
+			avPerVertexWeights[weight.mVertexId].emplace_back(i,weight.mWeight);
 		}
 	}
 	return avPerVertexWeights;
@@ -100,7 +99,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 	// TODO: should better use std::(multi)set for source_mesh_map.
 
 	if (in_mesh->mNumVertices <= LIMIT)	{
-		source_mesh_map.push_back(std::make_pair(in_mesh,a));
+		source_mesh_map.emplace_back(in_mesh,a);
 		return;
 	}
 
@@ -110,7 +109,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 	// we need to split this mesh into sub meshes. Estimate submesh size
 	const unsigned int sub_meshes = (in_mesh->mNumVertices / LIMIT) + 1;
 
-	// create a std::vector<unsigned int> to remember which vertices have already 
+	// create a std::vector<unsigned int> to remember which vertices have already
 	// been copied and to which position (i.e. output index)
 	std::vector<unsigned int> was_copied_to;
 	was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED);
@@ -125,7 +124,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 	while (true) {
 		const unsigned int out_vertex_index = LIMIT;
 
-		aiMesh* out_mesh = new aiMesh();			
+		aiMesh* out_mesh = new aiMesh();
 		out_mesh->mNumVertices = 0;
 		out_mesh->mMaterialIndex = in_mesh->mMaterialIndex;
 
@@ -179,7 +178,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 
 				// check whether we do already have this vertex
 				if (WAS_NOT_COPIED == was_copied_to[index])	{
-					iNeed++; 
+					iNeed++;
 				}
 			}
 			if (out_mesh->mNumVertices + iNeed > out_vertex_index)	{
@@ -187,7 +186,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 				break;
 			}
 
-			vFaces.push_back(aiFace());
+			vFaces.emplace_back();
 			aiFace& rFace = vFaces.back();
 
 			// setup face type and number of indices
@@ -240,7 +239,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 						out_mesh->mTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index];
 					}
 				}
-				// vertex colors 
+				// vertex colors
 				for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) {
 					if (in_mesh->HasVertexColors( c)) {
 						out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index];

+ 14 - 23
Engine/lib/assimp/code/Assjson/mesh_splitter.h → Engine/lib/assimp/code/AssetLib/Assjson/mesh_splitter.h

@@ -22,40 +22,31 @@ struct aiNode;
 
 // ---------------------------------------------------------------------------
 /** Splits meshes of unique vertices into meshes with no more vertices than
- *  a given, configurable threshold value. 
+ *  a given, configurable threshold value.
  */
-class MeshSplitter 
-{
-
+class MeshSplitter {
 public:
-	
-	void SetLimit(unsigned int l) {
-		LIMIT = l;
-	}
+    unsigned int LIMIT;
 
-	unsigned int GetLimit() const {
-		return LIMIT;
-	}
+    void SetLimit(unsigned int l) {
+        LIMIT = l;
+    }
 
-public:
+    unsigned int GetLimit() const {
+        return LIMIT;
+    }
 
-	// -------------------------------------------------------------------
-	/** Executes the post processing step on the given imported data.
+    // -------------------------------------------------------------------
+    /** Executes the post processing step on the given imported data.
 	 * At the moment a process is not supposed to fail.
 	 * @param pScene The imported data to work at.
 	 */
-	void Execute( aiScene* pScene);
-
+    void Execute(aiScene *pScene);
 
 private:
+    void UpdateNode(aiNode *pcNode, const std::vector<std::pair<aiMesh *, unsigned int>> &source_mesh_map);
+    void SplitMesh(unsigned int index, aiMesh *mesh, std::vector<std::pair<aiMesh *, unsigned int>> &source_mesh_map);
 
-	void UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
-	void SplitMesh (unsigned int index, aiMesh* mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
-
-public:
-
-	unsigned int LIMIT;
 };
 
 #endif // INCLUDED_MESH_SPLITTER
-

+ 68 - 0
Engine/lib/assimp/code/AssetLib/Assxml/AssxmlExporter.cpp

@@ -0,0 +1,68 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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  AssxmlExporter.cpp
+ *  ASSXML exporter main code
+ */
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
+
+#include "AssxmlFileWriter.h"
+#include <assimp/IOSystem.hpp>
+#include <assimp/Exporter.hpp>
+
+namespace Assimp   {
+
+void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
+{
+    DumpSceneToAssxml(
+        pFile,
+        "\0", // command(s)
+        pIOSystem,
+        pScene,
+        false); // shortened?
+}
+
+} // end of namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_ASSXML_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 2 - 1
Engine/lib/assimp/code/Assxml/AssxmlExporter.h → Engine/lib/assimp/code/AssetLib/Assxml/AssxmlExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file AssxmlExporter.h
  * ASSXML Exporter Main Header
  */
+#pragma once
 #ifndef AI_ASSXMLEXPORTER_H_INC
 #define AI_ASSXMLEXPORTER_H_INC
 

+ 662 - 0
Engine/lib/assimp/code/AssetLib/Assxml/AssxmlFileWriter.cpp

@@ -0,0 +1,662 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 chunks 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(nullptr);
+#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\" name=\"%s\" num_components=\"%u\"> \n",
+                         mesh->mNumVertices,
+                         a,
+                         (mesh->HasTextureCoordsName(a) ? mesh->GetTextureCoordsName(a)->C_Str() : ""),
+                         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

+ 64 - 0
Engine/lib/assimp/code/AssetLib/Assxml/AssxmlFileWriter.h

@@ -0,0 +1,64 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.h
+ *  @brief Declaration of Assxml file writer.
+ */
+#pragma once
+#ifndef AI_ASSXMLFILEWRITER_H_INC
+#define AI_ASSXMLFILEWRITER_H_INC
+
+#include <assimp/defs.h>
+#include <assimp/scene.h>
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp {
+
+void ASSIMP_API DumpSceneToAssxml(
+    const char* pFile,
+    const char* cmd,
+    IOSystem* pIOSystem,
+    const aiScene* pScene,
+    bool shortened);
+
+}
+
+#endif // AI_ASSXMLFILEWRITER_H_INC

+ 744 - 0
Engine/lib/assimp/code/AssetLib/B3D/B3DImporter.cpp

@@ -0,0 +1,744 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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() {
+    // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+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(const string &str) {
+#ifdef DEBUG_B3D
+    ASSIMP_LOG_ERROR("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("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("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("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

+ 13 - 15
Engine/lib/assimp/code/B3D/B3DImporter.h → Engine/lib/assimp/code/AssetLib/B3D/B3DImporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -40,8 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/** @file Definition of the .b3d importer class. */
-
+/**
+ *  @file Definition of the .b3d importer class.
+ */
+#pragma once
 #ifndef AI_B3DIMPORTER_H_INC
 #define AI_B3DIMPORTER_H_INC
 
@@ -62,14 +63,12 @@ namespace Assimp{
 class B3DImporter : public BaseImporter{
 public:
     B3DImporter() = default;
-    virtual ~B3DImporter();
-
-    virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+    ~B3DImporter() override;
+    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
 
 protected:
-
-    virtual const aiImporterDesc* GetInfo () const;
-    virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+    const aiImporterDesc* GetInfo () const override;
+    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
 
 private:
 
@@ -82,7 +81,7 @@ private:
     std::string ReadString();
     std::string ReadChunk();
     void ExitChunk();
-    unsigned ChunkSize();
+    size_t ChunkSize();
 
     template<class T>
     T *to_array( const std::vector<T> &v );
@@ -96,7 +95,7 @@ private:
     };
 
     AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX;
-    AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX;
+    AI_WONT_RETURN void Fail(const std::string &str) AI_WONT_RETURN_SUFFIX;
 
     void ReadTEXS();
     void ReadBRUS();
@@ -112,10 +111,9 @@ private:
 
     void ReadBB3D( aiScene *scene );
 
-    unsigned _pos;
-//  unsigned _size;
+    size_t _pos;
     std::vector<unsigned char> _buf;
-    std::vector<unsigned> _stack;
+    std::vector<size_t> _stack;
 
     std::vector<std::string> _textures;
     std::vector<std::unique_ptr<aiMaterial> > _materials;

+ 528 - 0
Engine/lib/assimp/code/AssetLib/BVH/BVHLoader.cpp

@@ -0,0 +1,528 @@
+/** Implementation of the BVH loader */
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+template<typename... T>
+AI_WONT_RETURN void BVHLoader::ThrowException(T&&... args) {
+    throw DeadlyImportError(mFileName, ":", mLine, " - ", args...);
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 /*checkSig*/) const {
+    static const char *tokens[] = { "HIERARCHY" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+}
+
+// ------------------------------------------------------------------------------------------------
+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 children and returns the created node;
+aiNode *BVHLoader::ReadNode() {
+    // first token is name
+    std::string nodeName = GetNextToken();
+    if (nodeName.empty() || nodeName == "{")
+        ThrowException("Expected node name, but found \"", nodeName, "\".");
+
+    // then an opening brace should follow
+    std::string openBrace = GetNextToken();
+    if (openBrace != "{")
+        ThrowException("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("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("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("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("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("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("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("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((unsigned char)*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((unsigned char)*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("Expected a floating point number, but found \"", token, "\".");
+
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 nullptr to ensure proper deletion if an exception is thrown
+    for (unsigned int i = 0; i < anim->mNumChannels; ++i)
+        anim->mChannels[i] = nullptr;
+
+    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 (unsigned int channelIdx = 0; channelIdx < node.mChannels.size(); ++ channelIdx) {
+					switch (node.mChannels[channelIdx]) {
+                    case Channel_RotationX:
+                        {
+                        const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+                        aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+                        }
+                        break;
+                    case Channel_RotationY:
+                        {
+                        const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+                        aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp);
+                        }
+                        break;
+                    case Channel_RotationZ:
+                        {
+                        const float angle = node.mChannelValues[fr * node.mChannels.size() + channelIdx] * float(AI_MATH_PI) / 180.0f;
+                        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

+ 25 - 31
Engine/lib/assimp/code/BVH/BVHLoader.h → Engine/lib/assimp/code/AssetLib/BVH/BVHLoader.h

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -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();
+    /** Reads a node and recursively its children and returns the created node. */
+    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,11 @@ protected:
     float GetNextTokenAsFloat();
 
     /** Aborts the file reading with an exception */
-    AI_WONT_RETURN void ThrowException( const std::string& pError) AI_WONT_RETURN_SUFFIX;
+    template<typename... T>
+    AI_WONT_RETURN void ThrowException(T&&... args) 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 */

+ 63 - 85
Engine/lib/assimp/code/Blender/BlenderBMesh.cpp → Engine/lib/assimp/code/AssetLib/Blender/BlenderBMesh.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2013, assimp team
+Copyright (c) 2006-2022, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -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

+ 1 - 1
Engine/lib/assimp/code/Blender/BlenderBMesh.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderBMesh.h

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

+ 181 - 0
Engine/lib/assimp/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("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
Engine/lib/assimp/code/Blender/BlenderCustomData.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderCustomData.h


+ 75 - 99
Engine/lib/assimp/code/Blender/BlenderDNA.cpp → Engine/lib/assimp/code/AssetLib/Blender/BlenderDNA.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -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,76 +71,74 @@ 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)"
-            ));
+            throw DeadlyImportError("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;
-        //s.index = dna.structures.size()-1;
+        Structure &s = dna.structures.back();
+        s.name = types[n].name;
 
         n = stream.GetI2();
         s.fields.reserve(n);
@@ -151,13 +148,10 @@ 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)"
-                ));
+                throw DeadlyImportError("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;
@@ -165,10 +159,7 @@ 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)"
-                ));
+                throw DeadlyImportError("BlenderDNA: Invalid name index in structure field ", j, " (there are only ", names.size(), " entries)");
             }
 
             f.name = names[j];
@@ -190,29 +181,26 @@ void DNAParser::Parse ()
             if (*f.name.rbegin() == ']') {
                 const std::string::size_type rb = f.name.find('[');
                 if (rb == std::string::npos) {
-                    throw DeadlyImportError((format(),
-                        "BlenderDNA: Encountered invalid array declaration ",
-                        f.name
-                    ));
+                    throw DeadlyImportError("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("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields");
 
-#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+#if ASSIMP_BUILD_BLENDER_DEBUG_DNA
     dna.DumpToFile();
 #endif
 
@@ -220,13 +208,11 @@ void DNAParser::Parse ()
     dna.RegisterConverters();
 }
 
-
-#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+#if ASSIMP_BUILD_BLENDER_DEBUG_DNA
 
 #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 +221,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";
@@ -249,14 +237,12 @@ void DNA :: DumpToFile()
 
     ASSIMP_LOG_INFO("BlenderDNA: Dumped dna to dna.txt");
 }
-#endif
+#endif // ASSIMP_BUILD_BLENDER_DEBUG_DNA
 
 // ------------------------------------------------------------------------------------------------
-/*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 +250,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 +258,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 +293,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 +321,7 @@ void DNA :: AddPrimitiveStructures()
 }
 
 // ------------------------------------------------------------------------------------------------
-void SectionParser :: Next()
-{
+void SectionParser ::Next() {
     stream.SetCurrentPos(current.start + current.size);
 
     const char tmp[] = {
@@ -352,7 +330,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();
@@ -366,10 +344,8 @@ void SectionParser :: Next()
     }
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
-    ASSIMP_LOG_DEBUG(current.id);
+    ASSIMP_LOG_VERBOSE_DEBUG(current.id);
 #endif
 }
 
-
-
 #endif

+ 177 - 213
Engine/lib/assimp/code/Blender/BlenderDNA.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderDNA.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -49,26 +49,34 @@ 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
+
+// set this to non-zero to dump BlenderDNA stuff to dna.txt.
+// you could set it on the assimp build command line too without touching it here.
+// !!! please make sure this is set to 0 in the repo !!!
+#ifndef ASSIMP_BUILD_BLENDER_DEBUG_DNA
+#define ASSIMP_BUILD_BLENDER_DEBUG_DNA 0
 #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,9 +90,9 @@ class ObjectCache;
  *  ancestry. */
 // -------------------------------------------------------------------------------
 struct Error : DeadlyImportError {
-    Error (const std::string& s)
-    : DeadlyImportError(s) {
-        // empty
+    template <typename... T>
+    explicit Error(T &&...args) :
+            DeadlyImportError(args...) {
     }
 };
 
@@ -93,9 +101,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 +117,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 +127,8 @@ struct ElemBase {
  *  they used to point to.*/
 // -------------------------------------------------------------------------------
 struct Pointer {
-    Pointer()
-    : val() {
+    Pointer() :
+            val() {
         // empty
     }
     uint64_t val;
@@ -131,8 +138,8 @@ struct Pointer {
 /** Represents a generic offset within a BLEND file */
 // -------------------------------------------------------------------------------
 struct FileOffset {
-    FileOffset()
-    : val() {
+    FileOffset() :
+            val() {
         // empty
     }
     uint64_t val;
@@ -154,7 +161,7 @@ public:
         resize(0);
     }
 
-    operator bool () const {
+    operator bool() const {
         return !empty();
     }
 };
@@ -164,7 +171,7 @@ public:
 // -------------------------------------------------------------------------------
 enum FieldFlags {
     FieldFlag_Pointer = 0x1,
-    FieldFlag_Array   = 0x2
+    FieldFlag_Array = 0x2
 };
 
 // -------------------------------------------------------------------------------
@@ -186,7 +193,7 @@ struct Field {
 };
 
 // -------------------------------------------------------------------------------
-/** Range of possible behaviours for fields absend in the input file. Some are
+/** Range of possible behaviors for fields absence in the input file. Some are
  *  mission critical so we need them, while others can silently be default
  *  initialized and no animations are harmed. */
 // -------------------------------------------------------------------------------
@@ -200,7 +207,7 @@ enum ErrorPolicy {
 };
 
 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
-#   define ErrorPolicy_Igno ErrorPolicy_Warn
+#define ErrorPolicy_Igno ErrorPolicy_Warn
 #endif
 
 // -------------------------------------------------------------------------------
@@ -212,47 +219,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;
+     *  returns nullptr 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;
 
     // --------------------------------------------------------
     /** 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 +262,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 +320,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,42 +331,42 @@ 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;
+        return s ? &out.front() : nullptr;
     }
 
     // --------------------------------------------------------
@@ -372,14 +374,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 * = nullptr) {
             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 * = nullptr) {
             for (unsigned int i = 0; i < N; ++i) {
                 for (unsigned int j = 0; j < M; ++j) {
                     out[i][j] = T();
@@ -388,21 +390,21 @@ private:
         }
 
         template <typename T>
-        void operator ()(T& out, const char* = NULL) {
+        void operator()(T &out, const char * = nullptr) {
             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 +412,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 +424,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 +437,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
+    /** Access a structure by its canonical name, the pointer version returns nullptr 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 */
@@ -480,10 +476,9 @@ public:
      *  in BlenderScene.cpp and is machine-generated.
      *  Converters are used to quickly handle objects whose
      *  exact data type is a runtime-property and not yet
-     *  known at compile time (consier Object::data).*/
+     *  known at compile time (consider 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 +486,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,12 +499,10 @@ 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
+#if ASSIMP_BUILD_BLENDER_DEBUG_DNA
     // --------------------------------------------------------
     /** Dump the DNA to a text file. This is for debugging purposes.
      *  The output file is `dna.txt` in the current working folder*/
@@ -527,25 +519,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 +557,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 +613,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 +643,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 +657,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 +677,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 +722,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 +735,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 +756,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 +778,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 +800,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"
 

+ 28 - 39
Engine/lib/assimp/code/Blender/BlenderDNA.inl → Engine/lib/assimp/code/AssetLib/Blender/BlenderDNA.inl

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -58,9 +57,7 @@ const Field& Structure :: operator [] (const std::string& ss) const
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     if (it == indices.end()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`"
-            ));
+        throw Error("BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`");
     }
 
     return fields[(*it).second];
@@ -70,16 +67,14 @@ const Field& Structure :: operator [] (const std::string& ss) const
 const Field* Structure :: Get (const std::string& ss) const
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
-    return it == indices.end() ? NULL : &fields[(*it).second];
+    return it == indices.end() ? nullptr : &fields[(*it).second];
 }
 
 //--------------------------------------------------------------------------------
 const Field& Structure :: operator [] (const size_t i) const
 {
     if (i >= fields.size()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: There is no field with index `",i,"` in structure `",name,"`"
-            ));
+        throw Error("BlendDNA: There is no field with index `",i,"` in structure `",name,"`");
     }
 
     return fields[i];
@@ -110,9 +105,7 @@ void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatab
 
         // is the input actually an array?
         if (!(f.flags & FieldFlag_Array)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be an array of size ",M
-                ));
+            throw Error("Field `",name,"` of structure `",this->name,"` ought to be an array of size ",M);
         }
 
         db.reader->IncPtr(f.offset);
@@ -149,9 +142,9 @@ void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileD
 
         // is the input actually an array?
         if (!(f.flags & FieldFlag_Array)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
+            throw Error("Field `",name,"` of structure `",
                 this->name,"` ought to be an array of size ",M,"*",N
-                ));
+                );
         }
 
         db.reader->IncPtr(f.offset);
@@ -196,8 +189,8 @@ bool Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabas
 
         // sanity check, should never happen if the genblenddna script is right
         if (!(f->flags & FieldFlag_Pointer)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be a pointer"));
+            throw Error("Field `",name,"` of structure `",
+                this->name,"` ought to be a pointer");
         }
 
         db.reader->IncPtr(f->offset);
@@ -239,11 +232,13 @@ bool Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
     try {
         f = &(*this)[name];
 
+#ifdef _DEBUG
         // sanity check, should never happen if the genblenddna script is right
         if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be a pointer AND an array"));
+            throw Error("Field `",name,"` of structure `",
+                this->name,"` ought to be a pointer AND an array");
         }
+#endif // _DEBUG
 
         db.reader->IncPtr(f->offset);
 
@@ -321,8 +316,8 @@ bool Structure::ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, con
 
 		// sanity check, should never happen if the genblenddna script is right
 		if (!(f->flags & FieldFlag_Pointer)) {
-			throw Error((Formatter::format(), "Field `", name, "` of structure `",
-				this->name, "` ought to be a pointer"));
+			throw Error("Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer");
 		}
 
 		db.reader->IncPtr(f->offset);
@@ -368,8 +363,8 @@ bool Structure::ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const
 
 		// sanity check, should never happen if the genblenddna script is right
 		if (!(f->flags & FieldFlag_Pointer)) {
-			throw Error((Formatter::format(), "Field `", name, "` of structure `",
-				this->name, "` ought to be a pointer"));
+			throw Error("Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer");
 		}
 
 		db.reader->IncPtr(f->offset);
@@ -427,9 +422,9 @@ bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const Fil
     // and check if it matches the type which we expect.
     const Structure& ss = db.dna[block->dna_index];
     if (ss != s) {
-        throw Error((Formatter::format(),"Expected target to be of type `",s.name,
+        throw Error("Expected target to be of type `",s.name,
             "` but seemingly it is a `",ss.name,"` instead"
-            ));
+            );
     }
 
     // try to retrieve the object from the cache
@@ -570,7 +565,7 @@ template <> bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shar
         // this might happen if DNA::RegisterConverters hasn't been called so far
         // or if the target type is not contained in `our` DNA.
         out.reset();
-        ASSIMP_LOG_WARN_F( "Failed to find a converter for the `",s.name,"` structure" );
+        ASSIMP_LOG_WARN( "Failed to find a converter for the `",s.name,"` structure" );
         return false;
     }
 
@@ -613,16 +608,14 @@ const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrv
     if (it == db.entries.end()) {
         // this is crucial, pointers may not be invalid.
         // this is either a corrupted file or an attempted attack.
-        throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
-            std::hex,ptrval.val,", no file block falls into this address range"
-            ));
+        throw DeadlyImportError("Failure resolving pointer 0x",
+            std::hex,ptrval.val,", no file block falls into this address range");
     }
     if (ptrval.val >= (*it).address.val + (*it).size) {
-        throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
+        throw DeadlyImportError("Failure resolving pointer 0x",
             std::hex,ptrval.val,", nearest file block starting at 0x",
             (*it).address.val," ends at 0x",
-            (*it).address.val + (*it).size
-            ));
+            (*it).address.val + (*it).size);
     }
     return &*it;
 }
@@ -675,7 +668,7 @@ template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,
         out = static_cast<T>(db.reader->GetF8());
     }
     else {
-        throw DeadlyImportError("Unknown source for conversion to primitive data type: "+in.name);
+        throw DeadlyImportError("Unknown source for conversion to primitive data type: ", in.name);
     }
 }
 
@@ -783,9 +776,7 @@ const Structure& DNA :: operator [] (const std::string& ss) const
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     if (it == indices.end()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: Did not find a structure named `",ss,"`"
-            ));
+        throw Error("BlendDNA: Did not find a structure named `",ss,"`");
     }
 
     return structures[(*it).second];
@@ -795,16 +786,14 @@ 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];
 }
 
 //--------------------------------------------------------------------------------
 const Structure& DNA :: operator [] (const size_t i) const
 {
     if (i >= structures.size()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: There is no structure with index `",i,"`"
-            ));
+        throw Error("BlendDNA: There is no structure with index `",i,"`");
     }
 
     return structures[i];

+ 1 - 1
Engine/lib/assimp/code/Blender/BlenderIntermediate.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderIntermediate.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 295 - 334
Engine/lib/assimp/code/AssetLib/Blender/BlenderLoader.cpp


+ 198 - 0
Engine/lib/assimp/code/AssetLib/Blender/BlenderLoader.h

@@ -0,0 +1,198 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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  BlenderLoader.h
+ *  @brief Declaration of the Blender 3D (*.blend) importer class.
+ */
+#pragma once
+#ifndef INCLUDED_AI_BLEND_LOADER_H
+#define INCLUDED_AI_BLEND_LOADER_H
+
+#include <assimp/BaseImporter.h>
+#include <assimp/LogAux.h>
+#include <memory>
+
+struct aiNode;
+struct aiMesh;
+struct aiLight;
+struct aiCamera;
+struct aiMaterial;
+
+namespace Assimp {
+
+// TinyFormatter.h
+namespace Formatter {
+
+template <typename T, typename TR, typename A>
+class basic_formatter;
+
+typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>> format;
+
+} // namespace Formatter
+
+// BlenderDNA.h
+namespace Blender {
+class FileDatabase;
+struct ElemBase;
+} // namespace Blender
+
+// BlenderScene.h
+namespace Blender {
+struct Scene;
+struct Object;
+struct Collection;
+struct Mesh;
+struct Camera;
+struct Lamp;
+struct MTex;
+struct Image;
+struct Material;
+} // namespace Blender
+
+// BlenderIntermediate.h
+namespace Blender {
+struct ConversionData;
+template <template <typename, typename> class TCLASS, typename T>
+struct TempArray;
+} // namespace Blender
+
+// BlenderModifier.h
+namespace Blender {
+class BlenderModifierShowcase;
+class BlenderModifier;
+} // namespace Blender
+
+// -------------------------------------------------------------------------------------------
+/** Load blenders official binary format. The actual file structure (the `DNA` how they
+ *  call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the
+ *  conversion from intermediate format to aiScene. */
+// -------------------------------------------------------------------------------------------
+class BlenderImporter : public BaseImporter, public LogFunctions<BlenderImporter> {
+public:
+    BlenderImporter();
+    ~BlenderImporter() override;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
+
+protected:
+    const aiImporterDesc *GetInfo() const override;
+    void SetupProperties(const Importer *pImp) override;
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+    void ParseBlendFile(Blender::FileDatabase &out, std::shared_ptr<IOStream> stream);
+    void ExtractScene(Blender::Scene &out, const Blender::FileDatabase &file);
+    void ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr<Blender::Collection> collection, Blender::ConversionData &conv_data);
+    void ConvertBlendFile(aiScene *out, const Blender::Scene &in, const Blender::FileDatabase &file);
+
+private:
+    aiNode *ConvertNode(const Blender::Scene &in,
+            const Blender::Object *obj,
+            Blender::ConversionData &conv_info,
+            const aiMatrix4x4 &parentTransform);
+
+    // --------------------
+    void ConvertMesh(const Blender::Scene &in,
+            const Blender::Object *obj,
+            const Blender::Mesh *mesh,
+            Blender::ConversionData &conv_data,
+            Blender::TempArray<std::vector, aiMesh> &temp);
+
+    // --------------------
+    aiLight *ConvertLight(const Blender::Scene &in,
+            const Blender::Object *obj,
+            const Blender::Lamp *mesh,
+            Blender::ConversionData &conv_data);
+
+    // --------------------
+    aiCamera *ConvertCamera(const Blender::Scene &in,
+            const Blender::Object *obj,
+            const Blender::Camera *mesh,
+            Blender::ConversionData &conv_data);
+
+    // --------------------
+    void BuildDefaultMaterial(
+            Blender::ConversionData &conv_data);
+
+    // --------------------
+    void AddBlendParams(
+            aiMaterial *result,
+            const Blender::Material *source);
+
+    // --------------------
+    void BuildMaterials(
+            Blender::ConversionData &conv_data);
+
+    // --------------------
+    void ResolveTexture(
+            aiMaterial *out,
+            const Blender::Material *mat,
+            const Blender::MTex *tex,
+            Blender::ConversionData &conv_data);
+
+    // --------------------
+    void ResolveImage(
+            aiMaterial *out,
+            const Blender::Material *mat,
+            const Blender::MTex *tex,
+            const Blender::Image *img,
+            Blender::ConversionData &conv_data);
+
+    // --------------------
+    void AddSentinelTexture(
+            aiMaterial *out,
+            const Blender::Material *mat,
+            const Blender::MTex *tex,
+            Blender::ConversionData &conv_data);
+
+private: // static stuff, mostly logging and error reporting.
+    // --------------------
+    static void CheckActualType(const Blender::ElemBase *dt,
+            const char *check);
+
+    // --------------------
+    static void NotSupportedObjectType(const Blender::Object *obj,
+            const char *type);
+
+private:
+    Blender::BlenderModifierShowcase *modifier_cache;
+
+}; // !class BlenderImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC

+ 89 - 89
Engine/lib/assimp/code/Blender/BlenderModifier.cpp → Engine/lib/assimp/code/AssetLib/Blender/BlenderModifier.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -57,52 +57,51 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace Assimp::Blender;
 
-template <typename T> BlenderModifier* god() {
+template <typename T>
+BlenderModifier *god() {
     return new T();
 }
 
 // add all available modifiers here
-typedef BlenderModifier* (*fpCreateModifier)();
+typedef BlenderModifier *(*fpCreateModifier)();
 static const fpCreateModifier creators[] = {
-        &god<BlenderModifier_Mirror>,
-        &god<BlenderModifier_Subdivision>,
+    &god<BlenderModifier_Mirror>,
+    &god<BlenderModifier_Subdivision>,
 
-        NULL // sentinel
+    nullptr // sentinel
 };
 
 // ------------------------------------------------------------------------------------------------
-struct SharedModifierData : ElemBase
-{
+struct SharedModifierData : ElemBase {
     ModifierData modifier;
 };
 
 // ------------------------------------------------------------------------------------------------
-void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_data, const Scene& in, const Object& orig_object )
-{
+void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_data, const Scene &in, const Object &orig_object) {
     size_t cnt = 0u, ful = 0u;
 
     // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before
     // we're allowed to dereference the pointers without risking to crash. We might still be
     // invoking UB btw - we're assuming that the ModifierData member of the respective modifier
     // structures is at offset sizeof(vftable) with no padding.
-    const SharedModifierData* cur = static_cast<const SharedModifierData *> ( orig_object.modifiers.first.get() );
-    for (; cur; cur =  static_cast<const SharedModifierData *> ( cur->modifier.next.get() ), ++ful) {
+    const SharedModifierData *cur = static_cast<const SharedModifierData *>(orig_object.modifiers.first.get());
+    for (; cur; cur = static_cast<const SharedModifierData *>(cur->modifier.next.get()), ++ful) {
         ai_assert(cur->dna_type);
 
-        const Structure* s = conv_data.db.dna.Get( cur->dna_type );
+        const Structure *s = conv_data.db.dna.Get(cur->dna_type);
         if (!s) {
-            ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: ",cur->dna_type);
+            ASSIMP_LOG_WARN("BlendModifier: could not resolve DNA name: ", cur->dna_type);
             continue;
         }
 
         // this is a common trait of all XXXMirrorData structures in BlenderDNA
-        const Field* f = s->Get("modifier");
+        const Field *f = s->Get("modifier");
         if (!f || f->offset != 0) {
             ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0");
             continue;
         }
 
-        s = conv_data.db.dna.Get( f->type );
+        s = conv_data.db.dna.Get(f->type);
         if (!s || s->name != "ModifierData") {
             ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member");
             continue;
@@ -110,30 +109,30 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_d
 
         // now, we can be sure that we should be fine to dereference *cur* as
         // ModifierData (with the above note).
-        const ModifierData& dat = cur->modifier;
+        const ModifierData &dat = cur->modifier;
 
-        const fpCreateModifier* curgod = creators;
-        std::vector< BlenderModifier* >::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end();
+        const fpCreateModifier *curgod = creators;
+        std::vector<BlenderModifier *>::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end();
 
-        for (;*curgod;++curgod,++curmod) { // allocate modifiers on the fly
+        for (; *curgod; ++curgod, ++curmod) { // allocate modifiers on the fly
             if (curmod == endmod) {
                 cached_modifiers->push_back((*curgod)());
 
                 endmod = cached_modifiers->end();
-                curmod = endmod-1;
+                curmod = endmod - 1;
             }
 
-            BlenderModifier* const modifier = *curmod;
-            if(modifier->IsActive(dat)) {
-                modifier->DoIt(out,conv_data,*static_cast<const ElemBase *>(cur),in,orig_object);
+            BlenderModifier *const modifier = *curmod;
+            if (modifier->IsActive(dat)) {
+                modifier->DoIt(out, conv_data, *static_cast<const ElemBase *>(cur), in, orig_object);
                 cnt++;
 
-                curgod = NULL;
+                curgod = nullptr;
                 break;
             }
         }
         if (curgod) {
-            ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: ",dat.name);
+            ASSIMP_LOG_WARN("Couldn't find a handler for modifier: ", dat.name);
         }
     }
 
@@ -141,26 +140,22 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_d
     // object, we still can't say whether our modifier implementations were
     // able to fully do their job.
     if (ful) {
-        ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for ",cnt," of ",ful," modifiers on `",orig_object.id.name,
-            "`, check log messages above for errors");
+        ASSIMP_LOG_DEBUG("BlendModifier: found handlers for ", cnt, " of ", ful, " modifiers on `", orig_object.id.name,
+                "`, check log messages above for errors");
     }
 }
 
-
-
 // ------------------------------------------------------------------------------------------------
-bool BlenderModifier_Mirror :: IsActive (const ModifierData& modin)
-{
+bool BlenderModifier_Mirror ::IsActive(const ModifierData &modin) {
     return modin.type == ModifierData::eModifierType_Mirror;
 }
 
 // ------------------------------------------------------------------------------------------------
-void  BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data,  const ElemBase& orig_modifier,
-    const Scene& /*in*/,
-    const Object& orig_object )
-{
+void BlenderModifier_Mirror ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier,
+        const Scene & /*in*/,
+        const Object &orig_object) {
     // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
-    const MirrorModifierData& mir = static_cast<const MirrorModifierData&>(orig_modifier);
+    const MirrorModifierData &mir = static_cast<const MirrorModifierData &>(orig_modifier);
     ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror);
 
     conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes);
@@ -169,48 +164,55 @@ void  BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data,  co
 
     // take all input meshes and clone them
     for (unsigned int i = 0; i < out.mNumMeshes; ++i) {
-        aiMesh* mesh;
-        SceneCombiner::Copy(&mesh,conv_data.meshes[out.mMeshes[i]]);
+        aiMesh *mesh;
+        SceneCombiner::Copy(&mesh, conv_data.meshes[out.mMeshes[i]]);
 
         const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f;
         const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f;
         const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f;
 
         if (mir.mirror_ob) {
-            const aiVector3D center( mir.mirror_ob->obmat[3][0],mir.mirror_ob->obmat[3][1],mir.mirror_ob->obmat[3][2] );
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mVertices[i];
+            const aiVector3D center(mir.mirror_ob->obmat[3][0], mir.mirror_ob->obmat[3][1], mir.mirror_ob->obmat[3][2]);
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mVertices[j];
 
-                v.x = center.x + xs*(center.x - v.x);
-                v.y = center.y + ys*(center.y - v.y);
-                v.z = center.z + zs*(center.z - v.z);
+                v.x = center.x + xs * (center.x - v.x);
+                v.y = center.y + ys * (center.y - v.y);
+                v.z = center.z + zs * (center.z - v.z);
             }
-        }
-        else {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mVertices[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+        } else {
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mVertices[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
         if (mesh->mNormals) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mNormals[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mNormals[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
         if (mesh->mTangents) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mTangents[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mTangents[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
         if (mesh->mBitangents) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mBitangents[i];
-                v.x *= xs;v.y *= ys;v.z *= zs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mBitangents[j];
+                v.x *= xs;
+                v.y *= ys;
+                v.z *= zs;
             }
         }
 
@@ -218,55 +220,53 @@ void  BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data,  co
         const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f;
 
         for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) {
-            for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                aiVector3D& v = mesh->mTextureCoords[n][i];
-                v.x *= us;v.y *= vs;
+            for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
+                aiVector3D &v = mesh->mTextureCoords[n][j];
+                v.x *= us;
+                v.y *= vs;
             }
         }
 
         // Only reverse the winding order if an odd number of axes were mirrored.
         if (xs * ys * zs < 0) {
-            for( unsigned int i = 0; i < mesh->mNumFaces; i++) {
-                aiFace& face = mesh->mFaces[i];
-                for( unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
-                    std::swap( face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
+            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]);
             }
         }
 
         conv_data.meshes->push_back(mesh);
     }
-    unsigned int* nind = new unsigned int[out.mNumMeshes*2];
+    unsigned int *nind = new unsigned int[out.mNumMeshes * 2];
 
-    std::copy(out.mMeshes,out.mMeshes+out.mNumMeshes,nind);
-    std::transform(out.mMeshes,out.mMeshes+out.mNumMeshes,nind+out.mNumMeshes,
-        [&out](unsigned int n) { return out.mNumMeshes + n; });
+    std::copy(out.mMeshes, out.mMeshes + out.mNumMeshes, nind);
+    std::transform(out.mMeshes, out.mMeshes + out.mNumMeshes, nind + out.mNumMeshes,
+            [&out](unsigned int n) { return out.mNumMeshes + n; });
 
     delete[] out.mMeshes;
     out.mMeshes = nind;
     out.mNumMeshes *= 2;
 
-    ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Mirror` modifier to `",
-        orig_object.id.name,"`");
+    ASSIMP_LOG_INFO("BlendModifier: Applied the `Mirror` modifier to `",
+            orig_object.id.name, "`");
 }
 
 // ------------------------------------------------------------------------------------------------
-bool BlenderModifier_Subdivision :: IsActive (const ModifierData& modin)
-{
+bool BlenderModifier_Subdivision ::IsActive(const ModifierData &modin) {
     return modin.type == ModifierData::eModifierType_Subsurf;
 }
 
 // ------------------------------------------------------------------------------------------------
-void  BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data,  const ElemBase& orig_modifier,
-    const Scene& /*in*/,
-    const Object& orig_object )
-{
+void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data, const ElemBase &orig_modifier,
+        const Scene & /*in*/,
+        const Object &orig_object) {
     // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
-    const SubsurfModifierData& mir = static_cast<const SubsurfModifierData&>(orig_modifier);
+    const SubsurfModifierData &mir = static_cast<const SubsurfModifierData &>(orig_modifier);
     ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf);
 
     Subdivider::Algorithm algo;
-    switch (mir.subdivType)
-    {
+    switch (mir.subdivType) {
     case SubsurfModifierData::TYPE_CatmullClarke:
         algo = Subdivider::CATMULL_CLARKE;
         break;
@@ -277,23 +277,23 @@ void  BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data
         break;
 
     default:
-        ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ",mir.subdivType);
+        ASSIMP_LOG_WARN("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType);
         return;
     };
 
     std::unique_ptr<Subdivider> subd(Subdivider::Create(algo));
     ai_assert(subd);
-    if ( conv_data.meshes->empty() ) {
+    if (conv_data.meshes->empty()) {
         return;
     }
-    aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
-    std::unique_ptr<aiMesh*[]> tempmeshes(new aiMesh*[out.mNumMeshes]());
+    aiMesh **const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
+    std::unique_ptr<aiMesh *[]> tempmeshes(new aiMesh *[out.mNumMeshes]());
 
-    subd->Subdivide(meshes,out.mNumMeshes,tempmeshes.get(),std::max( mir.renderLevels, mir.levels ),true);
-    std::copy(tempmeshes.get(),tempmeshes.get()+out.mNumMeshes,meshes);
+    subd->Subdivide(meshes, out.mNumMeshes, tempmeshes.get(), std::max(mir.renderLevels, mir.levels), true);
+    std::copy(tempmeshes.get(), tempmeshes.get() + out.mNumMeshes, meshes);
 
-    ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Subdivision` modifier to `",
-        orig_object.id.name,"`");
+    ASSIMP_LOG_INFO("BlendModifier: Applied the `Subdivision` modifier to `",
+            orig_object.id.name, "`");
 }
 
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 11 - 11
Engine/lib/assimp/code/Blender/BlenderModifier.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderModifier.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -52,9 +52,9 @@ namespace Assimp {
 namespace Blender {
 
 // -------------------------------------------------------------------------------------------
-/** 
+/**
  *  Dummy base class for all blender modifiers. Modifiers are reused between imports, so
- *  they should be stateless and not try to cache model data. 
+ *  they should be stateless and not try to cache model data.
  */
 // -------------------------------------------------------------------------------------------
 class BlenderModifier {
@@ -67,7 +67,7 @@ public:
     }
 
     // --------------------
-    /** 
+    /**
      *  Check if *this* modifier is active, given a ModifierData& block.
      */
     virtual bool IsActive( const ModifierData& /*modin*/) {
@@ -75,10 +75,10 @@ public:
     }
 
     // --------------------
-    /** 
+    /**
      *  Apply the modifier to a given output node. The original data used
      *  to construct the node is given as well. Not called unless IsActive()
-     *  was called and gave positive response. 
+     *  was called and gave positive response.
      */
     virtual void DoIt(aiNode& /*out*/,
         ConversionData& /*conv_data*/,
@@ -86,14 +86,14 @@ public:
         const Scene& /*in*/,
         const Object& /*orig_object*/
     ) {
-        ASSIMP_LOG_INFO_F("This modifier is not supported, skipping: ",orig_modifier.dna_type );
+        ASSIMP_LOG_INFO("This modifier is not supported, skipping: ",orig_modifier.dna_type );
         return;
     }
 };
 
 // -------------------------------------------------------------------------------------------
-/** 
- *  Manage all known modifiers and instance and apply them if necessary 
+/**
+ *  Manage all known modifiers and instance and apply them if necessary
  */
 // -------------------------------------------------------------------------------------------
 class BlenderModifierShowcase {
@@ -113,8 +113,8 @@ private:
 // MODIFIERS /////////////////////////////////////////////////////////////////////////////////
 
 // -------------------------------------------------------------------------------------------
-/** 
- *  Mirror modifier. Status: implemented. 
+/**
+ *  Mirror modifier. Status: implemented.
  */
 // -------------------------------------------------------------------------------------------
 class BlenderModifier_Mirror : public BlenderModifier {

+ 891 - 0
Engine/lib/assimp/code/AssetLib/Blender/BlenderScene.cpp

@@ -0,0 +1,891 @@
+/*
+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"
+
+namespace Assimp {
+namespace 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<CollectionObject>(
+        CollectionObject &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+    {
+        //std::shared_ptr<CollectionObject> prev;
+        //ReadFieldPtr<ErrorPolicy_Fail>(prev, "*prev", db);
+        //dest.prev = prev.get();
+
+        std::shared_ptr<Object> ob;
+        ReadFieldPtr<ErrorPolicy_Igno>(ob, "*ob", db);
+        dest.ob = ob.get();
+    }
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CollectionChild>(
+        CollectionChild &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.prev, "*prev", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.collection, "*collection", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<Collection>(
+        Collection &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Fail>(dest.gobject, "gobject", db);
+    ReadField<ErrorPolicy_Fail>(dest.children, "children", 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);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.master_collection, "*master_collection", 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>);
+    converters["Collection"] = DNA::FactoryPair(&Structure::Allocate<Collection>, &Structure::Convert<Collection>);
+    converters["CollectionChild"] = DNA::FactoryPair(&Structure::Allocate<CollectionChild>, &Structure::Convert<CollectionChild>);
+    converters["CollectionObject"] = DNA::FactoryPair(&Structure::Allocate<CollectionObject>, &Structure::Convert<CollectionObject>);
+}
+
+} // namespace Blender
+} //namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 278 - 278
Engine/lib/assimp/code/Blender/BlenderScene.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderScene.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -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,19 +94,20 @@ 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;
 struct Image;
+struct Collection;
 
 #include <memory>
 
@@ -117,7 +117,7 @@ static const size_t MaxNameLen = 1024;
 
 // -------------------------------------------------------------------------------
 struct ID : ElemBase {
-    char name[ MaxNameLen ] WARN;
+    char name[MaxNameLen] WARN;
     short flag;
 };
 
@@ -127,17 +127,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;
 };
 
@@ -149,6 +148,26 @@ struct Group : ElemBase {
     std::shared_ptr<GroupObject> gobject;
 };
 
+// -------------------------------------------------------------------------------
+struct CollectionObject : ElemBase {
+    //CollectionObject* prev;
+    std::shared_ptr<CollectionObject> next;
+    Object *ob;
+};
+
+// -------------------------------------------------------------------------------
+struct CollectionChild : ElemBase {
+    std::shared_ptr<CollectionChild> next, prev;
+    std::shared_ptr<Collection> collection;
+};
+
+// -------------------------------------------------------------------------------
+struct Collection : ElemBase {
+    ID id FAIL;
+    ListBase gobject; // CollectionObject
+    ListBase children; // CollectionChild
+};
+
 // -------------------------------------------------------------------------------
 struct World : ElemBase {
     ID id FAIL;
@@ -157,23 +176,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; // read 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 +206,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 +219,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 +248,12 @@ 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 +265,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 +414,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 +444,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 +483,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 +504,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 +649,7 @@ struct ModifierData : ElemBase  {
 };
 
 // -------------------------------------------------------------------------------
-struct SubsurfModifierData : ElemBase  {
+struct SubsurfModifierData : ElemBase {
 
     enum Type {
 
@@ -646,13 +659,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 +673,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 +690,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 +715,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
     }
@@ -745,14 +750,12 @@ struct Scene : ElemBase {
     std::shared_ptr<Object> camera WARN;
     std::shared_ptr<World> world WARN;
     std::shared_ptr<Base> basact WARN;
+    std::shared_ptr<Collection> master_collection WARN;
 
     ListBase base;
 
-    Scene()
-    : ElemBase()
-    , camera()
-    , world()
-    , basact() {
+    Scene() :
+            ElemBase(), camera(), world(), basact(), master_collection() {
         // empty
     }
 };
@@ -783,9 +786,9 @@ struct Image : ElemBase {
     short animspeed;
 
     short gen_x, gen_y, gen_type;
-    
-    Image()
-    : ElemBase() {
+
+    Image() :
+            ElemBase() {
         // empty
     }
 };
@@ -795,33 +798,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 +879,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 +889,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 +945,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 +953,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 +972,12 @@ struct MTex : ElemBase {
     //float shadowfac;
     //float zenupfac, zendownfac, blendfac;
 
-    MTex()
-    : ElemBase() {
+    MTex() :
+            ElemBase() {
         // empty
     }
 };
 
-}
-}
+} // namespace Blender
+} // namespace Assimp
 #endif

+ 7 - 1
Engine/lib/assimp/code/Blender/BlenderSceneGen.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderSceneGen.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (ASSIMP)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2016, ASSIMP Development Team
+Copyright (c) 2006-2020, ASSIMP Development Team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -62,6 +62,12 @@ template <> void Structure :: Convert<Group> (
     ) const
 ;
 
+template <> void Structure::Convert<Collection>(
+    Collection& dest,
+    const FileDatabase& db
+    ) const
+;
+
 template <> void Structure :: Convert<MTex> (
     MTex& dest,
     const FileDatabase& db

+ 8 - 2
Engine/lib/assimp/code/Blender/BlenderTessellator.cpp → Engine/lib/assimp/code/AssetLib/Blender/BlenderTessellator.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -386,7 +386,14 @@ void BlenderTessellatorP2T::ReferencePoints( std::vector< Blender::PointP2T >& p
 // ------------------------------------------------------------------------------------------------
 inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& point ) const
 {
+#if defined __clang__
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Winvalid-offsetof"
+#endif // __clang__
     unsigned int pointOffset = offsetof( PointP2T, point2D );
+#if defined __clang__
+#    pragma clang diagnostic pop
+#endif
     PointP2T& pointStruct = *reinterpret_cast< PointP2T* >( reinterpret_cast< char* >( &point ) - pointOffset );
     if ( pointStruct.magic != static_cast<int>( BLEND_TESS_MAGIC ) )
     {
@@ -394,7 +401,6 @@ inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& poi
     }
     return pointStruct;
 }
-
 // ------------------------------------------------------------------------------------------------
 void BlenderTessellatorP2T::MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const
 {

+ 1 - 1
Engine/lib/assimp/code/Blender/BlenderTessellator.h → Engine/lib/assimp/code/AssetLib/Blender/BlenderTessellator.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 34 - 35
Engine/lib/assimp/code/C4D/C4DImporter.cpp → Engine/lib/assimp/code/AssetLib/C4D/C4DImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2021, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -51,7 +51,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endif
 
 #include "C4DImporter.h"
-#include <assimp/TinyFormatter.h>
 #include <memory>
 #include <assimp/IOSystem.hpp>
 #include <assimp/scene.h>
@@ -65,7 +64,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "c4d_file.h"
 #include "default_alien_overloads.h"
 
-using namespace melange;
+namespace {
+
+aiString aiStringFrom(cineware::String const & cinestring) {
+    aiString result;
+    cinestring.GetCString(result.data, MAXLEN-1);
+    result.length = static_cast<ai_uint32>(cinestring.GetLength());
+    return result;
+}
+
+}
+
+using namespace Assimp;
+using namespace cineware;
 
 // overload this function and fill in your own unique data
 void GetWriterInfo(int &id, String &appname) {
@@ -73,9 +84,6 @@ void GetWriterInfo(int &id, String &appname) {
     appname = "Open Asset Import Library";
 }
 
-using namespace Assimp;
-using namespace Assimp::Formatter;
-
 namespace Assimp {
     template<> const char* LogFunctions<C4DImporter>::Prefix() {
         static auto prefix = "C4D: ";
@@ -109,14 +117,14 @@ C4DImporter::~C4DImporter() {
 }
 
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
+bool C4DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const {
     const std::string& extension = GetExtension(pFile);
     if (extension == "c4d") {
         return true;
     } else if ((!extension.length() || checkSig) && pIOHandler)   {
         // TODO
     }
-
+    
     return false;
 }
 
@@ -125,11 +133,6 @@ const aiImporterDesc* C4DImporter::GetInfo () const {
     return &desc;
 }
 
-// ------------------------------------------------------------------------------------------------
-void C4DImporter::SetupProperties(const Importer* /*pImp*/) {
-    // nothing to be done for the moment
-}
-
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
@@ -154,8 +157,14 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
         ThrowException("failed to read document " + pFile);
     }
 
+    // Generate the root-node
     pScene->mRootNode = new aiNode("<C4DRoot>");
 
+    // convert left-handed to right-handed
+    pScene->mRootNode->mTransformation.a1 = 0.01f;
+    pScene->mRootNode->mTransformation.b2 = 0.01f;
+    pScene->mRootNode->mTransformation.c3 = -0.01f;
+
     // first convert all materials
     ReadMaterials(doc->GetFirstMaterial());
 
@@ -199,8 +208,8 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
 
 
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) {
-    // based on Melange sample code (C4DImportExport.cpp)
+bool C4DImporter::ReadShader(aiMaterial* out, BaseShader* shader) {
+    // based on Cineware sample code (C4DImportExport.cpp)
     while(shader) {
         if(shader->GetType() == Xlayer) {
             BaseContainer* container = shader->GetDataInstance();
@@ -242,13 +251,11 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) {
                 lsl = lsl->GetNext();
             }
         } else if ( shader->GetType() == Xbitmap ) {
-            aiString path;
-            shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
-            path.length = ::strlen(path.data);
+            auto const path = aiStringFrom(shader->GetFileName().GetString());
             out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
             return true;
         } else {
-            LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
+            LogWarn("ignoring shader type: ", GetObjectTypeName(shader->GetType()));
         }
         shader = shader->GetNext();
     }
@@ -257,18 +264,15 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::ReadMaterials(melange::BaseMaterial* mat) {
-    // based on Melange sample code
+void C4DImporter::ReadMaterials(BaseMaterial* mat) {
+    // based on Cineware sample code
     while (mat) {
-        const String& name = mat->GetName();
         if (mat->GetType() == Mmaterial) {
             aiMaterial* out = new aiMaterial();
             material_mapping[mat] = static_cast<unsigned int>(materials.size());
             materials.push_back(out);
 
-            aiString ai_name;
-            name.GetCString(ai_name.data, MAXLEN-1);
-            ai_name.length = ::strlen(ai_name.data);
+            auto const ai_name = aiStringFrom(mat->GetName());
             out->AddProperty(&ai_name, AI_MATKEY_NAME);
 
             Material& m = dynamic_cast<Material&>(*mat);
@@ -294,7 +298,7 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat) {
                 ReadShader(out, shader);
             }
         } else {
-            LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
+            LogWarn("ignoring plugin material: ", GetObjectTypeName(mat->GetType()));
         }
         mat = mat->GetNext();
     }
@@ -305,19 +309,15 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) {
     ai_assert(parent != nullptr );
     std::vector<aiNode*> nodes;
 
-    // based on Melange sample code
+    // based on Cineware sample code
     while (object) {
-        const String& name = object->GetName();
         const LONG type = object->GetType();
         const Matrix& ml = object->GetMl();
 
-        aiString string;
-        name.GetCString(string.data, MAXLEN-1);
-        string.length = ::strlen(string.data);
         aiNode* const nd = new aiNode();
 
         nd->mParent = parent;
-        nd->mName = string;
+        nd->mName = aiStringFrom(object->GetName());
 
         nd->mTransformation.a1 = ml.v1.x;
         nd->mTransformation.b1 = ml.v1.y;
@@ -352,7 +352,7 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) {
                 meshes.push_back(mesh);
             }
         } else {
-            LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
+            LogWarn("ignoring object: ", GetObjectTypeName(type));
         }
 
         RecurseHierarchy(object->GetDown(), nd);
@@ -370,7 +370,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
     ai_assert(object != nullptr);
     ai_assert( object->GetType() == Opolygon );
 
-    // based on Melange sample code
+    // based on Cineware sample code
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
     ai_assert(polyObject != nullptr);
 
@@ -618,4 +618,3 @@ unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj) {
 }
 
 #endif // ASSIMP_BUILD_NO_C4D_IMPORTER
-

+ 15 - 24
Engine/lib/assimp/code/C4D/C4DImporter.h → Engine/lib/assimp/code/AssetLib/C4D/C4DImporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2021, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -56,8 +56,8 @@ struct aiMaterial;
 
 struct aiImporterDesc;
 
-namespace melange {
-    class BaseObject; // c4d_file.h
+namespace cineware {
+    class BaseObject;
     class PolygonObject;
     class BaseMaterial;
     class BaseShader;
@@ -71,43 +71,34 @@ namespace Assimp  {
     }
 
 // -------------------------------------------------------------------------------------------
-/** Importer class to load Cinema4D files using the Melange library to be obtained from
- *  www.plugincafe.com
+/** Importer class to load Cinema4D files using the Cineware library to be obtained from
+ *  https://developers.maxon.net
  *
- *  Note that Melange is not free software. */
+ *  Note that Cineware is not free software. */
 // -------------------------------------------------------------------------------------------
 class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter> {
 public:
-    C4DImporter();
-    ~C4DImporter();
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+    bool CanRead( const std::string& pFile, IOSystem*, bool checkSig) const override;
 
 protected:
 
-    // --------------------
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
-    // --------------------
-    void SetupProperties(const Importer* pImp);
-
-    // --------------------
-    void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
+    void InternReadFile( const std::string& pFile, aiScene*, IOSystem* ) override;
 
 private:
 
-    void ReadMaterials(melange::BaseMaterial* mat);
-    void RecurseHierarchy(melange::BaseObject* object, aiNode* parent);
-    aiMesh* ReadMesh(melange::BaseObject* object);
-    unsigned int ResolveMaterial(melange::PolygonObject* obj);
+    void ReadMaterials(cineware::BaseMaterial* mat);
+    void RecurseHierarchy(cineware::BaseObject* object, aiNode* parent);
+    aiMesh* ReadMesh(cineware::BaseObject* object);
+    unsigned int ResolveMaterial(cineware::PolygonObject* obj);
 
-    bool ReadShader(aiMaterial* out, melange::BaseShader* shader);
+    bool ReadShader(aiMaterial* out, cineware::BaseShader* shader);
 
     std::vector<aiMesh*> meshes;
     std::vector<aiMaterial*> materials;
 
-    typedef std::map<melange::BaseMaterial*, unsigned int> MaterialMap;
+    typedef std::map<cineware::BaseMaterial*, unsigned int> MaterialMap;
     MaterialMap material_mapping;
 
 }; // !class C4DImporter

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 256 - 288
Engine/lib/assimp/code/AssetLib/COB/COBLoader.cpp


+ 152 - 0
Engine/lib/assimp/code/AssetLib/COB/COBLoader.h

@@ -0,0 +1,152 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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  COBLoader.h
+ *  @brief Declaration of the TrueSpace (*.cob,*.scn) importer class.
+ */
+#ifndef INCLUDED_AI_COB_LOADER_H
+#define INCLUDED_AI_COB_LOADER_H
+
+#include <assimp/BaseImporter.h>
+#include <assimp/StreamReader.h>
+
+struct aiNode;
+
+namespace Assimp {
+class LineSplitter;
+
+// TinyFormatter.h
+namespace Formatter {
+template <typename T, typename TR, typename A>
+class basic_formatter;
+typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>> format;
+} // namespace Formatter
+
+// COBScene.h
+namespace COB {
+struct ChunkInfo;
+struct Node;
+struct Scene;
+} // namespace COB
+
+// -------------------------------------------------------------------------------------------
+/** Importer class to load TrueSpace files (cob,scn) up to v6.
+ *
+ *  Currently relatively limited, loads only ASCII files and needs more test coverage. */
+// -------------------------------------------------------------------------------------------
+class COBImporter : public BaseImporter {
+public:
+    COBImporter();
+    ~COBImporter() override;
+
+    // --------------------
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+            bool checkSig) const override;
+
+protected:
+    // --------------------
+    const aiImporterDesc *GetInfo() const override;
+
+    // --------------------
+    void SetupProperties(const Importer *pImp) override;
+
+    // --------------------
+    void InternReadFile(const std::string &pFile, aiScene *pScene,
+            IOSystem *pIOHandler) override;
+
+private:
+    // -------------------------------------------------------------------
+    /** Prepend 'COB: ' and throw msg.*/
+    AI_WONT_RETURN static void ThrowException(const std::string &msg) AI_WONT_RETURN_SUFFIX;
+
+    // -------------------------------------------------------------------
+    /** @brief Read from an ascii scene/object file
+     *  @param out Receives output data.
+     *  @param stream Stream to read from. */
+    void ReadAsciiFile(COB::Scene &out, StreamReaderLE *stream);
+
+    // -------------------------------------------------------------------
+    /** @brief Read from a binary scene/object file
+     *  @param out Receives output data.
+     *  @param stream Stream to read from.  */
+    void ReadBinaryFile(COB::Scene &out, StreamReaderLE *stream);
+
+    // Conversion to Assimp output format
+    aiNode *BuildNodes(const COB::Node &root, const COB::Scene &scin, aiScene *fill);
+
+private:
+    // ASCII file support
+
+    void UnsupportedChunk_Ascii(LineSplitter &splitter, const COB::ChunkInfo &nfo, const char *name);
+    void ReadChunkInfo_Ascii(COB::ChunkInfo &out, const LineSplitter &splitter);
+    void ReadBasicNodeInfo_Ascii(COB::Node &msh, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    template <typename T>
+    void ReadFloat3Tuple_Ascii(T &fill, const char **in);
+
+    void ReadPolH_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadBitM_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadMat1_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadGrou_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadBone_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadCame_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadLght_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadUnit_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadChan_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+
+    // Binary file support
+
+    void UnsupportedChunk_Binary(StreamReaderLE &reader, const COB::ChunkInfo &nfo, const char *name);
+    void ReadString_Binary(std::string &out, StreamReaderLE &reader);
+    void ReadBasicNodeInfo_Binary(COB::Node &msh, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+
+    void ReadPolH_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadBitM_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadMat1_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadCame_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadLght_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadGrou_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadUnit_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+
+}; // !class COBImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC

+ 7 - 8
Engine/lib/assimp/code/COB/COBScene.h → Engine/lib/assimp/code/AssetLib/COB/COBScene.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,16 +42,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file  COBScene.h
 *  @brief Utilities for the COB importer.
 */
+#pragma once
 #ifndef INCLUDED_AI_COB_SCENE_H
 #define INCLUDED_AI_COB_SCENE_H
 
-#include <memory>
-#include <deque>
-#include <map>
-
 #include <assimp/BaseImporter.h>
 #include <assimp/material.h>
 
+#include <deque>
+#include <map>
+
 namespace Assimp {
 namespace COB {
 
@@ -75,10 +74,10 @@ struct Face
 
 // ------------------
 /** COB chunk header information */
+const unsigned int NO_SIZE = UINT_MAX;
+
 struct ChunkInfo
 {
-    enum {NO_SIZE=UINT_MAX};
-
     ChunkInfo ()
         :   id        (0)
         ,   parent_id (0)

+ 7 - 16
Engine/lib/assimp/code/CSM/CSMLoader.cpp → Engine/lib/assimp/code/AssetLib/CSM/CSMLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 
@@ -90,19 +90,10 @@ CSMImporter::~CSMImporter()
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
 {
-    // check file extension
-    const std::string extension = GetExtension(pFile);
-
-    if( extension == "csm")
-        return true;
-
-    if ((checkSig || !extension.length()) && pIOHandler) {
-        const char* tokens[] = {"$Filename"};
-        return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
-    }
-    return false;
+    static const char* tokens[] = {"$Filename"};
+    return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -127,8 +118,8 @@ void CSMImporter::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() == NULL) {
-        throw DeadlyImportError( "Failed to open CSM file " + pFile + ".");
+    if( file.get() == nullptr) {
+        throw DeadlyImportError( "Failed to open CSM file ", pFile, ".");
     }
 
     // allocate storage and copy the contents of the file to a memory buffer
@@ -178,7 +169,7 @@ void CSMImporter::InternReadFile( const std::string& pFile,
                         *ot++ = *buffer++;
 
                     *ot = '\0';
-                    nda->mNodeName.length = (size_t)(ot-nda->mNodeName.data);
+                    nda->mNodeName.length = static_cast<ai_uint32>(ot-nda->mNodeName.data);
                 }
 
                 anim->mNumChannels = static_cast<unsigned int>(anims_temp.size());

+ 12 - 16
Engine/lib/assimp/code/CSM/CSMLoader.h → Engine/lib/assimp/code/AssetLib/CSM/CSMLoader.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -48,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/BaseImporter.h>
 
-namespace Assimp    {
+namespace Assimp {
 
 // ---------------------------------------------------------------------------
 /** Importer class to load MOCAPs in CharacterStudio Motion format.
@@ -59,35 +58,32 @@ namespace Assimp    {
  *  Link to file format specification:
  *  <max_8_dvd>\samples\Motion\Docs\CSM.rtf
 */
-class CSMImporter : public BaseImporter
-{
+class CSMImporter : public BaseImporter {
 public:
     CSMImporter();
-    ~CSMImporter();
-
+    ~CSMImporter() override;
 
-public:
     // -------------------------------------------------------------------
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+            bool checkSig) const override;
 
 protected:
-
     // -------------------------------------------------------------------
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc *GetInfo() const override;
 
     // -------------------------------------------------------------------
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer *pImp) override;
 
     // -------------------------------------------------------------------
-    void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
+    void InternReadFile(const std::string &pFile, aiScene *pScene,
+            IOSystem *pIOHandler) override;
 
 private:
-
     bool noSkeletonMesh;
 
 }; // end of class CSMImporter
+
 } // end of namespace Assimp
+
 #endif // AI_AC3DIMPORTER_H_INC
 

+ 1748 - 0
Engine/lib/assimp/code/AssetLib/Collada/ColladaExporter.cpp

@@ -0,0 +1,1748 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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/ColladaMetaData.h>
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/Exceptional.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 <ctime>
+#include <memory>
+
+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);
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 reduce ID collisions
+            idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT];
+        }
+    }
+
+    return idEncoded.str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Helper functions to create unique ids
+inline bool IsUniqueId(const std::unordered_set<std::string> &idSet, const std::string &idStr) {
+    return (idSet.find(idStr) == idSet.end());
+}
+
+inline std::string MakeUniqueId(const std::unordered_set<std::string> &idSet, const std::string &idPrefix, const std::string &postfix) {
+    std::string result(idPrefix + postfix);
+    if (!IsUniqueId(idSet, result)) {
+        // Select a number to append
+        size_t idnum = 1;
+        do {
+            result = idPrefix + '_' + ai_to_string(idnum) + postfix;
+            ++idnum;
+        } while (!IsUniqueId(idSet, result));
+    }
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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),
+        mScene(pScene),
+        endstr("\n") {
+    // 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);
+
+    // start writing the file
+    WriteFile();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor
+ColladaExporter::~ColladaExporter() {
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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();
+
+    // Add node names to the unique id database first so they are most likely to use their names as unique ids
+    CreateNodeIds(mScene->mRootNode);
+
+    WriteCamerasLibrary();
+    WriteLightsLibrary();
+    WriteMaterials();
+    WriteGeometryLibrary();
+    WriteControllerLibrary();
+
+    WriteSceneLibrary();
+
+    // customized, Writes the animation library
+    WriteAnimationsLibrary();
+
+    // instantiate the scene(s)
+    // For Assimp there will only ever be one
+    mOutput << startstr << "<scene>" << endstr;
+    PushTag();
+    mOutput << startstr << "<instance_visual_scene url=\"#" + mSceneId + "\" />" << 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(nullptr);
+    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();
+
+    mAdd_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 {
+        mAdd_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 {
+        mAdd_root_node = true;
+    }
+
+    if (!position.Equal(aiVector3D(0, 0, 0))) {
+        mAdd_root_node = true;
+    }
+
+    // Assimp root nodes can have meshes, Collada Scenes cannot
+    if (mScene->mRootNode->mNumChildren == 0 || mScene->mRootNode->mMeshes != 0) {
+        mAdd_root_node = true;
+    }
+
+    if (mAdd_root_node) {
+        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 == nullptr) {
+                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 cameraId = GetObjectUniqueId(AiObjectType::Camera, pIndex);
+    const std::string cameraName = GetObjectName(AiObjectType::Camera, pIndex);
+
+    mOutput << startstr << "<camera id=\"" << cameraId << "\" 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 lightId = GetObjectUniqueId(AiObjectType::Light, pIndex);
+    const std::string lightName = GetObjectName(AiObjectType::Light, pIndex);
+
+    mOutput << startstr << "<light id=\"" << lightId << "\" 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
+bool 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, nullptr, &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<DeadlyExportError>(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;
+    }
+    return poSurface.exist;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 &imageId) {
+    if (!pSurface.texture.empty()) {
+        mOutput << startstr << "<image id=\"" << imageId << "\">" << 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 &imageId) {
+    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=\"" << imageId << "\" 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 &materialId) {
+    // 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=\"" << materialId << "-" << pTypeName << "-surface\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<surface type=\"2D\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<init_from>" << materialId << "-" << pTypeName << "-image</init_from>" << endstr;
+        PopTag();
+        mOutput << startstr << "</surface>" << endstr;
+        PopTag();
+        mOutput << startstr << "</newparam>" << endstr;
+
+        mOutput << startstr << "<newparam sid=\"" << materialId << "-" << pTypeName << "-sampler\">" << endstr;
+        PushTag();
+        mOutput << startstr << "<sampler2D>" << endstr;
+        PushTag();
+        mOutput << startstr << "<source>" << materialId << "-" << 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() {
+    std::vector<Material> materials;
+    materials.resize(mScene->mNumMaterials);
+
+    /// collect all materials from the scene
+    size_t numTextures = 0;
+    for (size_t a = 0; a < mScene->mNumMaterials; ++a) {
+        Material &material = materials[a];
+        material.id = GetObjectUniqueId(AiObjectType::Material, a);
+        material.name = GetObjectName(AiObjectType::Material, a);
+
+        const aiMaterial &mat = *(mScene->mMaterials[a]);
+        aiShadingMode shading = aiShadingMode_Flat;
+        material.shading_model = "phong";
+        if (mat.Get(AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) {
+            if (shading == aiShadingMode_Phong) {
+                material.shading_model = "phong";
+            } else if (shading == aiShadingMode_Blinn) {
+                material.shading_model = "blinn";
+            } else if (shading == aiShadingMode_NoShading) {
+                material.shading_model = "constant";
+            } else if (shading == aiShadingMode_Gouraud) {
+                material.shading_model = "lambert";
+            }
+        }
+
+        if (ReadMaterialSurface(material.ambient, mat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT))
+            ++numTextures;
+        if (ReadMaterialSurface(material.diffuse, mat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE))
+            ++numTextures;
+        if (ReadMaterialSurface(material.specular, mat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR))
+            ++numTextures;
+        if (ReadMaterialSurface(material.emissive, mat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE))
+            ++numTextures;
+        if (ReadMaterialSurface(material.reflective, mat, aiTextureType_REFLECTION, AI_MATKEY_COLOR_REFLECTIVE))
+            ++numTextures;
+        if (ReadMaterialSurface(material.transparent, mat, aiTextureType_OPACITY, AI_MATKEY_COLOR_TRANSPARENT))
+            ++numTextures;
+        if (ReadMaterialSurface(material.normal, mat, aiTextureType_NORMALS, nullptr, 0, 0))
+            ++numTextures;
+
+        material.shininess.exist = mat.Get(AI_MATKEY_SHININESS, material.shininess.value) == aiReturn_SUCCESS;
+        material.transparency.exist = mat.Get(AI_MATKEY_OPACITY, material.transparency.value) == aiReturn_SUCCESS;
+        material.index_refraction.exist = mat.Get(AI_MATKEY_REFRACTI, material.index_refraction.value) == aiReturn_SUCCESS;
+    }
+
+    // output textures if present
+    if (numTextures > 0) {
+        mOutput << startstr << "<library_images>" << endstr;
+        PushTag();
+        for (const Material &mat : materials) {
+            WriteImageEntry(mat.ambient, mat.id + "-ambient-image");
+            WriteImageEntry(mat.diffuse, mat.id + "-diffuse-image");
+            WriteImageEntry(mat.specular, mat.id + "-specular-image");
+            WriteImageEntry(mat.emissive, mat.id + "-emission-image");
+            WriteImageEntry(mat.reflective, mat.id + "-reflective-image");
+            WriteImageEntry(mat.transparent, mat.id + "-transparent-image");
+            WriteImageEntry(mat.normal, mat.id + "-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 (const Material &mat : materials) {
+            // this is so ridiculous it must be right
+            mOutput << startstr << "<effect id=\"" << mat.id << "-fx\" name=\"" << mat.name << "\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<profile_COMMON>" << endstr;
+            PushTag();
+
+            // write sampler- and surface params for the texture entries
+            WriteTextureParamEntry(mat.emissive, "emission", mat.id);
+            WriteTextureParamEntry(mat.ambient, "ambient", mat.id);
+            WriteTextureParamEntry(mat.diffuse, "diffuse", mat.id);
+            WriteTextureParamEntry(mat.specular, "specular", mat.id);
+            WriteTextureParamEntry(mat.reflective, "reflective", mat.id);
+            WriteTextureParamEntry(mat.transparent, "transparent", mat.id);
+            WriteTextureParamEntry(mat.normal, "normal", mat.id);
+
+            mOutput << startstr << "<technique sid=\"standard\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<" << mat.shading_model << ">" << endstr;
+            PushTag();
+
+            WriteTextureColorEntry(mat.emissive, "emission", mat.id + "-emission-sampler");
+            WriteTextureColorEntry(mat.ambient, "ambient", mat.id + "-ambient-sampler");
+            WriteTextureColorEntry(mat.diffuse, "diffuse", mat.id + "-diffuse-sampler");
+            WriteTextureColorEntry(mat.specular, "specular", mat.id + "-specular-sampler");
+            WriteFloatEntry(mat.shininess, "shininess");
+            WriteTextureColorEntry(mat.reflective, "reflective", mat.id + "-reflective-sampler");
+            WriteTextureColorEntry(mat.transparent, "transparent", mat.id + "-transparent-sampler");
+            WriteFloatEntry(mat.transparency, "transparency");
+            WriteFloatEntry(mat.index_refraction, "index_of_refraction");
+
+            if (!mat.normal.texture.empty()) {
+                WriteTextureColorEntry(mat.normal, "bump", mat.id + "-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=\"" << mat.id << "\" name=\"" << mat.name << "\">" << endstr;
+            PushTag();
+            mOutput << startstr << "<instance_effect url=\"#" << mat.id << "-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];
+    // Is there a skin controller?
+    if (mesh->mNumBones == 0 || mesh->mNumFaces == 0 || mesh->mNumVertices == 0)
+        return;
+
+    const std::string idstr = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
+    const std::string namestr = GetObjectName(AiObjectType::Mesh, pIndex);
+
+    mOutput << startstr << "<controller id=\"" << idstr << "-skin\" ";
+    mOutput << "name=\"skinCluster" << pIndex << "\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<skin source=\"#" << idstr << "\">" << 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=\"" << idstr << "-skin-joints\" name=\"" << namestr << "-skin-joints\">" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<Name_array id=\"" << idstr << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
+
+    for (size_t i = 0; i < mesh->mNumBones; ++i)
+        mOutput << GetBoneUniqueId(mesh->mBones[i]) << ' ';
+
+    mOutput << "</Name_array>" << endstr;
+
+    mOutput << startstr << "<technique_common>" << endstr;
+    PushTag();
+
+    mOutput << startstr << "<accessor source=\"#" << idstr << "-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=\"#" << idstr << "-skin-joints\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"INV_BIND_MATRIX\" source=\"#" << idstr << "-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=\"#" << idstr << "-skin-joints\" offset=\"0\"></input>" << endstr;
+    mOutput << startstr << "<input semantic=\"WEIGHT\" source=\"#" << idstr << "-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 geometryId = GetObjectUniqueId(AiObjectType::Mesh, pIndex);
+    const std::string geometryName = GetObjectName(AiObjectType::Mesh, pIndex);
+
+    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(geometryId + "-positions", FloatType_Vector, (ai_real *)mesh->mVertices, mesh->mNumVertices);
+    // Normals, if any
+    if (mesh->HasNormals())
+        WriteFloatArray(geometryId + "-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(geometryId + "-tex" + ai_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(geometryId + "-color" + ai_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() {
+    // Determine if we are using the aiScene root or our own
+    std::string sceneName("Scene");
+    if (mAdd_root_node) {
+        mSceneId = MakeUniqueId(mUniqueIds, sceneName, std::string());
+        mUniqueIds.insert(mSceneId);
+    } else {
+        mSceneId = GetNodeUniqueId(mScene->mRootNode);
+        sceneName = GetNodeName(mScene->mRootNode);
+    }
+
+    mOutput << startstr << "<library_visual_scenes>" << endstr;
+    PushTag();
+    mOutput << startstr << "<visual_scene id=\"" + mSceneId + "\" name=\"" + sceneName + "\">" << endstr;
+    PushTag();
+
+    if (mAdd_root_node) {
+        // Export the root node
+        WriteNode(mScene->mRootNode);
+    } else {
+        // Have already exported the root node
+        for (size_t a = 0; a < mScene->mRootNode->mNumChildren; ++a)
+            WriteNode(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 animationNameEscaped = GetObjectName(AiObjectType::Animation, pIndex);
+    const std::string idstrEscaped = GetObjectUniqueId(AiObjectType::Animation, pIndex);
+
+    mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animationNameEscaped + "\">" << 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 aiString &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 (name == bone->mName) {
+                return bone;
+            }
+        }
+    }
+    return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Helper to find the node associated with a bone in the scene
+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 = nullptr;
+            if (aChild) {
+                foundFromChild = findBoneNode(aChild, bone);
+                if (foundFromChild) {
+                    return foundFromChild;
+                }
+            }
+        }
+    }
+
+    return nullptr;
+}
+
+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) != nullptr) {
+                    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 nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively writes the given node
+void ColladaExporter::WriteNode(const aiNode *pNode) {
+    // If the node is associated with a bone, it is a joint node (JOINT)
+    // otherwise it is a normal node (NODE)
+    // Assimp-specific: nodes with no name cannot be associated with bones
+    const char *node_type;
+    bool is_joint, is_skeleton_root = false;
+    if (pNode->mName.length == 0 || nullptr == findBone(mScene, pNode->mName)) {
+        node_type = "NODE";
+        is_joint = false;
+    } else {
+        node_type = "JOINT";
+        is_joint = true;
+        if (!pNode->mParent || nullptr == findBone(mScene, pNode->mParent->mName)) {
+            is_skeleton_root = true;
+        }
+    }
+
+    const std::string node_id = GetNodeUniqueId(pNode);
+    const std::string node_name = GetNodeName(pNode);
+    mOutput << startstr << "<node ";
+    if (is_skeleton_root) {
+        mFoundSkeletonRootNodeID = node_id; // For now, only support one skeleton in a scene.
+    }
+    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=\"#" << GetObjectUniqueId(AiObjectType::Camera, i) << "\"/>" << 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=\"#" << GetObjectUniqueId(AiObjectType::Light, i) << "\"/>" << 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 meshId = GetObjectUniqueId(AiObjectType::Mesh, pNode->mMeshes[a]);
+
+            if (mesh->mNumBones == 0) {
+                mOutput << startstr << "<instance_geometry url=\"#" << meshId << "\">" << endstr;
+                PushTag();
+            } else {
+                mOutput << startstr
+                        << "<instance_controller url=\"#" << meshId << "-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(mScene, mesh);
+                if (skeletonRootBoneNode) {
+                    mFoundSkeletonRootNodeID = GetNodeUniqueId(skeletonRootBoneNode);
+                }
+                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=\"#" << GetObjectUniqueId(AiObjectType::Material, mesh->mMaterialIndex) << "\">" << 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(pNode->mChildren[a]);
+
+    PopTag();
+    mOutput << startstr << "</node>" << endstr;
+}
+
+void ColladaExporter::CreateNodeIds(const aiNode *node) {
+    GetNodeUniqueId(node);
+    for (size_t a = 0; a < node->mNumChildren; ++a)
+        CreateNodeIds(node->mChildren[a]);
+}
+
+std::string ColladaExporter::GetNodeUniqueId(const aiNode *node) {
+    // Use the pointer as the key. This is safe because the scene is immutable.
+    auto idIt = mNodeIdMap.find(node);
+    if (idIt != mNodeIdMap.cend())
+        return idIt->second;
+
+    // Prefer the requested Collada Id if extant
+    std::string idStr;
+    aiString origId;
+    if (node->mMetaData && node->mMetaData->Get(AI_METADATA_COLLADA_ID, origId)) {
+        idStr = origId.C_Str();
+    } else {
+        idStr = node->mName.C_Str();
+    }
+    // Make sure the requested id is valid
+    if (idStr.empty())
+        idStr = "node";
+    else
+        idStr = XMLIDEncode(idStr);
+
+    // Ensure it's unique
+    idStr = MakeUniqueId(mUniqueIds, idStr, std::string());
+    mUniqueIds.insert(idStr);
+    mNodeIdMap.insert(std::make_pair(node, idStr));
+    return idStr;
+}
+
+std::string ColladaExporter::GetNodeName(const aiNode *node) {
+
+    return XMLEscape(node->mName.C_Str());
+}
+
+std::string ColladaExporter::GetBoneUniqueId(const aiBone *bone) {
+    // Find the Node that is this Bone
+    const aiNode *boneNode = findBoneNode(mScene->mRootNode, bone);
+    if (boneNode == nullptr)
+        return std::string();
+
+    return GetNodeUniqueId(boneNode);
+}
+
+std::string ColladaExporter::GetObjectUniqueId(AiObjectType type, size_t pIndex) {
+    auto idIt = GetObjectIdMap(type).find(pIndex);
+    if (idIt != GetObjectIdMap(type).cend())
+        return idIt->second;
+
+    // Not seen this object before, create and add
+    NameIdPair result = AddObjectIndexToMaps(type, pIndex);
+    return result.second;
+}
+
+std::string ColladaExporter::GetObjectName(AiObjectType type, size_t pIndex) {
+    auto objectName = GetObjectNameMap(type).find(pIndex);
+    if (objectName != GetObjectNameMap(type).cend())
+        return objectName->second;
+
+    // Not seen this object before, create and add
+    NameIdPair result = AddObjectIndexToMaps(type, pIndex);
+    return result.first;
+}
+
+// Determine unique id and add the name and id to the maps
+// @param type object type
+// @param index object index
+// @param name in/out. Caller to set the original name if known.
+// @param idStr in/out. Caller to set the preferred id if known.
+ColladaExporter::NameIdPair ColladaExporter::AddObjectIndexToMaps(AiObjectType type, size_t index) {
+
+    std::string name;
+    std::string idStr;
+    std::string idPostfix;
+
+    // Get the name and id postfix
+    switch (type) {
+    case AiObjectType::Mesh: name = mScene->mMeshes[index]->mName.C_Str(); break;
+    case AiObjectType::Material: name = mScene->mMaterials[index]->GetName().C_Str(); break;
+    case AiObjectType::Animation: name = mScene->mAnimations[index]->mName.C_Str(); break;
+    case AiObjectType::Light:
+        name = mScene->mLights[index]->mName.C_Str();
+        idPostfix = "-light";
+        break;
+    case AiObjectType::Camera:
+        name = mScene->mCameras[index]->mName.C_Str();
+        idPostfix = "-camera";
+        break;
+    case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
+    }
+
+    if (name.empty()) {
+        // Default ids if empty name
+        switch (type) {
+        case AiObjectType::Mesh: idStr = std::string("mesh_"); break;
+        case AiObjectType::Material: idStr = std::string("material_"); break; // This one should never happen
+        case AiObjectType::Animation: idStr = std::string("animation_"); break;
+        case AiObjectType::Light: idStr = std::string("light_"); break;
+        case AiObjectType::Camera: idStr = std::string("camera_"); break;
+        case AiObjectType::Count: throw std::logic_error("ColladaExporter::AiObjectType::Count is not an object type");
+        }
+        idStr.append(ai_to_string(index));
+    } else {
+        idStr = XMLIDEncode(name);
+    }
+
+    if (!name.empty())
+        name = XMLEscape(name);
+
+    idStr = MakeUniqueId(mUniqueIds, idStr, idPostfix);
+
+    // Add to maps
+    mUniqueIds.insert(idStr);
+    GetObjectIdMap(type).insert(std::make_pair(index, idStr));
+    GetObjectNameMap(type).insert(std::make_pair(index, name));
+
+    return std::make_pair(name, idStr);
+}
+
+} // end of namespace Assimp
+
+#endif
+#endif

+ 257 - 0
Engine/lib/assimp/code/AssetLib/Collada/ColladaExporter.h

@@ -0,0 +1,257 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 ColladaExporter.h
+ * Declares the exporter class to write a scene to a Collada file
+ */
+#ifndef AI_COLLADAEXPORTER_H_INC
+#define AI_COLLADAEXPORTER_H_INC
+
+#include <assimp/ai_assert.h>
+#include <assimp/material.h>
+
+#include <array>
+#include <map>
+#include <sstream>
+#include <unordered_set>
+#include <vector>
+
+struct aiScene;
+struct aiNode;
+struct aiLight;
+struct aiBone;
+
+namespace Assimp {
+
+class IOSystem;
+
+/// Helper class to export a given scene to a Collada file. Just for my personal
+/// comfort when implementing it.
+class ColladaExporter {
+public:
+    /// Constructor for a specific scene to export
+    ColladaExporter(const aiScene *pScene, IOSystem *pIOSystem, const std::string &path, const std::string &file);
+
+    /// Destructor
+    virtual ~ColladaExporter();
+
+protected:
+    /// Starts writing the contents
+    void WriteFile();
+
+    /// Writes the asset header
+    void WriteHeader();
+
+    /// Writes the embedded textures
+    void WriteTextures();
+
+    /// Writes the material setup
+    void WriteMaterials();
+
+    /// Writes the cameras library
+    void WriteCamerasLibrary();
+
+    // Write a camera entry
+    void WriteCamera(size_t pIndex);
+
+    /// Writes the cameras library
+    void WriteLightsLibrary();
+
+    // Write a camera entry
+    void WriteLight(size_t pIndex);
+    void WritePointLight(const aiLight *const light);
+    void WriteDirectionalLight(const aiLight *const light);
+    void WriteSpotLight(const aiLight *const light);
+    void WriteAmbienttLight(const aiLight *const light);
+
+    /// Writes the controller library
+    void WriteControllerLibrary();
+
+    /// Writes a skin controller of the given mesh
+    void WriteController(size_t pIndex);
+
+    /// Writes the geometry library
+    void WriteGeometryLibrary();
+
+    /// Writes the given mesh
+    void WriteGeometry(size_t pIndex);
+
+    //enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color, FloatType_Mat4x4, FloatType_Weight };
+    // customized to add animation related type
+    enum FloatDataType { FloatType_Vector,
+        FloatType_TexCoord2,
+        FloatType_TexCoord3,
+        FloatType_Color,
+        FloatType_Mat4x4,
+        FloatType_Weight,
+        FloatType_Time };
+
+    /// Writes a float array of the given type
+    void WriteFloatArray(const std::string &pIdString, FloatDataType pType, const ai_real *pData, size_t pElementCount);
+
+    /// Writes the scene library
+    void WriteSceneLibrary();
+
+    // customized, Writes the animation library
+    void WriteAnimationsLibrary();
+    void WriteAnimationLibrary(size_t pIndex);
+    std::string mFoundSkeletonRootNodeID = "skeleton_root"; // will be replaced by found node id in the WriteNode call.
+
+    /// Recursively writes the given node
+    void WriteNode(const aiNode *pNode);
+
+    /// Enters a new xml element, which increases the indentation
+    void PushTag() { startstr.append("  "); }
+    /// Leaves an element, decreasing the indentation
+    void PopTag() {
+        ai_assert(startstr.length() > 1);
+        startstr.erase(startstr.length() - 2);
+    }
+
+    void CreateNodeIds(const aiNode *node);
+
+    /// Get or Create a unique Node ID string for the given Node
+    std::string GetNodeUniqueId(const aiNode *node);
+    std::string GetNodeName(const aiNode *node);
+
+    std::string GetBoneUniqueId(const aiBone *bone);
+
+    enum class AiObjectType {
+        Mesh,
+        Material,
+        Animation,
+        Light,
+        Camera,
+        Count,
+    };
+    /// Get or Create a unique ID string for the given scene object index
+    std::string GetObjectUniqueId(AiObjectType type, size_t pIndex);
+    /// Get or Create a name string for the given scene object index
+    std::string GetObjectName(AiObjectType type, size_t pIndex);
+
+    typedef std::map<size_t, std::string> IndexIdMap;
+    typedef std::pair<std::string, std::string> NameIdPair;
+    NameIdPair AddObjectIndexToMaps(AiObjectType type, size_t pIndex);
+
+    // Helpers
+    inline IndexIdMap &GetObjectIdMap(AiObjectType type) { return mObjectIdMap[static_cast<size_t>(type)]; }
+    inline IndexIdMap &GetObjectNameMap(AiObjectType type) { return mObjectNameMap[static_cast<size_t>(type)]; }
+
+private:
+    std::unordered_set<std::string> mUniqueIds; // Cache of used unique ids
+    std::map<const void *, std::string> mNodeIdMap; // Cache of encoded node and bone ids
+    std::array<IndexIdMap, static_cast<size_t>(AiObjectType::Count)> mObjectIdMap; // Cache of encoded unique IDs
+    std::array<IndexIdMap, static_cast<size_t>(AiObjectType::Count)> mObjectNameMap; // Cache of encoded names
+
+public:
+    /// Stringstream to write all output into
+    std::stringstream mOutput;
+
+    /// The IOSystem for output
+    IOSystem *mIOSystem;
+
+    /// Path of the directory where the scene will be exported
+    const std::string mPath;
+
+    /// Name of the file (without extension) where the scene will be exported
+    const std::string mFile;
+
+    /// The scene to be written
+    const aiScene *const mScene;
+    std::string mSceneId;
+    bool mAdd_root_node = false;
+
+    /// current line start string, contains the current indentation for simple stream insertion
+    std::string startstr;
+    /// current line end string for simple stream insertion
+    const std::string endstr;
+
+    // pair of color and texture - texture precedences color
+    struct Surface {
+        bool exist;
+        aiColor4D color;
+        std::string texture;
+        size_t channel;
+        Surface() {
+            exist = false;
+            channel = 0;
+        }
+    };
+
+    struct Property {
+        bool exist;
+        ai_real value;
+        Property() :
+                exist(false),
+                value(0.0) {}
+    };
+
+    // summarize a material in an convenient way.
+    struct Material {
+        std::string id;
+        std::string name;
+        std::string shading_model;
+        Surface ambient, diffuse, specular, emissive, reflective, transparent, normal;
+        Property shininess, transparency, index_refraction;
+
+        Material() {}
+    };
+
+    std::map<unsigned int, std::string> textures;
+
+public:
+    /// Dammit C++ - y u no compile two-pass? No I have to add all methods below the struct definitions
+    /// Reads a single surface entry from the given material keys
+    bool ReadMaterialSurface(Surface &poSurface, const aiMaterial &pSrcMat, aiTextureType pTexture, const char *pKey, size_t pType, size_t pIndex);
+    /// Writes an image entry for the given surface
+    void WriteImageEntry(const Surface &pSurface, const std::string &imageId);
+    /// Writes the two parameters necessary for referencing a texture in an effect entry
+    void WriteTextureParamEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &materialId);
+    /// Writes a color-or-texture entry into an effect definition
+    void WriteTextureColorEntry(const Surface &pSurface, const std::string &pTypeName, const std::string &imageId);
+    /// Writes a scalar property
+    void WriteFloatEntry(const Property &pProperty, const std::string &pTypeName);
+};
+
+} // namespace Assimp
+
+#endif // !! AI_COLLADAEXPORTER_H_INC

+ 99 - 0
Engine/lib/assimp/code/AssetLib/Collada/ColladaHelper.cpp

@@ -0,0 +1,99 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+
+----------------------------------------------------------------------
+*/
+/** Helper structures for the Collada loader */
+
+#include "ColladaHelper.h"
+
+#include <assimp/ParsingUtils.h>
+#include <assimp/commonMetaData.h>
+
+namespace Assimp {
+namespace Collada {
+
+const MetaKeyPairVector MakeColladaAssimpMetaKeys() {
+    MetaKeyPairVector result;
+    result.emplace_back("authoring_tool", AI_METADATA_SOURCE_GENERATOR);
+    result.emplace_back("copyright", AI_METADATA_SOURCE_COPYRIGHT);
+    return result;
+}
+
+const MetaKeyPairVector &GetColladaAssimpMetaKeys() {
+    static const MetaKeyPairVector result = MakeColladaAssimpMetaKeys();
+    return result;
+}
+
+const MetaKeyPairVector MakeColladaAssimpMetaKeysCamelCase() {
+    MetaKeyPairVector result = MakeColladaAssimpMetaKeys();
+    for (auto &val : result) {
+        ToCamelCase(val.first);
+    }
+    return result;
+}
+
+const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase() {
+    static const MetaKeyPairVector result = MakeColladaAssimpMetaKeysCamelCase();
+    return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert underscore_separated to CamelCase: "authoring_tool" becomes "AuthoringTool"
+void ToCamelCase(std::string &text) {
+    if (text.empty())
+        return;
+    // Capitalise first character
+    auto it = text.begin();
+    (*it) = ai_toupper(*it);
+    ++it;
+    for (/*started above*/; it != text.end(); /*iterated below*/) {
+        if ((*it) == '_') {
+            it = text.erase(it);
+            if (it != text.end())
+                (*it) = ai_toupper(*it);
+        } else {
+            // Make lower case
+            (*it) = ai_tolower(*it);
+            ++it;
+        }
+    }
+}
+
+} // namespace Collada
+} // namespace Assimp

+ 679 - 0
Engine/lib/assimp/code/AssetLib/Collada/ColladaHelper.h

@@ -0,0 +1,679 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+
+----------------------------------------------------------------------
+*/
+
+/** Helper structures for the Collada loader */
+
+#ifndef AI_COLLADAHELPER_H_INC
+#define AI_COLLADAHELPER_H_INC
+
+#include <assimp/light.h>
+#include <assimp/material.h>
+#include <assimp/mesh.h>
+
+#include <cstdint>
+#include <map>
+#include <set>
+#include <vector>
+
+struct aiMaterial;
+
+namespace Assimp {
+namespace Collada {
+
+/// Collada file versions which evolved during the years ...
+enum FormatVersion {
+    FV_1_5_n,
+    FV_1_4_n,
+    FV_1_3_n
+};
+
+/// Transformation types that can be applied to a node
+enum TransformType {
+    TF_LOOKAT,
+    TF_ROTATE,
+    TF_TRANSLATE,
+    TF_SCALE,
+    TF_SKEW,
+    TF_MATRIX
+};
+
+/// Different types of input data to a vertex or face
+enum InputType {
+    IT_Invalid,
+    IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
+    IT_Position,
+    IT_Normal,
+    IT_Texcoord,
+    IT_Color,
+    IT_Tangent,
+    IT_Bitangent
+};
+
+/// Supported controller types
+enum ControllerType {
+    Skin,
+    Morph
+};
+
+/// Supported morph methods
+enum MorphMethod {
+    Normalized,
+    Relative
+};
+
+/// Common metadata keys as <Collada, Assimp>
+using MetaKeyPair = std::pair<std::string, std::string>;
+using MetaKeyPairVector = std::vector<MetaKeyPair>;
+
+/// Collada as lower_case (native)
+const MetaKeyPairVector &GetColladaAssimpMetaKeys();
+
+// Collada as CamelCase (used by Assimp for consistency)
+const MetaKeyPairVector &GetColladaAssimpMetaKeysCamelCase();
+
+/// Convert underscore_separated to CamelCase "authoring_tool" becomes "AuthoringTool"
+void ToCamelCase(std::string &text);
+
+/// Contains all data for one of the different transformation types
+struct Transform {
+    std::string mID; ///< SID of the transform step, by which anim channels address their target node
+    TransformType mType;
+    ai_real f[16]; ///< Interpretation of data depends on the type of the transformation
+};
+
+/// A collada camera.
+struct Camera {
+    Camera() :
+            mOrtho(false),
+            mHorFov(10e10f),
+            mVerFov(10e10f),
+            mAspect(10e10f),
+            mZNear(0.1f),
+            mZFar(1000.f) {}
+
+    /// Name of camera
+    std::string mName;
+
+    /// True if it is an orthographic camera
+    bool mOrtho;
+
+    /// Horizontal field of view in degrees
+    ai_real mHorFov;
+
+    /// Vertical field of view in degrees
+    ai_real mVerFov;
+
+    /// Screen aspect
+    ai_real mAspect;
+
+    /// Near& far z
+    ai_real mZNear, mZFar;
+};
+
+#define ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET 1e9f
+
+/** A collada light source. */
+struct Light {
+    Light() :
+            mType(aiLightSource_UNDEFINED),
+            mAttConstant(1.f),
+            mAttLinear(0.f),
+            mAttQuadratic(0.f),
+            mFalloffAngle(180.f),
+            mFalloffExponent(0.f),
+            mPenumbraAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET),
+            mOuterAngle(ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET),
+            mIntensity(1.f) {}
+
+    /// Type of the light source aiLightSourceType + ambient
+    unsigned int mType;
+
+    /// Color of the light
+    aiColor3D mColor;
+
+    /// Light attenuation
+    ai_real mAttConstant, mAttLinear, mAttQuadratic;
+
+    /// Spot light falloff
+    ai_real mFalloffAngle;
+    ai_real mFalloffExponent;
+
+    // -----------------------------------------------------
+    // FCOLLADA extension from here
+
+    /// ... related stuff from maja and max extensions
+    ai_real mPenumbraAngle;
+    ai_real mOuterAngle;
+
+    /// Common light intensity
+    ai_real mIntensity;
+};
+
+/** Short vertex index description */
+struct InputSemanticMapEntry {
+    InputSemanticMapEntry() :
+            mSet(0),
+            mType(IT_Invalid) {}
+
+    /// Index of set, optional
+    unsigned int mSet;
+
+    /// Type of referenced vertex input
+    InputType mType;
+};
+
+/// Table to map from effect to vertex input semantics
+struct SemanticMappingTable {
+    /// Name of material
+    std::string mMatName;
+
+    /// List of semantic map commands, grouped by effect semantic name
+    using InputSemanticMap = std::map<std::string, InputSemanticMapEntry>;
+    InputSemanticMap mMap;
+
+    /// For std::find
+    bool operator==(const std::string &s) const {
+        return s == mMatName;
+    }
+};
+
+/// A reference to a mesh inside a node, including materials assigned to the various subgroups.
+/// The ID refers to either a mesh or a controller which specifies the mesh
+struct MeshInstance {
+    ///< ID of the mesh or controller to be instanced
+    std::string mMeshOrController;
+
+    ///< Map of materials by the subgroup ID they're applied to
+    std::map<std::string, SemanticMappingTable> mMaterials;
+};
+
+/// A reference to a camera inside a node
+struct CameraInstance {
+    ///< ID of the camera
+    std::string mCamera;
+};
+
+/// A reference to a light inside a node
+struct LightInstance {
+    ///< ID of the camera
+    std::string mLight;
+};
+
+/// A reference to a node inside a node
+struct NodeInstance {
+    ///< ID of the node
+    std::string mNode;
+};
+
+/// A node in a scene hierarchy
+struct Node {
+    std::string mName;
+    std::string mID;
+    std::string mSID;
+    Node *mParent;
+    std::vector<Node *> mChildren;
+
+    /// Operations in order to calculate the resulting transformation to parent.
+    std::vector<Transform> mTransforms;
+
+    /// Meshes at this node
+    std::vector<MeshInstance> mMeshes;
+
+    /// Lights at this node
+    std::vector<LightInstance> mLights;
+
+    /// Cameras at this node
+    std::vector<CameraInstance> mCameras;
+
+    /// Node instances at this node
+    std::vector<NodeInstance> mNodeInstances;
+
+    /// Root-nodes: Name of primary camera, if any
+    std::string mPrimaryCamera;
+
+    /// Constructor. Begin with a zero parent
+    Node() :
+            mParent(nullptr) {
+        // empty
+    }
+
+    /// Destructor: delete all children subsequently
+    ~Node() {
+        for (std::vector<Node *>::iterator it = mChildren.begin(); it != mChildren.end(); ++it) {
+            delete *it;
+        }
+    }
+};
+
+/// Data source array: either floats or strings
+struct Data {
+    bool mIsStringArray;
+    std::vector<ai_real> mValues;
+    std::vector<std::string> mStrings;
+};
+
+/// Accessor to a data array
+struct Accessor {
+    size_t mCount; // in number of objects
+    size_t mSize; // size of an object, in elements (floats or strings, mostly 1)
+    size_t mOffset; // in number of values
+    size_t mStride; // Stride in number of values
+    std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
+    size_t mSubOffset[4]; // Sub-offset inside the object for the common 4 elements. For a vector, that's XYZ, for a color RGBA and so on.
+            // For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
+    std::string mSource; // URL of the source array
+    mutable const Data *mData; // Pointer to the source array, if resolved. nullptr else
+
+    Accessor() {
+        mCount = 0;
+        mSize = 0;
+        mOffset = 0;
+        mStride = 0;
+        mData = nullptr;
+        mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0;
+    }
+};
+
+/// A single face in a mesh
+struct Face {
+    std::vector<size_t> mIndices;
+};
+
+/// An input channel for mesh data, referring to a single accessor
+struct InputChannel {
+    InputType mType; // Type of the data
+    size_t mIndex; // Optional index, if multiple sets of the same data type are given
+    size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
+    std::string mAccessor; // ID of the accessor where to read the actual values from.
+    mutable const Accessor *mResolved; // Pointer to the accessor, if resolved. nullptr else
+
+    InputChannel() {
+        mType = IT_Invalid;
+        mIndex = 0;
+        mOffset = 0;
+        mResolved = nullptr;
+    }
+};
+
+/// Subset of a mesh with a certain material
+struct SubMesh {
+    std::string mMaterial; ///< subgroup identifier
+    size_t mNumFaces; ///< number of faces in this sub-mesh
+};
+
+/// Contains data for a single mesh
+struct Mesh {
+    Mesh(const std::string &id) :
+            mId(id) {
+        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+            mNumUVComponents[i] = 2;
+        }
+    }
+
+    const std::string mId;
+    std::string mName;
+
+    // just to check if there's some sophisticated addressing involved...
+    // which we don't support, and therefore should warn about.
+    std::string mVertexID;
+
+    // Vertex data addressed by vertex indices
+    std::vector<InputChannel> mPerVertexData;
+
+    // actual mesh data, assembled on encounter of a <p> element. Verbose format, not indexed
+    std::vector<aiVector3D> mPositions;
+    std::vector<aiVector3D> mNormals;
+    std::vector<aiVector3D> mTangents;
+    std::vector<aiVector3D> mBitangents;
+    std::vector<aiVector3D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+    std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+    unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+    // Faces. Stored are only the number of vertices for each face.
+    // 1 == point, 2 == line, 3 == triangle, 4+ == poly
+    std::vector<size_t> mFaceSize;
+
+    // Position indices for all faces in the sequence given in mFaceSize -
+    // necessary for bone weight assignment
+    std::vector<size_t> mFacePosIndices;
+
+    // Sub-meshes in this mesh, each with a given material
+    std::vector<SubMesh> mSubMeshes;
+};
+
+/// Which type of primitives the ReadPrimitives() function is going to read
+enum PrimitiveType {
+    Prim_Invalid,
+    Prim_Lines,
+    Prim_LineStrip,
+    Prim_Triangles,
+    Prim_TriStrips,
+    Prim_TriFans,
+    Prim_Polylist,
+    Prim_Polygon
+};
+
+/// A skeleton controller to deform a mesh with the use of joints
+struct Controller {
+    // controller type
+    ControllerType mType;
+
+    // Morphing method if type is Morph
+    MorphMethod mMethod;
+
+    // the URL of the mesh deformed by the controller.
+    std::string mMeshId;
+
+    // accessor URL of the joint names
+    std::string mJointNameSource;
+
+    ///< The bind shape matrix, as array of floats. I'm not sure what this matrix actually describes, but it can't be ignored in all cases
+    ai_real mBindShapeMatrix[16];
+
+    // accessor URL of the joint inverse bind matrices
+    std::string mJointOffsetMatrixSource;
+
+    // input channel: joint names.
+    InputChannel mWeightInputJoints;
+    // input channel: joint weights
+    InputChannel mWeightInputWeights;
+
+    // Number of weights per vertex.
+    std::vector<size_t> mWeightCounts;
+
+    // JointIndex-WeightIndex pairs for all vertices
+    std::vector<std::pair<size_t, size_t>> mWeights;
+
+    std::string mMorphTarget;
+    std::string mMorphWeight;
+};
+
+/// A collada material. Pretty much the only member is a reference to an effect.
+struct Material {
+    std::string mName;
+    std::string mEffect;
+};
+
+/// Type of the effect param
+enum ParamType {
+    Param_Sampler,
+    Param_Surface
+};
+
+/// A param for an effect. Might be of several types, but they all just refer to each other, so I summarize them
+struct EffectParam {
+    ParamType mType;
+    std::string mReference; // to which other thing the param is referring to.
+};
+
+/// Shading type supported by the standard effect spec of Collada
+enum ShadeType {
+    Shade_Invalid,
+    Shade_Constant,
+    Shade_Lambert,
+    Shade_Phong,
+    Shade_Blinn
+};
+
+/// Represents a texture sampler in collada
+struct Sampler {
+    Sampler() :
+            mWrapU(true),
+            mWrapV(true),
+            mMirrorU(),
+            mMirrorV(),
+            mOp(aiTextureOp_Multiply),
+            mUVId(UINT_MAX),
+            mWeighting(1.f),
+            mMixWithPrevious(1.f) {}
+
+    /// Name of image reference
+    std::string mName;
+
+    /// Wrap U?
+    bool mWrapU;
+
+    /// Wrap V?
+    bool mWrapV;
+
+    /// Mirror U?
+    bool mMirrorU;
+
+    /// Mirror V?
+    bool mMirrorV;
+
+    /// Blend mode
+    aiTextureOp mOp;
+
+    /// UV transformation
+    aiUVTransform mTransform;
+
+    /// Name of source UV channel
+    std::string mUVChannel;
+
+    /// Resolved UV channel index or UINT_MAX if not known
+    unsigned int mUVId;
+
+    // OKINO/MAX3D extensions from here
+    // -------------------------------------------------------
+
+    /// Weighting factor
+    ai_real mWeighting;
+
+    /// Mixing factor from OKINO
+    ai_real mMixWithPrevious;
+};
+
+/// A collada effect. Can contain about anything according to the Collada spec,
+/// but we limit our version to a reasonable subset.
+struct Effect {
+    /// Shading mode
+    ShadeType mShadeType;
+
+    /// Colors
+    aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular,
+            mTransparent, mReflective;
+
+    /// Textures
+    Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular,
+            mTexTransparent, mTexBump, mTexReflective;
+
+    /// Scalar factory
+    ai_real mShininess, mRefractIndex, mReflectivity;
+    ai_real mTransparency;
+    bool mHasTransparency;
+    bool mRGBTransparency;
+    bool mInvertTransparency;
+
+    /// local params referring to each other by their SID
+    using ParamLibrary = std::map<std::string, Collada::EffectParam>;
+    ParamLibrary mParams;
+
+    // MAX3D extensions
+    // ---------------------------------------------------------
+    // Double-sided?
+    bool mDoubleSided, mWireframe, mFaceted;
+
+    Effect() :
+            mShadeType(Shade_Phong),
+            mEmissive(0, 0, 0, 1),
+            mAmbient(0.1f, 0.1f, 0.1f, 1),
+            mDiffuse(0.6f, 0.6f, 0.6f, 1),
+            mSpecular(0.4f, 0.4f, 0.4f, 1),
+            mTransparent(0, 0, 0, 1),
+            mShininess(10.0f),
+            mRefractIndex(1.f),
+            mReflectivity(0.f),
+            mTransparency(1.f),
+            mHasTransparency(false),
+            mRGBTransparency(false),
+            mInvertTransparency(false),
+            mDoubleSided(false),
+            mWireframe(false),
+            mFaceted(false) {
+    }
+};
+
+/// An image, meaning texture
+struct Image {
+    std::string mFileName;
+
+    /// Embedded image data
+    std::vector<uint8_t> mImageData;
+
+    /// File format hint of embedded image data
+    std::string mEmbeddedFormat;
+};
+
+/// An animation channel.
+struct AnimationChannel {
+    /// URL of the data to animate. Could be about anything, but we support only the
+    /// "NodeID/TransformID.SubElement" notation
+    std::string mTarget;
+
+    /// Source URL of the time values. Collada calls them "input". Meh.
+    std::string mSourceTimes;
+    /// Source URL of the value values. Collada calls them "output".
+    std::string mSourceValues;
+    /// Source URL of the IN_TANGENT semantic values.
+    std::string mInTanValues;
+    /// Source URL of the OUT_TANGENT semantic values.
+    std::string mOutTanValues;
+    /// Source URL of the INTERPOLATION semantic values.
+    std::string mInterpolationValues;
+};
+
+/// An animation. Container for 0-x animation channels or 0-x animations
+struct Animation {
+    /// Anim name
+    std::string mName;
+
+    /// the animation channels, if any
+    std::vector<AnimationChannel> mChannels;
+
+    /// the sub-animations, if any
+    std::vector<Animation *> mSubAnims;
+
+    /// Destructor
+    ~Animation() {
+        for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) {
+            delete *it;
+        }
+    }
+
+    /// Collect all channels in the animation hierarchy into a single channel list.
+    void CollectChannelsRecursively(std::vector<AnimationChannel> &channels) {
+        channels.insert(channels.end(), mChannels.begin(), mChannels.end());
+
+        for (std::vector<Animation *>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it) {
+            Animation *pAnim = (*it);
+            pAnim->CollectChannelsRecursively(channels);
+        }
+    }
+
+    /// Combine all single-channel animations' channel into the same (parent) animation channel list.
+    void CombineSingleChannelAnimations() {
+        CombineSingleChannelAnimationsRecursively(this);
+    }
+
+    void CombineSingleChannelAnimationsRecursively(Animation *pParent) {
+        std::set<std::string> childrenTargets;
+        bool childrenAnimationsHaveDifferentChannels = true;
+
+        for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
+            Animation *anim = *it;
+            CombineSingleChannelAnimationsRecursively(anim);
+
+            if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 &&
+                    childrenTargets.find(anim->mChannels[0].mTarget) == childrenTargets.end()) {
+                childrenTargets.insert(anim->mChannels[0].mTarget);
+            } else {
+                childrenAnimationsHaveDifferentChannels = false;
+            }
+
+            ++it;
+        }
+
+        // We only want to combine animations if they have different channels
+        if (childrenAnimationsHaveDifferentChannels) {
+            for (std::vector<Animation *>::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) {
+                Animation *anim = *it;
+
+                pParent->mChannels.push_back(anim->mChannels[0]);
+
+                it = pParent->mSubAnims.erase(it);
+
+                delete anim;
+                continue;
+            }
+        }
+    }
+};
+
+/// Description of a collada animation channel which has been determined to affect the current node
+struct ChannelEntry {
+    const Collada::AnimationChannel *mChannel; ///< the source channel
+    std::string mTargetId;
+    std::string mTransformId; // the ID of the transformation step of the node which is influenced
+    size_t mTransformIndex; // Index into the node's transform chain to apply the channel to
+    size_t mSubElement; // starting index inside the transform data
+
+    // resolved data references
+    const Collada::Accessor *mTimeAccessor; ///> Collada accessor to the time values
+    const Collada::Data *mTimeData; ///> Source data array for the time values
+    const Collada::Accessor *mValueAccessor; ///> Collada accessor to the key value values
+    const Collada::Data *mValueData; ///> Source datat array for the key value values
+
+    ChannelEntry() :
+            mChannel(),
+            mTransformIndex(),
+            mSubElement(),
+            mTimeAccessor(),
+            mTimeData(),
+            mValueAccessor(),
+            mValueData() {}
+};
+
+} // end of namespace Collada
+} // end of namespace Assimp
+
+#endif // AI_COLLADAHELPER_H_INC

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 280 - 280
Engine/lib/assimp/code/AssetLib/Collada/ColladaLoader.cpp


+ 71 - 81
Engine/lib/assimp/code/Collada/ColladaLoader.h → Engine/lib/assimp/code/AssetLib/Collada/ColladaLoader.h

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -45,8 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_COLLADALOADER_H_INC
 #define AI_COLLADALOADER_H_INC
 
-#include <assimp/BaseImporter.h>
 #include "ColladaParser.h"
+#include <assimp/BaseImporter.h>
 
 struct aiNode;
 struct aiCamera;
@@ -54,28 +54,24 @@ struct aiLight;
 struct aiTexture;
 struct aiAnimation;
 
-namespace Assimp
-{
+namespace Assimp {
 
-struct ColladaMeshIndex
-{
+struct ColladaMeshIndex {
     std::string mMeshID;
     size_t mSubMesh;
     std::string mMaterial;
-    ColladaMeshIndex( const std::string& pMeshID, size_t pSubMesh, const std::string& pMaterial)
-        : mMeshID( pMeshID), mSubMesh( pSubMesh), mMaterial( pMaterial)
-    {   }
-
-    bool operator < (const ColladaMeshIndex& p) const
-    {
-        if( mMeshID == p.mMeshID)
-        {
-            if( mSubMesh == p.mSubMesh)
+    ColladaMeshIndex(const std::string &pMeshID, size_t pSubMesh, const std::string &pMaterial) :
+            mMeshID(pMeshID), mSubMesh(pSubMesh), mMaterial(pMaterial) {
+        ai_assert(!pMeshID.empty());
+    }
+
+    bool operator<(const ColladaMeshIndex &p) const {
+        if (mMeshID == p.mMeshID) {
+            if (mSubMesh == p.mSubMesh)
                 return mMaterial < p.mMaterial;
             else
                 return mSubMesh < p.mSubMesh;
-        } else
-        {
+        } else {
             return mMeshID < p.mMeshID;
         }
     }
@@ -84,108 +80,102 @@ struct ColladaMeshIndex
 /** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing
  * more useless stuff, so I limited the data to what I think is useful for games.
 */
-class ColladaLoader : public BaseImporter
-{
+class ColladaLoader : public BaseImporter {
 public:
+    /// The class constructor.
     ColladaLoader();
-    ~ColladaLoader();
 
+    /// The class destructor.
+    ~ColladaLoader() override;
 
-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 override;
+    /// Returns whether the class can handle the format of the given file.
+    /// @see BaseImporter::CanRead() for more details.
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
 
 protected:
-    /** Return importer meta information.
-     * See #BaseImporter::GetInfo for the details
-     */
-    const aiImporterDesc* GetInfo () const override;
+    /// See #BaseImporter::GetInfo for the details
+    const aiImporterDesc *GetInfo() const override;
 
-    void SetupProperties(const Importer* pImp) override;
+    /// See #BaseImporter::SetupProperties for the details
+    void SetupProperties(const Importer *pImp) override;
 
-    /** Imports the given file into the given scene structure.
-     * See BaseImporter::InternReadFile() for details
-     */
-    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
+    /// See #BaseImporter::InternReadFile for the details
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
 
     /** Recursively constructs a scene node for the given parser node and returns it. */
-    aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode);
+    aiNode *BuildHierarchy(const ColladaParser &pParser, const Collada::Node *pNode);
 
     /** Resolve node instances */
-    void ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
-        std::vector<const Collada::Node*>& resolved);
+    void ResolveNodeInstances(const ColladaParser &pParser, const Collada::Node *pNode,
+            std::vector<const Collada::Node *> &resolved);
 
     /** Builds meshes for the given node and references them */
-    void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
-        aiNode* pTarget);
-		
-    aiMesh *findMesh(const std::string& meshid);
+    void BuildMeshesForNode(const ColladaParser &pParser, const Collada::Node *pNode,
+            aiNode *pTarget);
+
+    aiMesh *findMesh(const std::string &meshid);
 
     /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
-    aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
-        const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace);
+    aiMesh *CreateMesh(const ColladaParser &pParser, const Collada::Mesh *pSrcMesh, const Collada::SubMesh &pSubMesh,
+            const Collada::Controller *pSrcController, size_t pStartVertex, size_t pStartFace);
 
     /** Builds cameras for the given node and references them */
-    void BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode,
-        aiNode* pTarget);
+    void BuildCamerasForNode(const ColladaParser &pParser, const Collada::Node *pNode,
+            aiNode *pTarget);
 
     /** Builds lights for the given node and references them */
-    void BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode,
-        aiNode* pTarget);
+    void BuildLightsForNode(const ColladaParser &pParser, const Collada::Node *pNode,
+            aiNode *pTarget);
 
     /** Stores all meshes in the given scene */
-    void StoreSceneMeshes( aiScene* pScene);
+    void StoreSceneMeshes(aiScene *pScene);
 
     /** Stores all materials in the given scene */
-    void StoreSceneMaterials( aiScene* pScene);
+    void StoreSceneMaterials(aiScene *pScene);
 
     /** Stores all lights in the given scene */
-    void StoreSceneLights( aiScene* pScene);
+    void StoreSceneLights(aiScene *pScene);
 
     /** Stores all cameras in the given scene */
-    void StoreSceneCameras( aiScene* pScene);
+    void StoreSceneCameras(aiScene *pScene);
 
     /** Stores all textures in the given scene */
-    void StoreSceneTextures( aiScene* pScene);
+    void StoreSceneTextures(aiScene *pScene);
 
     /** Stores all animations
      * @param pScene target scene to store the anims
      */
-    void StoreAnimations( aiScene* pScene, const ColladaParser& pParser);
+    void StoreAnimations(aiScene *pScene, const ColladaParser &pParser);
 
     /** Stores all animations for the given source anim and its nested child animations
      * @param pScene target scene to store the anims
      * @param pSrcAnim the source animation to process
      * @param pPrefix Prefix to the name in case of nested animations
      */
-    void StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pPrefix);
+    void StoreAnimations(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pPrefix);
 
     /** Constructs the animation for the given source anim */
-    void CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName);
+    void CreateAnimation(aiScene *pScene, const ColladaParser &pParser, const Collada::Animation *pSrcAnim, const std::string &pName);
 
     /** Constructs materials from the collada material definitions */
-    void BuildMaterials( ColladaParser& pParser, aiScene* pScene);
+    void BuildMaterials(ColladaParser &pParser, aiScene *pScene);
 
     /** Fill materials from the collada material definitions */
-    void FillMaterials( const ColladaParser& pParser, aiScene* pScene);
+    void FillMaterials(const ColladaParser &pParser, aiScene *pScene);
 
     /** Resolve UV channel mappings*/
-    void ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
-        const Collada::SemanticMappingTable& table);
+    void ApplyVertexToEffectSemanticMapping(Collada::Sampler &sampler,
+            const Collada::SemanticMappingTable &table);
 
     /** Add a texture and all of its sampling properties to a material*/
-    void AddTexture ( aiMaterial& mat, const ColladaParser& pParser,
-        const Collada::Effect& effect,
-        const Collada::Sampler& sampler,
-        aiTextureType type, unsigned int idx = 0);
+    void AddTexture(aiMaterial &mat, const ColladaParser &pParser,
+            const Collada::Effect &effect,
+            const Collada::Sampler &sampler,
+            aiTextureType type, unsigned int idx = 0);
 
     /** Resolves the texture name for the given effect texture entry */
-    aiString FindFilenameForEffectTexture( const ColladaParser& pParser,
-        const Collada::Effect& pEffect, const std::string& pName);
-
-    /** Converts a path read from a collada file to the usual representation */
-    void ConvertPath( aiString& ss);
+    aiString FindFilenameForEffectTexture(const ColladaParser &pParser,
+            const Collada::Effect &pEffect, const std::string &pName);
 
     /** Reads a float value from an accessor and its data array.
      * @param pAccessor The accessor to use for reading
@@ -194,7 +184,7 @@ protected:
      * @param pOffset Offset into the element, for multipart elements such as vectors or matrices
      * @return the specified value
      */
-    ai_real ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const;
+    ai_real ReadFloat(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex, size_t pOffset) const;
 
     /** Reads a string value from an accessor and its data array.
      * @param pAccessor The accessor to use for reading
@@ -202,18 +192,18 @@ protected:
      * @param pIndex The index of the element to retrieve
      * @return the specified value
      */
-    const std::string& ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const;
+    const std::string &ReadString(const Collada::Accessor &pAccessor, const Collada::Data &pData, size_t pIndex) const;
 
     /** Recursively collects all nodes into the given array */
-    void CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const;
+    void CollectNodes(const aiNode *pNode, std::vector<const aiNode *> &poNodes) const;
 
     /** Finds a node in the collada scene by the given name */
-    const Collada::Node* FindNode( const Collada::Node* pNode, const std::string& pName) const;
+    const Collada::Node *FindNode(const Collada::Node *pNode, const std::string &pName) const;
     /** Finds a node in the collada scene by the given SID */
-    const Collada::Node* FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const;
+    const Collada::Node *FindNodeBySID(const Collada::Node *pNode, const std::string &pSID) const;
 
     /** Finds a proper name for a node derived from the collada-node's properties */
-    std::string FindNameForNode( const Collada::Node* pNode);
+    std::string FindNameForNode(const Collada::Node *pNode);
 
 protected:
     /** Filename, for a verbose error message */
@@ -226,25 +216,25 @@ protected:
     std::map<std::string, size_t> mMaterialIndexByName;
 
     /** Accumulated meshes for the target scene */
-    std::vector<aiMesh*> mMeshes;
-	
+    std::vector<aiMesh *> mMeshes;
+
     /** Accumulated morph target meshes */
-    std::vector<aiMesh*> mTargetMeshes;
+    std::vector<aiMesh *> mTargetMeshes;
 
     /** Temporary material list */
-    std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats;
+    std::vector<std::pair<Collada::Effect *, aiMaterial *>> newMats;
 
     /** Temporary camera list */
-    std::vector<aiCamera*> mCameras;
+    std::vector<aiCamera *> mCameras;
 
     /** Temporary light list */
-    std::vector<aiLight*> mLights;
+    std::vector<aiLight *> mLights;
 
     /** Temporary texture list */
-    std::vector<aiTexture*> mTextures;
+    std::vector<aiTexture *> mTextures;
 
     /** Accumulated animations for the target scene */
-    std::vector<aiAnimation*> mAnims;
+    std::vector<aiAnimation *> mAnims;
 
     bool noSkeletonMesh;
     bool ignoreUpDirection;

+ 2402 - 0
Engine/lib/assimp/code/AssetLib/Collada/ColladaParser.cpp

@@ -0,0 +1,2402 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 ColladaParser.cpp
+ *  @brief Implementation of the Collada parser helper
+ */
+
+#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
+
+#include "ColladaParser.h"
+#include <assimp/ParsingUtils.h>
+#include <assimp/StringUtils.h>
+#include <assimp/ZipArchiveIOSystem.h>
+#include <assimp/commonMetaData.h>
+#include <assimp/fast_atof.h>
+#include <assimp/light.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <memory>
+
+using namespace Assimp;
+using namespace Assimp::Collada;
+using namespace Assimp::Formatter;
+
+static void ReportWarning(const char *msg, ...) {
+    ai_assert(nullptr != msg);
+
+    va_list args;
+    va_start(args, msg);
+
+    char szBuffer[3000];
+    const int iLen = vsprintf(szBuffer, msg, args);
+    ai_assert(iLen > 0);
+
+    va_end(args);
+    ASSIMP_LOG_WARN("Validation warning: ", std::string(szBuffer, iLen));
+}
+
+static bool FindCommonKey(const std::string &collada_key, const MetaKeyPairVector &key_renaming, size_t &found_index) {
+    for (size_t i = 0; i < key_renaming.size(); ++i) {
+        if (key_renaming[i].first == collada_key) {
+            found_index = i;
+            return true;
+        }
+    }
+    found_index = std::numeric_limits<size_t>::max();
+
+    return false;
+}
+
+static void readUrlAttribute(XmlNode &node, std::string &url) {
+    url.clear();
+    if (!XmlParser::getStdStrAttribute(node, "url", url)) {
+        return;
+    }
+    if (url[0] != '#') {
+        throw DeadlyImportError("Unknown reference format");
+    }
+    url = url.c_str() + 1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
+        mFileName(pFile),
+        mXmlParser(),
+        mDataLibrary(),
+        mAccessorLibrary(),
+        mMeshLibrary(),
+        mNodeLibrary(),
+        mImageLibrary(),
+        mEffectLibrary(),
+        mMaterialLibrary(),
+        mLightLibrary(),
+        mCameraLibrary(),
+        mControllerLibrary(),
+        mRootNode(nullptr),
+        mAnims(),
+        mUnitSize(1.0f),
+        mUpDirection(UP_Y),
+        mFormat(FV_1_5_n) {
+    if (nullptr == pIOHandler) {
+        throw DeadlyImportError("IOSystem is nullptr.");
+    }
+
+    std::unique_ptr<IOStream> daefile;
+    std::unique_ptr<ZipArchiveIOSystem> zip_archive;
+
+    // Determine type
+    std::string extension = BaseImporter::GetExtension(pFile);
+    if (extension != "dae") {
+        zip_archive.reset(new ZipArchiveIOSystem(pIOHandler, pFile));
+    }
+
+    if (zip_archive && zip_archive->isOpen()) {
+        std::string dae_filename = ReadZaeManifest(*zip_archive);
+
+        if (dae_filename.empty()) {
+            throw DeadlyImportError("Invalid ZAE");
+        }
+
+        daefile.reset(zip_archive->Open(dae_filename.c_str()));
+        if (daefile == nullptr) {
+            throw DeadlyImportError("Invalid ZAE manifest: '", dae_filename, "' is missing");
+        }
+    } else {
+        // attempt to open the file directly
+        daefile.reset(pIOHandler->Open(pFile));
+        if (daefile.get() == nullptr) {
+            throw DeadlyImportError("Failed to open file '", pFile, "'.");
+        }
+    }
+
+    // generate a XML reader for it
+    if (!mXmlParser.parse(daefile.get())) {
+        throw DeadlyImportError("Unable to read file, malformed XML");
+    }
+    // start reading
+    XmlNode node = mXmlParser.getRootNode();
+    XmlNode colladaNode = node.child("COLLADA");
+    if (colladaNode.empty()) {
+        return;
+    }
+
+    // Read content and embedded textures
+    ReadContents(colladaNode);
+    if (zip_archive && zip_archive->isOpen()) {
+        ReadEmbeddedTextures(*zip_archive);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ColladaParser::~ColladaParser() {
+    for (auto &it : mNodeLibrary) {
+        delete it.second;
+    }
+    for (auto &it : mMeshLibrary) {
+        delete it.second;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a ZAE manifest and return the filename to attempt to open
+std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
+    // Open the manifest
+    std::unique_ptr<IOStream> manifestfile(zip_archive.Open("manifest.xml"));
+    if (manifestfile == nullptr) {
+        // No manifest, hope there is only one .DAE inside
+        std::vector<std::string> file_list;
+        zip_archive.getFileListExtension(file_list, "dae");
+
+        if (file_list.empty()) {
+            return std::string();
+        }
+
+        return file_list.front();
+    }
+    XmlParser manifestParser;
+    if (!manifestParser.parse(manifestfile.get())) {
+        return std::string();
+    }
+
+    XmlNode root = manifestParser.getRootNode();
+    const std::string &name = root.name();
+    if (name != "dae_root") {
+        root = *manifestParser.findNode("dae_root");
+        if (nullptr == root) {
+            return std::string();
+        }
+        std::string v;
+        XmlParser::getValueAsString(root, v);
+        aiString ai_str(v);
+        UriDecodePath(ai_str);
+        return std::string(ai_str.C_Str());
+    }
+
+    return std::string();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a path read from a collada file to the usual representation
+void ColladaParser::UriDecodePath(aiString &ss) {
+    // TODO: collada spec, p 22. Handle URI correctly.
+    // For the moment we're just stripping the file:// away to make it work.
+    // Windows doesn't seem to be able to find stuff like
+    // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
+    if (0 == strncmp(ss.data, "file://", 7)) {
+        ss.length -= 7;
+        memmove(ss.data, ss.data + 7, ss.length);
+        ss.data[ss.length] = '\0';
+    }
+
+    // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
+    // I need to filter it without destroying linux paths starting with "/somewhere"
+    if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') {
+        --ss.length;
+        ::memmove(ss.data, ss.data + 1, ss.length);
+        ss.data[ss.length] = 0;
+    }
+
+    // find and convert all %xy special chars
+    char *out = ss.data;
+    for (const char *it = ss.data; it != ss.data + ss.length; /**/) {
+        if (*it == '%' && (it + 3) < ss.data + ss.length) {
+            // separate the number to avoid dragging in chars from behind into the parsing
+            char mychar[3] = { it[1], it[2], 0 };
+            size_t nbr = strtoul16(mychar);
+            it += 3;
+            *out++ = (char)(nbr & 0xFF);
+        } else {
+            *out++ = *it++;
+        }
+    }
+
+    // adjust length and terminator of the shortened string
+    *out = 0;
+    ai_assert(out > ss.data);
+    ss.length = static_cast<ai_uint32>(out - ss.data);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the contents of the file
+void ColladaParser::ReadContents(XmlNode &node) {
+    const std::string name = node.name();
+    if (name == "COLLADA") {
+        std::string version;
+        if (XmlParser::getStdStrAttribute(node, "version", version)) {
+            aiString v;
+            v.Set(version.c_str());
+            mAssetMetaData.emplace(AI_METADATA_SOURCE_FORMAT_VERSION, v);
+            if (!::strncmp(version.c_str(), "1.5", 3)) {
+                mFormat = FV_1_5_n;
+                ASSIMP_LOG_DEBUG("Collada schema version is 1.5.n");
+            } else if (!::strncmp(version.c_str(), "1.4", 3)) {
+                mFormat = FV_1_4_n;
+                ASSIMP_LOG_DEBUG("Collada schema version is 1.4.n");
+            } else if (!::strncmp(version.c_str(), "1.3", 3)) {
+                mFormat = FV_1_3_n;
+                ASSIMP_LOG_DEBUG("Collada schema version is 1.3.n");
+            }
+        }
+        ReadStructure(node);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the structure of the file
+void ColladaParser::ReadStructure(XmlNode &node) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "asset") {
+            ReadAssetInfo(currentNode);
+        } else if (currentName == "library_animations") {
+            ReadAnimationLibrary(currentNode);
+        } else if (currentName == "library_animation_clips") {
+            ReadAnimationClipLibrary(currentNode);
+        } else if (currentName == "library_controllers") {
+            ReadControllerLibrary(currentNode);
+        } else if (currentName == "library_images") {
+            ReadImageLibrary(currentNode);
+        } else if (currentName == "library_materials") {
+            ReadMaterialLibrary(currentNode);
+        } else if (currentName == "library_effects") {
+            ReadEffectLibrary(currentNode);
+        } else if (currentName == "library_geometries") {
+            ReadGeometryLibrary(currentNode);
+        } else if (currentName == "library_visual_scenes") {
+            ReadSceneLibrary(currentNode);
+        } else if (currentName == "library_lights") {
+            ReadLightLibrary(currentNode);
+        } else if (currentName == "library_cameras") {
+            ReadCameraLibrary(currentNode);
+        } else if (currentName == "library_nodes") {
+            ReadSceneNode(currentNode, nullptr); /* some hacking to reuse this piece of code */
+        } else if (currentName == "scene") {
+            ReadScene(currentNode);
+        }
+    }
+
+    PostProcessRootAnimations();
+    PostProcessControllers();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads asset information such as coordinate system information and legal blah
+void ColladaParser::ReadAssetInfo(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "unit") {
+            mUnitSize = 1.f;
+            std::string tUnitSizeString;
+            if (XmlParser::getStdStrAttribute(currentNode, "meter", tUnitSizeString)) {
+                try {
+                    fast_atoreal_move<ai_real>(tUnitSizeString.data(), mUnitSize);
+                } catch (const DeadlyImportError& die) {
+                    std::string warning("Collada: Failed to parse meter parameter to real number. Exception:\n");
+                    warning.append(die.what());
+                    ASSIMP_LOG_WARN(warning.data());
+                }
+            }
+        } else if (currentName == "up_axis") {
+            std::string v;
+            if (!XmlParser::getValueAsString(currentNode, v)) {
+                continue;
+            }
+            if (v == "X_UP") {
+                mUpDirection = UP_X;
+            } else if (v == "Z_UP") {
+                mUpDirection = UP_Z;
+            } else {
+                mUpDirection = UP_Y;
+            }
+        } else if (currentName == "contributor") {
+            for (XmlNode currentChildNode : currentNode.children()) {
+                ReadMetaDataItem(currentChildNode, mAssetMetaData);
+            }
+        } else {
+            ReadMetaDataItem(currentNode, mAssetMetaData);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single string metadata item
+void ColladaParser::ReadMetaDataItem(XmlNode &node, StringMetaData &metadata) {
+    const Collada::MetaKeyPairVector &key_renaming = GetColladaAssimpMetaKeysCamelCase();
+    const std::string name = node.name();
+    if (name.empty()) {
+        return;
+    }
+
+    std::string v;
+    if (!XmlParser::getValueAsString(node, v)) {
+        return;
+    }
+
+    v = ai_trim(v);
+    aiString aistr;
+    aistr.Set(v);
+
+    std::string camel_key_str(name);
+    ToCamelCase(camel_key_str);
+
+    size_t found_index;
+    if (FindCommonKey(camel_key_str, key_renaming, found_index)) {
+        metadata.emplace(key_renaming[found_index].second, aistr);
+    } else {
+        metadata.emplace(camel_key_str, aistr);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation clips
+void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    std::string animName;
+    if (!XmlParser::getStdStrAttribute(node, "name", animName)) {
+        if (!XmlParser::getStdStrAttribute(node, "id", animName)) {
+            animName = std::string("animation_") + ai_to_string(mAnimationClipLibrary.size());
+        }
+    }
+
+    std::pair<std::string, std::vector<std::string>> clip;
+    clip.first = animName;
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "instance_animation") {
+            std::string url;
+            readUrlAttribute(currentNode, url);
+            clip.second.push_back(url);
+        }
+
+        if (clip.second.size() > 0) {
+            mAnimationClipLibrary.push_back(clip);
+        }
+    }
+}
+
+void ColladaParser::PostProcessControllers() {
+    std::string meshId;
+    for (auto &it : mControllerLibrary) {
+        meshId = it.second.mMeshId;
+        if (meshId.empty()) {
+            continue;
+        }
+
+        ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
+        while (findItr != mControllerLibrary.end()) {
+            meshId = findItr->second.mMeshId;
+            findItr = mControllerLibrary.find(meshId);
+        }
+
+        it.second.mMeshId = meshId;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Re-build animations from animation clip library, if present, otherwise combine single-channel animations
+void ColladaParser::PostProcessRootAnimations() {
+    if (mAnimationClipLibrary.empty()) {
+        mAnims.CombineSingleChannelAnimations();
+        return;
+    }
+
+    Animation temp;
+    for (auto &it : mAnimationClipLibrary) {
+        std::string clipName = it.first;
+
+        Animation *clip = new Animation();
+        clip->mName = clipName;
+
+        temp.mSubAnims.push_back(clip);
+
+        for (const std::string &animationID : it.second) {
+            AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
+
+            if (animation != mAnimationLibrary.end()) {
+                Animation *pSourceAnimation = animation->second;
+                pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
+            }
+        }
+    }
+
+    mAnims = temp;
+
+    // Ensure no double deletes.
+    temp.mSubAnims.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation library
+void ColladaParser::ReadAnimationLibrary(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "animation") {
+            ReadAnimation(currentNode, &mAnims);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation into the given parent structure
+void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) {
+    if (node.empty()) {
+        return;
+    }
+
+    // an <animation> element may be a container for grouping sub-elements or an animation channel
+    // this is the channel collection by ID, in case it has channels
+    using ChannelMap = std::map<std::string, AnimationChannel>;
+    ChannelMap channels;
+    // this is the anim container in case we're a container
+    Animation *anim = nullptr;
+
+    // optional name given as an attribute
+    std::string animName;
+    if (!XmlParser::getStdStrAttribute(node, "name", animName)) {
+        animName = "animation";
+    }
+
+    std::string animID;
+    pugi::xml_attribute idAttr = node.attribute("id");
+    if (idAttr) {
+        animID = idAttr.as_string();
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "animation") {
+            if (!anim) {
+                anim = new Animation;
+                anim->mName = animName;
+                pParent->mSubAnims.push_back(anim);
+            }
+
+            // recurse into the sub-element
+            ReadAnimation(currentNode, anim);
+        } else if (currentName == "source") {
+            ReadSource(currentNode);
+        } else if (currentName == "sampler") {
+            std::string id;
+            if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
+                // have it read into a channel
+                ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first;
+                ReadAnimationSampler(currentNode, newChannel->second);
+            }
+        } else if (currentName == "channel") {
+            std::string source_name, target;
+            XmlParser::getStdStrAttribute(currentNode, "source", source_name);
+            XmlParser::getStdStrAttribute(currentNode, "target", target);
+            if (source_name[0] == '#') {
+                source_name = source_name.substr(1, source_name.size() - 1);
+            }
+            ChannelMap::iterator cit = channels.find(source_name);
+            if (cit != channels.end()) {
+                cit->second.mTarget = target;
+            }
+        }
+    }
+
+    // it turned out to have channels - add them
+    if (!channels.empty()) {
+        if (nullptr == anim) {
+            anim = new Animation;
+            anim->mName = animName;
+            pParent->mSubAnims.push_back(anim);
+        }
+
+        for (const auto &channel : channels) {
+            anim->mChannels.push_back(channel.second);
+        }
+
+        if (idAttr) {
+            mAnimationLibrary[animID] = anim;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation sampler into the given anim channel
+void ColladaParser::ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "input") {
+            if (XmlParser::hasAttribute(currentNode, "semantic")) {
+                std::string semantic, sourceAttr;
+                XmlParser::getStdStrAttribute(currentNode, "semantic", semantic);
+                if (XmlParser::hasAttribute(currentNode, "source")) {
+                    XmlParser::getStdStrAttribute(currentNode, "source", sourceAttr);
+                    const char *source = sourceAttr.c_str();
+                    if (source[0] != '#') {
+                        throw DeadlyImportError("Unsupported URL format");
+                    }
+                    source++;
+
+                    if (semantic == "INPUT") {
+                        pChannel.mSourceTimes = source;
+                    } else if (semantic == "OUTPUT") {
+                        pChannel.mSourceValues = source;
+                    } else if (semantic == "IN_TANGENT") {
+                        pChannel.mInTanValues = source;
+                    } else if (semantic == "OUT_TANGENT") {
+                        pChannel.mOutTanValues = source;
+                    } else if (semantic == "INTERPOLATION") {
+                        pChannel.mInterpolationValues = source;
+                    }
+                }
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the skeleton controller library
+void ColladaParser::ReadControllerLibrary(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName != "controller") {
+            continue;
+        }
+        std::string id;
+        if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
+            mControllerLibrary[id] = Controller();
+            ReadController(currentNode, mControllerLibrary[id]);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a controller into the given mesh structure
+void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controller) {
+    // initial values
+    controller.mType = Skin;
+    controller.mMethod = Normalized;
+
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "morph") {
+            controller.mType = Morph;
+            controller.mMeshId = currentNode.attribute("source").as_string();
+            int methodIndex = currentNode.attribute("method").as_int();
+            if (methodIndex > 0) {
+                std::string method;
+                XmlParser::getValueAsString(currentNode, method);
+
+                if (method == "RELATIVE") {
+                    controller.mMethod = Relative;
+                }
+            }
+        } else if (currentName == "skin") {
+            std::string id;
+            if (XmlParser::getStdStrAttribute(currentNode, "source", id)) {
+                controller.mMeshId = id.substr(1, id.size() - 1);
+            }
+        } else if (currentName == "bind_shape_matrix") {
+            std::string v;
+            XmlParser::getValueAsString(currentNode, v);
+            const char *content = v.c_str();
+            for (unsigned int a = 0; a < 16; a++) {
+                SkipSpacesAndLineEnd(&content);
+                // read a number
+                content = fast_atoreal_move<ai_real>(content, controller.mBindShapeMatrix[a]);
+                // skip whitespace after it
+                SkipSpacesAndLineEnd(&content);
+            }
+        } else if (currentName == "source") {
+            ReadSource(currentNode);
+        } else if (currentName == "joints") {
+            ReadControllerJoints(currentNode, controller);
+        } else if (currentName == "vertex_weights") {
+            ReadControllerWeights(currentNode, controller);
+        } else if (currentName == "targets") {
+            for (XmlNode currentChildNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+                const std::string &currentChildName = currentChildNode.name();
+                if (currentChildName == "input") {
+                    const char *semantics = currentChildNode.attribute("semantic").as_string();
+                    const char *source = currentChildNode.attribute("source").as_string();
+                    if (strcmp(semantics, "MORPH_TARGET") == 0) {
+                        controller.mMorphTarget = source + 1;
+                    } else if (strcmp(semantics, "MORPH_WEIGHT") == 0) {
+                        controller.mMorphWeight = source + 1;
+                    }
+                }
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint definitions for the given controller
+void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pController) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "input") {
+            const char *attrSemantic = currentNode.attribute("semantic").as_string();
+            const char *attrSource = currentNode.attribute("source").as_string();
+            if (attrSource[0] != '#') {
+                throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of <joints> data <input> element");
+            }
+            ++attrSource;
+            // parse source URL to corresponding source
+            if (strcmp(attrSemantic, "JOINT") == 0) {
+                pController.mJointNameSource = attrSource;
+            } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) {
+                pController.mJointOffsetMatrixSource = attrSource;
+            } else {
+                throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in <joints> data <input> element");
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint weights for the given controller
+void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pController) {
+    // Read vertex count from attributes and resize the array accordingly
+    int vertexCount = 0;
+    XmlParser::getIntAttribute(node, "count", vertexCount);
+    pController.mWeightCounts.resize(vertexCount);
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "input") {
+            InputChannel channel;
+
+            const char *attrSemantic = currentNode.attribute("semantic").as_string();
+            const char *attrSource = currentNode.attribute("source").as_string();
+            channel.mOffset = currentNode.attribute("offset").as_int();
+
+            // local URLS always start with a '#'. We don't support global URLs
+            if (attrSource[0] != '#') {
+                throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of <vertex_weights> data <input> element");
+            }
+            channel.mAccessor = attrSource + 1;
+
+            // parse source URL to corresponding source
+            if (strcmp(attrSemantic, "JOINT") == 0) {
+                pController.mWeightInputJoints = channel;
+            } else if (strcmp(attrSemantic, "WEIGHT") == 0) {
+                pController.mWeightInputWeights = channel;
+            } else {
+                throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in <vertex_weights> data <input> element");
+            }
+        } else if (currentName == "vcount" && vertexCount > 0) {
+            const char *text = currentNode.text().as_string();
+            size_t numWeights = 0;
+            for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) {
+                if (*text == 0) {
+                    throw DeadlyImportError("Out of data while reading <vcount>");
+                }
+
+                *it = strtoul10(text, &text);
+                numWeights += *it;
+                SkipSpacesAndLineEnd(&text);
+            }
+            // reserve weight count
+            pController.mWeights.resize(numWeights);
+        } else if (currentName == "v" && vertexCount > 0) {
+            // read JointIndex - WeightIndex pairs
+            std::string stdText;
+            XmlParser::getValueAsString(currentNode, stdText);
+            const char *text = stdText.c_str();
+            for (std::vector<std::pair<size_t, size_t>>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) {
+                if (text == 0) {
+                    throw DeadlyImportError("Out of data while reading <vertex_weights>");
+                }
+                it->first = strtoul10(text, &text);
+                SkipSpacesAndLineEnd(&text);
+                if (*text == 0) {
+                    throw DeadlyImportError("Out of data while reading <vertex_weights>");
+                }
+                it->second = strtoul10(text, &text);
+                SkipSpacesAndLineEnd(&text);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the image library contents
+void ColladaParser::ReadImageLibrary(XmlNode &node) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "image") {
+            std::string id;
+            if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
+                mImageLibrary[id] = Image();
+                // read on from there
+                ReadImage(currentNode, mImageLibrary[id]);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an image entry into the given image
+void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == "image") {
+            // Ignore
+            continue;
+        } else if (currentName == "init_from") {
+            if (mFormat == FV_1_4_n) {
+                // FIX: C4D exporter writes empty <init_from/> tags
+                if (!currentNode.empty()) {
+                    // element content is filename - hopefully
+                    const char *sz = currentNode.text().as_string();
+                    if (nullptr != sz) {
+                        aiString filepath(sz);
+                        UriDecodePath(filepath);
+                        pImage.mFileName = filepath.C_Str();
+                    }
+                }
+                if (!pImage.mFileName.length()) {
+                    pImage.mFileName = "unknown_texture";
+                }
+            }
+        } else if (mFormat == FV_1_5_n) {
+            std::string value;
+            XmlNode refChild = currentNode.child("ref");
+            XmlNode hexChild = currentNode.child("hex");
+            if (refChild) {
+                // element content is filename - hopefully
+                if (XmlParser::getValueAsString(refChild, value)) {
+                    aiString filepath(value);
+                    UriDecodePath(filepath);
+                    pImage.mFileName = filepath.C_Str();
+                }
+            } else if (hexChild && !pImage.mFileName.length()) {
+                // embedded image. get format
+                pImage.mEmbeddedFormat = hexChild.attribute("format").as_string();
+                if (pImage.mEmbeddedFormat.empty()) {
+                    ASSIMP_LOG_WARN("Collada: Unknown image file format");
+                }
+
+                XmlParser::getValueAsString(hexChild, value);
+                const char *data = value.c_str();
+                // hexadecimal-encoded binary octets. First of all, find the
+                // required buffer size to reserve enough storage.
+                const char *cur = data;
+                while (!IsSpaceOrNewLine(*cur)) {
+                    ++cur;
+                }
+
+                const unsigned int size = (unsigned int)(cur - data) * 2;
+                pImage.mImageData.resize(size);
+                for (unsigned int i = 0; i < size; ++i) {
+                    pImage.mImageData[i] = HexOctetToDecimal(data + (i << 1));
+                }
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the material library
+void ColladaParser::ReadMaterialLibrary(XmlNode &node) {
+    std::map<std::string, int> names;
+    for (XmlNode &currentNode : node.children()) {
+        std::string id = currentNode.attribute("id").as_string();
+        std::string name = currentNode.attribute("name").as_string();
+        mMaterialLibrary[id] = Material();
+
+        if (!name.empty()) {
+            std::map<std::string, int>::iterator it = names.find(name);
+            if (it != names.end()) {
+                std::ostringstream strStream;
+                strStream << ++it->second;
+                name.append(" " + strStream.str());
+            } else {
+                names[name] = 0;
+            }
+
+            mMaterialLibrary[id].mName = name;
+        }
+
+        ReadMaterial(currentNode, mMaterialLibrary[id]);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the light library
+void ColladaParser::ReadLightLibrary(XmlNode &node) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "light") {
+            std::string id;
+            if (XmlParser::getStdStrAttribute(currentNode, "id", id)) {
+                ReadLight(currentNode, mLightLibrary[id] = Light());
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the camera library
+void ColladaParser::ReadCameraLibrary(XmlNode &node) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "camera") {
+            std::string id;
+            if (!XmlParser::getStdStrAttribute(currentNode, "id", id)) {
+                continue;
+            }
+
+            // create an entry and store it in the library under its ID
+            Camera &cam = mCameraLibrary[id];
+            std::string name;
+            if (!XmlParser::getStdStrAttribute(currentNode, "name", name)) {
+                continue;
+            }
+            if (!name.empty()) {
+                cam.mName = name;
+            }
+            ReadCamera(currentNode, cam);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a material entry into the given material
+void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "instance_effect") {
+            std::string url;
+            readUrlAttribute(currentNode, url);
+            pMaterial.mEffect = url;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a light entry into the given light
+void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) {
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    // TODO: Check the current technique and skip over unsupported extra techniques
+
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "spot") {
+            pLight.mType = aiLightSource_SPOT;
+        } else if (currentName == "ambient") {
+            pLight.mType = aiLightSource_AMBIENT;
+        } else if (currentName == "directional") {
+            pLight.mType = aiLightSource_DIRECTIONAL;
+        } else if (currentName == "point") {
+            pLight.mType = aiLightSource_POINT;
+        } else if (currentName == "color") {
+            // text content contains 3 floats
+            std::string v;
+            XmlParser::getValueAsString(currentNode, v);
+            const char *content = v.c_str();
+
+            content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.r);
+            SkipSpacesAndLineEnd(&content);
+
+            content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.g);
+            SkipSpacesAndLineEnd(&content);
+
+            content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.b);
+            SkipSpacesAndLineEnd(&content);
+        } else if (currentName == "constant_attenuation") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mAttConstant);
+        } else if (currentName == "linear_attenuation") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mAttLinear);
+        } else if (currentName == "quadratic_attenuation") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mAttQuadratic);
+        } else if (currentName == "falloff_angle") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle);
+        } else if (currentName == "falloff_exponent") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mFalloffExponent);
+        }
+        // FCOLLADA extensions
+        // -------------------------------------------------------
+        else if (currentName == "outer_cone") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle);
+        } else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone
+            XmlParser::getValueAsFloat(currentNode, pLight.mPenumbraAngle);
+        } else if (currentName == "intensity") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mIntensity);
+        }
+        else if (currentName == "falloff") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle);
+        } else if (currentName == "hotspot_beam") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle);
+        }
+        // OpenCOLLADA extensions
+        // -------------------------------------------------------
+        else if (currentName == "decay_falloff") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a camera entry into the given light
+void ColladaParser::ReadCamera(XmlNode &node, Collada::Camera &camera) {
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "orthographic") {
+            camera.mOrtho = true;
+        } else if (currentName == "xfov" || currentName == "xmag") {
+            XmlParser::getValueAsFloat(currentNode, camera.mHorFov);
+        } else if (currentName == "yfov" || currentName == "ymag") {
+            XmlParser::getValueAsFloat(currentNode, camera.mVerFov);
+        } else if (currentName == "aspect_ratio") {
+            XmlParser::getValueAsFloat(currentNode, camera.mAspect);
+        } else if (currentName == "znear") {
+            XmlParser::getValueAsFloat(currentNode, camera.mZNear);
+        } else if (currentName == "zfar") {
+            XmlParser::getValueAsFloat(currentNode, camera.mZFar);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the effect library
+void ColladaParser::ReadEffectLibrary(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "effect") {
+            // read ID. Do I have to repeat my ranting about "optional" attributes?
+            std::string id;
+            XmlParser::getStdStrAttribute(currentNode, "id", id);
+
+            // create an entry and store it in the library under its ID
+            mEffectLibrary[id] = Effect();
+
+            // read on from there
+            ReadEffect(currentNode, mEffectLibrary[id]);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry into the given effect
+void ColladaParser::ReadEffect(XmlNode &node, Collada::Effect &pEffect) {
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "profile_COMMON") {
+            ReadEffectProfileCommon(currentNode, pEffect);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an COMMON effect profile
+void ColladaParser::ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect) {
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string currentName = currentNode.name();
+        if (currentName == "newparam") {
+            // save ID
+            std::string sid = currentNode.attribute("sid").as_string();
+            pEffect.mParams[sid] = EffectParam();
+            ReadEffectParam(currentNode, pEffect.mParams[sid]);
+        } else if (currentName == "technique" || currentName == "extra") {
+            // just syntactic sugar
+        } else if (mFormat == FV_1_4_n && currentName == "image") {
+            // read ID. Another entry which is "optional" by design but obligatory in reality
+            std::string id = currentNode.attribute("id").as_string();
+
+            // create an entry and store it in the library under its ID
+            mImageLibrary[id] = Image();
+
+            // read on from there
+            ReadImage(currentNode, mImageLibrary[id]);
+        } else if (currentName == "phong")
+            pEffect.mShadeType = Shade_Phong;
+        else if (currentName == "constant")
+            pEffect.mShadeType = Shade_Constant;
+        else if (currentName == "lambert")
+            pEffect.mShadeType = Shade_Lambert;
+        else if (currentName == "blinn")
+            pEffect.mShadeType = Shade_Blinn;
+
+        /* Color + texture properties */
+        else if (currentName == "emission")
+            ReadEffectColor(currentNode, pEffect.mEmissive, pEffect.mTexEmissive);
+        else if (currentName == "ambient")
+            ReadEffectColor(currentNode, pEffect.mAmbient, pEffect.mTexAmbient);
+        else if (currentName == "diffuse")
+            ReadEffectColor(currentNode, pEffect.mDiffuse, pEffect.mTexDiffuse);
+        else if (currentName == "specular")
+            ReadEffectColor(currentNode, pEffect.mSpecular, pEffect.mTexSpecular);
+        else if (currentName == "reflective") {
+            ReadEffectColor(currentNode, pEffect.mReflective, pEffect.mTexReflective);
+        } else if (currentName == "transparent") {
+            pEffect.mHasTransparency = true;
+            const char *opaque = currentNode.attribute("opaque").as_string();
+            //const char *opaque = mReader->getAttributeValueSafe("opaque");
+
+            if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) {
+                pEffect.mRGBTransparency = true;
+            }
+
+            // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure...
+            if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) {
+                pEffect.mInvertTransparency = true;
+            }
+
+            ReadEffectColor(currentNode, pEffect.mTransparent, pEffect.mTexTransparent);
+        } else if (currentName == "shininess")
+            ReadEffectFloat(currentNode, pEffect.mShininess);
+        else if (currentName == "reflectivity")
+            ReadEffectFloat(currentNode, pEffect.mReflectivity);
+
+        /* Single scalar properties */
+        else if (currentName == "transparency")
+            ReadEffectFloat(currentNode, pEffect.mTransparency);
+        else if (currentName == "index_of_refraction")
+            ReadEffectFloat(currentNode, pEffect.mRefractIndex);
+
+        // GOOGLEEARTH/OKINO extensions
+        // -------------------------------------------------------
+        else if (currentName == "double_sided")
+            XmlParser::getValueAsBool(currentNode, pEffect.mDoubleSided);
+
+        // FCOLLADA extensions
+        // -------------------------------------------------------
+        else if (currentName == "bump") {
+            aiColor4D dummy;
+            ReadEffectColor(currentNode, dummy, pEffect.mTexBump);
+        }
+
+        // MAX3D extensions
+        // -------------------------------------------------------
+        else if (currentName == "wireframe") {
+            XmlParser::getValueAsBool(currentNode, pEffect.mWireframe);
+        } else if (currentName == "faceted") {
+            XmlParser::getValueAsBool(currentNode, pEffect.mFaceted);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read texture wrapping + UV transform settings from a profile==Maya chunk
+void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) {
+    if (node.empty()) {
+        return;
+    }
+
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        // MAYA extensions
+        // -------------------------------------------------------
+        if (currentName == "wrapU") {
+            XmlParser::getValueAsBool(currentNode, out.mWrapU);
+        } else if (currentName == "wrapV") {
+            XmlParser::getValueAsBool(currentNode, out.mWrapV);
+        } else if (currentName == "mirrorU") {
+            XmlParser::getValueAsBool(currentNode, out.mMirrorU);
+        } else if (currentName == "mirrorV") {
+            XmlParser::getValueAsBool(currentNode, out.mMirrorV);
+        } else if (currentName == "repeatU") {
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.x);
+        } else if (currentName == "repeatV") {
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.y);
+        } else if (currentName == "offsetU") {
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.x);
+        } else if (currentName == "offsetV") {
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.y);
+        } else if (currentName == "rotateUV") {
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mRotation);
+        } else if (currentName == "blend_mode") {
+            std::string v;
+            XmlParser::getValueAsString(currentNode, v);
+            const char *sz = v.c_str();
+            // http://www.feelingsoftware.com/content/view/55/72/lang,en/
+            // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
+            if (0 == ASSIMP_strincmp(sz, "ADD", 3))
+                out.mOp = aiTextureOp_Add;
+            else if (0 == ASSIMP_strincmp(sz, "SUBTRACT", 8))
+                out.mOp = aiTextureOp_Subtract;
+            else if (0 == ASSIMP_strincmp(sz, "MULTIPLY", 8))
+                out.mOp = aiTextureOp_Multiply;
+            else {
+                ASSIMP_LOG_WARN("Collada: Unsupported MAYA texture blend mode");
+            }
+        }
+        // OKINO extensions
+        // -------------------------------------------------------
+        else if (currentName == "weighting") {
+            XmlParser::getValueAsFloat(currentNode, out.mWeighting);
+        } else if (currentName == "mix_with_previous_layer") {
+            XmlParser::getValueAsFloat(currentNode, out.mMixWithPrevious);
+        }
+        // MAX3D extensions
+        // -------------------------------------------------------
+        else if (currentName == "amount") {
+            XmlParser::getValueAsFloat(currentNode, out.mWeighting);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a color or a texture defining that color
+void ColladaParser::ReadEffectColor(XmlNode &node, aiColor4D &pColor, Sampler &pSampler) {
+    if (node.empty()) {
+        return;
+    }
+
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "color") {
+            // text content contains 4 floats
+            std::string v;
+            XmlParser::getValueAsString(currentNode, v);
+            const char *content = v.c_str();
+
+            content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.r);
+            SkipSpacesAndLineEnd(&content);
+
+            content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.g);
+            SkipSpacesAndLineEnd(&content);
+
+            content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.b);
+            SkipSpacesAndLineEnd(&content);
+
+            content = fast_atoreal_move<ai_real>(content, (ai_real &)pColor.a);
+            SkipSpacesAndLineEnd(&content);
+        } else if (currentName == "texture") {
+            // get name of source texture/sampler
+            XmlParser::getStdStrAttribute(currentNode, "texture", pSampler.mName);
+
+            // get name of UV source channel. Specification demands it to be there, but some exporters
+            // don't write it. It will be the default UV channel in case it's missing.
+            XmlParser::getStdStrAttribute(currentNode, "texcoord", pSampler.mUVChannel);
+
+            // as we've read texture, the color needs to be 1,1,1,1
+            pColor = aiColor4D(1.f, 1.f, 1.f, 1.f);
+        } else if (currentName == "technique") {
+            std::string profile;
+            XmlParser::getStdStrAttribute(currentNode, "profile", profile);
+
+            // Some extensions are quite useful ... ReadSamplerProperties processes
+            // several extensions in MAYA, OKINO and MAX3D profiles.
+            if (!::strcmp(profile.c_str(), "MAYA") || !::strcmp(profile.c_str(), "MAX3D") || !::strcmp(profile.c_str(), "OKINO")) {
+                // get more information on this sampler
+                ReadSamplerProperties(currentNode, pSampler);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a float
+void ColladaParser::ReadEffectFloat(XmlNode &node, ai_real &pFloat) {
+    pFloat = 0.f;
+    XmlNode floatNode = node.child("float");
+    if (floatNode.empty()) {
+        return;
+    }
+    XmlParser::getValueAsFloat(floatNode, pFloat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect parameter specification of any kind
+void ColladaParser::ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam) {
+    if (node.empty()) {
+        return;
+    }
+
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "surface") {
+            // image ID given inside <init_from> tags
+            XmlNode initNode = currentNode.child("init_from");
+            if (initNode) {
+                std::string v;
+                XmlParser::getValueAsString(initNode, v);
+                pParam.mType = Param_Surface;
+                pParam.mReference = v.c_str();
+            }
+        } else if (currentName == "sampler2D" && (FV_1_4_n == mFormat || FV_1_3_n == mFormat)) {
+            // surface ID is given inside <source> tags
+            const char *content = currentNode.value();
+            pParam.mType = Param_Sampler;
+            pParam.mReference = content;
+        } else if (currentName == "sampler2D") {
+            // surface ID is given inside <instance_image> tags
+            std::string url;
+            XmlParser::getStdStrAttribute(currentNode, "url", url);
+            if (url[0] != '#') {
+                throw DeadlyImportError("Unsupported URL format in instance_image");
+            }
+            pParam.mType = Param_Sampler;
+            pParam.mReference = url.c_str() + 1;
+        } else if (currentName == "source") {
+            const char *source = currentNode.child_value();
+            if (nullptr != source) {
+                pParam.mReference = source;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the geometry library contents
+void ColladaParser::ReadGeometryLibrary(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "geometry") {
+            // read ID. Another entry which is "optional" by design but obligatory in reality
+
+            std::string id;
+            XmlParser::getStdStrAttribute(currentNode, "id", id);
+            // create a mesh and store it in the library under its (resolved) ID
+            // Skip and warn if ID is not unique
+            if (mMeshLibrary.find(id) == mMeshLibrary.cend()) {
+                std::unique_ptr<Mesh> mesh(new Mesh(id));
+
+                XmlParser::getStdStrAttribute(currentNode, "name", mesh->mName);
+
+                // read on from there
+                ReadGeometry(currentNode, *mesh);
+                // Read successfully, add to library
+                mMeshLibrary.insert({ id, mesh.release() });
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a geometry from the geometry library.
+void ColladaParser::ReadGeometry(XmlNode &node, Collada::Mesh &pMesh) {
+    if (node.empty()) {
+        return;
+    }
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "mesh") {
+            ReadMesh(currentNode, pMesh);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh from the geometry library
+void ColladaParser::ReadMesh(XmlNode &node, Mesh &pMesh) {
+    if (node.empty()) {
+        return;
+    }
+
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "source") {
+            ReadSource(currentNode);
+        } else if (currentName == "vertices") {
+            ReadVertexData(currentNode, pMesh);
+        } else if (currentName == "triangles" || currentName == "lines" || currentName == "linestrips" ||
+                   currentName == "polygons" || currentName == "polylist" || currentName == "trifans" ||
+                   currentName == "tristrips") {
+            ReadIndexData(currentNode, pMesh);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a source element
+void ColladaParser::ReadSource(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    std::string sourceID;
+    XmlParser::getStdStrAttribute(node, "id", sourceID);
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "float_array" || currentName == "IDREF_array" || currentName == "Name_array") {
+            ReadDataArray(currentNode);
+        } else if (currentName == "technique_common") {
+            XmlNode technique = currentNode.child("accessor");
+            if (!technique.empty()) {
+                ReadAccessor(technique, sourceID);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a data array holding a number of floats, and stores it in the global library
+void ColladaParser::ReadDataArray(XmlNode &node) {
+    std::string name = node.name();
+    bool isStringArray = (name == "IDREF_array" || name == "Name_array");
+
+    // read attributes
+    std::string id;
+    XmlParser::getStdStrAttribute(node, "id", id);
+    unsigned int count = 0;
+    XmlParser::getUIntAttribute(node, "count", count);
+    std::string v;
+    XmlParser::getValueAsString(node, v);
+    v = ai_trim(v);
+    const char *content = v.c_str();
+
+    // read values and store inside an array in the data library
+    mDataLibrary[id] = Data();
+    Data &data = mDataLibrary[id];
+    data.mIsStringArray = isStringArray;
+
+    // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
+    if (content) {
+        if (isStringArray) {
+            data.mStrings.reserve(count);
+            std::string s;
+
+            for (unsigned int a = 0; a < count; a++) {
+                if (*content == 0) {
+                    throw DeadlyImportError("Expected more values while reading IDREF_array contents.");
+                }
+
+                s.clear();
+                while (!IsSpaceOrNewLine(*content))
+                    s += *content++;
+                data.mStrings.push_back(s);
+
+                SkipSpacesAndLineEnd(&content);
+            }
+        } else {
+            data.mValues.reserve(count);
+
+            for (unsigned int a = 0; a < count; a++) {
+                if (*content == 0) {
+                    throw DeadlyImportError("Expected more values while reading float_array contents.");
+                }
+
+                // read a number
+                ai_real value;
+                content = fast_atoreal_move<ai_real>(content, value);
+                data.mValues.push_back(value);
+                // skip whitespace after it
+                SkipSpacesAndLineEnd(&content);
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an accessor and stores it in the global library
+void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) {
+    // read accessor attributes
+    std::string source;
+    XmlParser::getStdStrAttribute(node, "source", source);
+    if (source[0] != '#') {
+        throw DeadlyImportError("Unknown reference format in url \"", source, "\" in source attribute of <accessor> element.");
+    }
+    int count = 0;
+    XmlParser::getIntAttribute(node, "count", count);
+
+    unsigned int offset = 0;
+    if (XmlParser::hasAttribute(node, "offset")) {
+        XmlParser::getUIntAttribute(node, "offset", offset);
+    }
+    unsigned int stride = 1;
+    if (XmlParser::hasAttribute(node, "stride")) {
+        XmlParser::getUIntAttribute(node, "stride", stride);
+    }
+    // store in the library under the given ID
+    mAccessorLibrary[pID] = Accessor();
+    Accessor &acc = mAccessorLibrary[pID];
+    acc.mCount = count;
+    acc.mOffset = offset;
+    acc.mStride = stride;
+    acc.mSource = source.c_str() + 1; // ignore the leading '#'
+    acc.mSize = 0; // gets incremented with every param
+
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "param") {
+            // read data param
+            std::string name;
+            if (XmlParser::hasAttribute(currentNode, "name")) {
+                XmlParser::getStdStrAttribute(currentNode, "name", name);
+
+                // analyse for common type components and store it's sub-offset in the corresponding field
+
+                // Cartesian coordinates
+                if (name == "X")
+                    acc.mSubOffset[0] = acc.mParams.size();
+                else if (name == "Y")
+                    acc.mSubOffset[1] = acc.mParams.size();
+                else if (name == "Z")
+                    acc.mSubOffset[2] = acc.mParams.size();
+
+                /* RGBA colors */
+                else if (name == "R")
+                    acc.mSubOffset[0] = acc.mParams.size();
+                else if (name == "G")
+                    acc.mSubOffset[1] = acc.mParams.size();
+                else if (name == "B")
+                    acc.mSubOffset[2] = acc.mParams.size();
+                else if (name == "A")
+                    acc.mSubOffset[3] = acc.mParams.size();
+
+                /* UVWQ (STPQ) texture coordinates */
+                else if (name == "S")
+                    acc.mSubOffset[0] = acc.mParams.size();
+                else if (name == "T")
+                    acc.mSubOffset[1] = acc.mParams.size();
+                else if (name == "P")
+                    acc.mSubOffset[2] = acc.mParams.size();
+                /* Generic extra data, interpreted as UV data, too*/
+                else if (name == "U")
+                    acc.mSubOffset[0] = acc.mParams.size();
+                else if (name == "V")
+                    acc.mSubOffset[1] = acc.mParams.size();
+            }
+            if (XmlParser::hasAttribute(currentNode, "type")) {
+                // read data type
+                // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
+                // which should be tested for here.
+                std::string type;
+
+                XmlParser::getStdStrAttribute(currentNode, "type", type);
+                if (type == "float4x4")
+                    acc.mSize += 16;
+                else
+                    acc.mSize += 1;
+            }
+
+            acc.mParams.push_back(name);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-vertex mesh data into the given mesh
+void ColladaParser::ReadVertexData(XmlNode &node, Mesh &pMesh) {
+    // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
+    XmlParser::getStdStrAttribute(node, "id", pMesh.mVertexID);
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "input") {
+            ReadInputChannel(currentNode, pMesh.mPerVertexData);
+        } else {
+            throw DeadlyImportError("Unexpected sub element <", currentName, "> in tag <vertices>");
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-index mesh data into the given mesh
+void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) {
+    std::vector<size_t> vcount;
+    std::vector<InputChannel> perIndexData;
+
+    unsigned int numPrimitives = 0;
+    XmlParser::getUIntAttribute(node, "count", numPrimitives);
+    // read primitive count from the attribute
+    //int attrCount = GetAttribute("count");
+    //size_t numPrimitives = (size_t)mReader->getAttributeValueAsInt(attrCount);
+    // some mesh types (e.g. tristrips) don't specify primitive count upfront,
+    // so we need to sum up the actual number of primitives while we read the <p>-tags
+    size_t actualPrimitives = 0;
+    SubMesh subgroup;
+    if (XmlParser::hasAttribute(node, "material")) {
+        XmlParser::getStdStrAttribute(node, "material", subgroup.mMaterial);
+    }
+
+    // distinguish between polys and triangles
+    std::string elementName = node.name();
+    PrimitiveType primType = Prim_Invalid;
+    if (elementName == "lines")
+        primType = Prim_Lines;
+    else if (elementName == "linestrips")
+        primType = Prim_LineStrip;
+    else if (elementName == "polygons")
+        primType = Prim_Polygon;
+    else if (elementName == "polylist")
+        primType = Prim_Polylist;
+    else if (elementName == "triangles")
+        primType = Prim_Triangles;
+    else if (elementName == "trifans")
+        primType = Prim_TriFans;
+    else if (elementName == "tristrips")
+        primType = Prim_TriStrips;
+
+    ai_assert(primType != Prim_Invalid);
+
+    // also a number of <input> elements, but in addition a <p> primitive collection and probably index counts for all primitives
+    XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
+    XmlNode currentNode;
+    while (xmlIt.getNext(currentNode)) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "input") {
+            ReadInputChannel(currentNode, perIndexData);
+        } else if (currentName == "vcount") {
+            if (!currentNode.empty()) {
+                if (numPrimitives) // It is possible to define a mesh without any primitives
+                {
+                    // case <polylist> - specifies the number of indices for each polygon
+                    std::string v;
+                    XmlParser::getValueAsString(currentNode, v);
+                    const char *content = v.c_str();
+                    vcount.reserve(numPrimitives);
+                    for (unsigned int a = 0; a < numPrimitives; a++) {
+                        if (*content == 0) {
+                            throw DeadlyImportError("Expected more values while reading <vcount> contents.");
+                        }
+                        // read a number
+                        vcount.push_back((size_t)strtoul10(content, &content));
+                        // skip whitespace after it
+                        SkipSpacesAndLineEnd(&content);
+                    }
+                }
+            }
+        } else if (currentName == "p") {
+            if (!currentNode.empty()) {
+                // now here the actual fun starts - these are the indices to construct the mesh data from
+                actualPrimitives += ReadPrimitives(currentNode, pMesh, perIndexData, numPrimitives, vcount, primType);
+            }
+        } else if (currentName == "extra") {
+            // skip
+        } else if (currentName == "ph") {
+            // skip
+        } else {
+            throw DeadlyImportError("Unexpected sub element <", currentName, "> in tag <", elementName, ">");
+        }
+    }
+
+#ifdef ASSIMP_BUILD_DEBUG
+    if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip &&
+            primType != Prim_Lines) { // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'.
+        ai_assert(actualPrimitives == numPrimitives);
+    }
+#endif
+
+    // only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
+    subgroup.mNumFaces = actualPrimitives;
+    pMesh.mSubMeshes.push_back(subgroup);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single input channel element and stores it in the given array, if valid
+void ColladaParser::ReadInputChannel(XmlNode &node, std::vector<InputChannel> &poChannels) {
+    InputChannel channel;
+
+    // read semantic
+    std::string semantic;
+    XmlParser::getStdStrAttribute(node, "semantic", semantic);
+    channel.mType = GetTypeForSemantic(semantic);
+
+    // read source
+    std::string source;
+    XmlParser::getStdStrAttribute(node, "source", source);
+    if (source[0] != '#') {
+        throw DeadlyImportError("Unknown reference format in url \"", source, "\" in source attribute of <input> element.");
+    }
+    channel.mAccessor = source.c_str() + 1; // skipping the leading #, hopefully the remaining text is the accessor ID only
+
+    // read index offset, if per-index <input>
+    if (XmlParser::hasAttribute(node, "offset")) {
+        XmlParser::getUIntAttribute(node, "offset", (unsigned int &)channel.mOffset);
+    }
+
+    // read set if texture coordinates
+    if (channel.mType == IT_Texcoord || channel.mType == IT_Color) {
+        unsigned int attrSet = 0;
+        if (XmlParser::getUIntAttribute(node, "set", attrSet))
+            channel.mIndex = attrSet;
+    }
+
+    // store, if valid type
+    if (channel.mType != IT_Invalid)
+        poChannels.push_back(channel);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a <p> primitive index list and assembles the mesh data into the given mesh
+size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<InputChannel> &pPerIndexChannels,
+        size_t pNumPrimitives, const std::vector<size_t> &pVCount, PrimitiveType pPrimType) {
+    // determine number of indices coming per vertex
+    // find the offset index for all per-vertex channels
+    size_t numOffsets = 1;
+    size_t perVertexOffset = SIZE_MAX; // invalid value
+    for (const InputChannel &channel : pPerIndexChannels) {
+        numOffsets = std::max(numOffsets, channel.mOffset + 1);
+        if (channel.mType == IT_Vertex)
+            perVertexOffset = channel.mOffset;
+    }
+
+    // determine the expected number of indices
+    size_t expectedPointCount = 0;
+    switch (pPrimType) {
+    case Prim_Polylist: {
+        for (size_t i : pVCount)
+            expectedPointCount += i;
+        break;
+    }
+    case Prim_Lines:
+        expectedPointCount = 2 * pNumPrimitives;
+        break;
+    case Prim_Triangles:
+        expectedPointCount = 3 * pNumPrimitives;
+        break;
+    default:
+        // other primitive types don't state the index count upfront... we need to guess
+        break;
+    }
+
+    // and read all indices into a temporary array
+    std::vector<size_t> indices;
+    if (expectedPointCount > 0) {
+        indices.reserve(expectedPointCount * numOffsets);
+    }
+
+    // It is possible to not contain any indices
+    if (pNumPrimitives > 0) {
+        std::string v;
+        XmlParser::getValueAsString(node, v);
+        const char *content = v.c_str();
+        SkipSpacesAndLineEnd(&content);
+        while (*content != 0) {
+            // read a value.
+            // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
+            int value = std::max(0, strtol10(content, &content));
+            indices.push_back(size_t(value));
+            // skip whitespace after it
+            SkipSpacesAndLineEnd(&content);
+        }
+    }
+
+    // complain if the index count doesn't fit
+    if (expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) {
+        if (pPrimType == Prim_Lines) {
+            // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
+            ReportWarning("Expected different index count in <p> element, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets);
+            pNumPrimitives = (indices.size() / numOffsets) / 2;
+        } else {
+            throw DeadlyImportError("Expected different index count in <p> element.");
+        }
+    } else if (expectedPointCount == 0 && (indices.size() % numOffsets) != 0) {
+        throw DeadlyImportError("Expected different index count in <p> element.");
+    }
+
+    // find the data for all sources
+    for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
+        InputChannel &input = *it;
+        if (input.mResolved) {
+            continue;
+        }
+
+        // find accessor
+        input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
+        // resolve accessor's data pointer as well, if necessary
+        const Accessor *acc = input.mResolved;
+        if (!acc->mData) {
+            acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
+        }
+    }
+    // and the same for the per-index channels
+    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
+        InputChannel &input = *it;
+        if (input.mResolved) {
+            continue;
+        }
+
+        // ignore vertex pointer, it doesn't refer to an accessor
+        if (input.mType == IT_Vertex) {
+            // warn if the vertex channel does not refer to the <vertices> element in the same mesh
+            if (input.mAccessor != pMesh.mVertexID) {
+                throw DeadlyImportError("Unsupported vertex referencing scheme.");
+            }
+            continue;
+        }
+
+        // find accessor
+        input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
+        // resolve accessor's data pointer as well, if necessary
+        const Accessor *acc = input.mResolved;
+        if (!acc->mData) {
+            acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
+        }
+    }
+
+    // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
+    size_t numPrimitives = pNumPrimitives;
+    if (pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
+        numPrimitives = 1;
+    // For continued primitives, the given count is actually the number of <p>'s inside the parent tag
+    if (pPrimType == Prim_TriStrips) {
+        size_t numberOfVertices = indices.size() / numOffsets;
+        numPrimitives = numberOfVertices - 2;
+    }
+    if (pPrimType == Prim_LineStrip) {
+        size_t numberOfVertices = indices.size() / numOffsets;
+        numPrimitives = numberOfVertices - 1;
+    }
+
+    pMesh.mFaceSize.reserve(numPrimitives);
+    pMesh.mFacePosIndices.reserve(indices.size() / numOffsets);
+
+    size_t polylistStartVertex = 0;
+    for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++) {
+        // determine number of points for this primitive
+        size_t numPoints = 0;
+        switch (pPrimType) {
+        case Prim_Lines:
+            numPoints = 2;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_LineStrip:
+            numPoints = 2;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_Triangles:
+            numPoints = 3;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_TriStrips:
+            numPoints = 3;
+            ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_Polylist:
+            numPoints = pVCount[currentPrimitive];
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
+            polylistStartVertex += numPoints;
+            break;
+        case Prim_TriFans:
+        case Prim_Polygon:
+            numPoints = indices.size() / numOffsets;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        default:
+            // LineStrip is not supported due to expected index unmangling
+            throw DeadlyImportError("Unsupported primitive type.");
+            break;
+        }
+
+        // store the face size to later reconstruct the face from
+        pMesh.mFaceSize.push_back(numPoints);
+    }
+
+    // if I ever get my hands on that guy who invented this steaming pile of indirection...
+    return numPrimitives;
+}
+
+///@note This function won't work correctly if both PerIndex and PerVertex channels have same channels.
+///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
+///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior
+void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh &pMesh,
+        std::vector<InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices) {
+    // calculate the base offset of the vertex whose attributes we ant to copy
+    size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
+
+    // don't overrun the boundaries of the index list
+    ai_assert((baseOffset + numOffsets - 1) < indices.size());
+
+    // extract per-vertex channels using the global per-vertex offset
+    for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
+        ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
+    }
+    // and extract per-index channels using there specified offset
+    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it) {
+        ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
+    }
+
+    // store the vertex-data index for later assignment of bone vertex weights
+    pMesh.mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
+}
+
+void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh &pMesh, std::vector<InputChannel> &pPerIndexChannels,
+        size_t currentPrimitive, const std::vector<size_t> &indices) {
+    if (currentPrimitive % 2 != 0) {
+        //odd tristrip triangles need their indices mangled, to preserve winding direction
+        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+    } else { //for non tristrips or even tristrip triangles
+        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extracts a single object from an input channel and stores it in the appropriate mesh data array
+void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, size_t pLocalIndex, Mesh &pMesh) {
+    // ignore vertex referrer - we handle them that separate
+    if (pInput.mType == IT_Vertex) {
+        return;
+    }
+
+    const Accessor &acc = *pInput.mResolved;
+    if (pLocalIndex >= acc.mCount) {
+        throw DeadlyImportError("Invalid data index (", pLocalIndex, "/", acc.mCount, ") in primitive specification");
+    }
+
+    // get a pointer to the start of the data object referred to by the accessor and the local index
+    const ai_real *dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex * acc.mStride;
+
+    // assemble according to the accessors component sub-offset list. We don't care, yet,
+    // what kind of object exactly we're extracting here
+    ai_real obj[4];
+    for (size_t c = 0; c < 4; ++c) {
+        obj[c] = dataObject[acc.mSubOffset[c]];
+    }
+
+    // now we reinterpret it according to the type we're reading here
+    switch (pInput.mType) {
+    case IT_Position: // ignore all position streams except 0 - there can be only one position
+        if (pInput.mIndex == 0) {
+            pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        } else {
+            ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported");
+        }
+        break;
+    case IT_Normal:
+        // pad to current vertex count if necessary
+        if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1)
+            pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0));
+
+        // ignore all normal streams except 0 - there can be only one normal
+        if (pInput.mIndex == 0) {
+            pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        } else {
+            ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported");
+        }
+        break;
+    case IT_Tangent:
+        // pad to current vertex count if necessary
+        if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1)
+            pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0));
+
+        // ignore all tangent streams except 0 - there can be only one tangent
+        if (pInput.mIndex == 0) {
+            pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        } else {
+            ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported");
+        }
+        break;
+    case IT_Bitangent:
+        // pad to current vertex count if necessary
+        if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) {
+            pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1));
+        }
+
+        // ignore all bitangent streams except 0 - there can be only one bitangent
+        if (pInput.mIndex == 0) {
+            pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        } else {
+            ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported");
+        }
+        break;
+    case IT_Texcoord:
+        // up to 4 texture coord sets are fine, ignore the others
+        if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+            // pad to current vertex count if necessary
+            if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1)
+                pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(),
+                        pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0));
+
+            pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2]));
+            if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) {
+                pMesh.mNumUVComponents[pInput.mIndex] = 3;
+            }
+        } else {
+            ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping.");
+        }
+        break;
+    case IT_Color:
+        // up to 4 color sets are fine, ignore the others
+        if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) {
+            // pad to current vertex count if necessary
+            if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1)
+                pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(),
+                        pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1));
+
+            aiColor4D result(0, 0, 0, 1);
+            for (size_t i = 0; i < pInput.mResolved->mSize; ++i) {
+                result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
+            }
+            pMesh.mColors[pInput.mIndex].push_back(result);
+        } else {
+            ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping.");
+        }
+
+        break;
+    default:
+        // IT_Invalid and IT_Vertex
+        ai_assert(false && "shouldn't ever get here");
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the library of node hierarchies and scene parts
+void ColladaParser::ReadSceneLibrary(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "visual_scene") {
+            // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
+            std::string id;
+            XmlParser::getStdStrAttribute(currentNode, "id", id);
+
+            // read name if given.
+            std::string attrName = "Scene";
+            if (XmlParser::hasAttribute(currentNode, "name")) {
+                XmlParser::getStdStrAttribute(currentNode, "name", attrName);
+            }
+
+            // create a node and store it in the library under its ID
+            Node *sceneNode = new Node;
+            sceneNode->mID = id;
+            sceneNode->mName = attrName;
+            mNodeLibrary[sceneNode->mID] = sceneNode;
+
+            ReadSceneNode(currentNode, sceneNode);
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a scene node's contents including children and stores it in the given node
+void ColladaParser::ReadSceneNode(XmlNode &node, Node *pNode) {
+    // quit immediately on <bla/> elements
+    if (node.empty()) {
+        return;
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "node") {
+            Node *child = new Node;
+            if (XmlParser::hasAttribute(currentNode, "id")) {
+                XmlParser::getStdStrAttribute(currentNode, "id", child->mID);
+            }
+            if (XmlParser::hasAttribute(currentNode, "sid")) {
+                XmlParser::getStdStrAttribute(currentNode, "id", child->mSID);
+            }
+            if (XmlParser::hasAttribute(currentNode, "name")) {
+                XmlParser::getStdStrAttribute(currentNode, "name", child->mName);
+            }
+            if (pNode) {
+                pNode->mChildren.push_back(child);
+                child->mParent = pNode;
+            } else {
+                // no parent node given, probably called from <library_nodes> element.
+                // create new node in node library
+                mNodeLibrary[child->mID] = child;
+            }
+
+            // read on recursively from there
+            ReadSceneNode(currentNode, child);
+            continue;
+        } else if (!pNode) {
+            // For any further stuff we need a valid node to work on
+            continue;
+        }
+        if (currentName == "lookat") {
+            ReadNodeTransformation(currentNode, pNode, TF_LOOKAT);
+        } else if (currentName == "matrix") {
+            ReadNodeTransformation(currentNode, pNode, TF_MATRIX);
+        } else if (currentName == "rotate") {
+            ReadNodeTransformation(currentNode, pNode, TF_ROTATE);
+        } else if (currentName == "scale") {
+            ReadNodeTransformation(currentNode, pNode, TF_SCALE);
+        } else if (currentName == "skew") {
+            ReadNodeTransformation(currentNode, pNode, TF_SKEW);
+        } else if (currentName == "translate") {
+            ReadNodeTransformation(currentNode, pNode, TF_TRANSLATE);
+        } else if (currentName == "render" && pNode->mParent == nullptr && 0 == pNode->mPrimaryCamera.length()) {
+            // ... scene evaluation or, in other words, postprocessing pipeline,
+            // or, again in other words, a turing-complete description how to
+            // render a Collada scene. The only thing that is interesting for
+            // us is the primary camera.
+            if (XmlParser::hasAttribute(currentNode, "camera_node")) {
+                std::string s;
+                XmlParser::getStdStrAttribute(currentNode, "camera_node", s);
+                if (s[0] != '#') {
+                    ASSIMP_LOG_ERROR("Collada: Unresolved reference format of camera");
+                } else {
+                    pNode->mPrimaryCamera = s.c_str() + 1;
+                }
+            }
+        } else if (currentName == "instance_node") {
+            // find the node in the library
+            if (XmlParser::hasAttribute(currentNode, "url")) {
+                std::string s;
+                XmlParser::getStdStrAttribute(currentNode, "url", s);
+                if (s[0] != '#') {
+                    ASSIMP_LOG_ERROR("Collada: Unresolved reference format of node");
+                } else {
+                    pNode->mNodeInstances.push_back(NodeInstance());
+                    pNode->mNodeInstances.back().mNode = s.c_str() + 1;
+                }
+            }
+        } else if (currentName == "instance_geometry" || currentName == "instance_controller") {
+            // Reference to a mesh or controller, with possible material associations
+            ReadNodeGeometry(currentNode, pNode);
+        } else if (currentName == "instance_light") {
+            // Reference to a light, name given in 'url' attribute
+            if (XmlParser::hasAttribute(currentNode, "url")) {
+                std::string url;
+                XmlParser::getStdStrAttribute(currentNode, "url", url);
+                if (url[0] != '#') {
+                    throw DeadlyImportError("Unknown reference format in <instance_light> element");
+                }
+
+                pNode->mLights.push_back(LightInstance());
+                pNode->mLights.back().mLight = url.c_str() + 1;
+            }
+        } else if (currentName == "instance_camera") {
+            // Reference to a camera, name given in 'url' attribute
+            if (XmlParser::hasAttribute(currentNode, "url")) {
+                std::string url;
+                XmlParser::getStdStrAttribute(currentNode, "url", url);
+                if (url[0] != '#') {
+                    throw DeadlyImportError("Unknown reference format in <instance_camera> element");
+                }
+                pNode->mCameras.push_back(CameraInstance());
+                pNode->mCameras.back().mCamera = url.c_str() + 1;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
+void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, TransformType pType) {
+    if (node.empty()) {
+        return;
+    }
+
+    std::string tagName = node.name();
+
+    Transform tf;
+    tf.mType = pType;
+
+    // read SID
+    if (XmlParser::hasAttribute(node, "sid")) {
+        XmlParser::getStdStrAttribute(node, "sid", tf.mID);
+    }
+
+    // how many parameters to read per transformation type
+    static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
+    std::string value;
+    XmlParser::getValueAsString(node, value);
+    const char *content = value.c_str();
+
+    // read as many parameters and store in the transformation
+    for (unsigned int a = 0; a < sNumParameters[pType]; a++) {
+        // skip whitespace before the number
+        SkipSpacesAndLineEnd(&content);
+        // read a number
+        content = fast_atoreal_move<ai_real>(content, tf.f[a]);
+    }
+
+    // place the transformation at the queue of the node
+    pNode->mTransforms.push_back(tf);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Processes bind_vertex_input and bind elements
+void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl) {
+    std::string name = node.name();
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "bind_vertex_input") {
+            Collada::InputSemanticMapEntry vn;
+
+            // effect semantic
+            if (XmlParser::hasAttribute(currentNode, "semantic")) {
+                std::string s;
+                XmlParser::getStdStrAttribute(currentNode, "semantic", s);
+                XmlParser::getUIntAttribute(currentNode, "input_semantic", (unsigned int &)vn.mType);
+            }
+            std::string s;
+            XmlParser::getStdStrAttribute(currentNode, "semantic", s);
+
+            // input semantic
+            XmlParser::getUIntAttribute(currentNode, "input_semantic", (unsigned int &)vn.mType);
+
+            // index of input set
+            if (XmlParser::hasAttribute(currentNode, "input_set")) {
+                XmlParser::getUIntAttribute(currentNode, "input_set", vn.mSet);
+            }
+
+            tbl.mMap[s] = vn;
+        } else if (currentName == "bind") {
+            ASSIMP_LOG_WARN("Collada: Found unsupported <bind> element");
+        }
+    }
+}
+
+void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) {
+    // Attempt to load any undefined Collada::Image in ImageLibrary
+    for (auto &it : mImageLibrary) {
+        Collada::Image &image = it.second;
+
+        if (image.mImageData.empty()) {
+            std::unique_ptr<IOStream> image_file(zip_archive.Open(image.mFileName.c_str()));
+            if (image_file) {
+                image.mImageData.resize(image_file->FileSize());
+                image_file->Read(image.mImageData.data(), image_file->FileSize(), 1);
+                image.mEmbeddedFormat = BaseImporter::GetExtension(image.mFileName);
+                if (image.mEmbeddedFormat == "jpeg") {
+                    image.mEmbeddedFormat = "jpg";
+                }
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh reference in a node and adds it to the node's mesh list
+void ColladaParser::ReadNodeGeometry(XmlNode &node, Node *pNode) {
+    // referred mesh is given as an attribute of the <instance_geometry> element
+    std::string url;
+    XmlParser::getStdStrAttribute(node, "url", url);
+    if (url[0] != '#') {
+        throw DeadlyImportError("Unknown reference format");
+    }
+
+    Collada::MeshInstance instance;
+    instance.mMeshOrController = url.c_str() + 1; // skipping the leading #
+
+    for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "bind_material") {
+            XmlNode techNode = currentNode.child("technique_common");
+            if (techNode) {
+                for (XmlNode instanceMatNode = techNode.child("instance_material"); instanceMatNode; instanceMatNode = instanceMatNode.next_sibling())
+                {
+                    const std::string &instance_name = instanceMatNode.name();
+                    if (instance_name == "instance_material")
+                    {
+                        // read ID of the geometry subgroup and the target material
+                        std::string group;
+                        XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group);
+                        XmlParser::getStdStrAttribute(instanceMatNode, "target", url);
+                        const char *urlMat = url.c_str();
+                        Collada::SemanticMappingTable s;
+                        if (urlMat[0] == '#')
+                            urlMat++;
+
+                        s.mMatName = urlMat;
+                        // store the association
+                        instance.mMaterials[group] = s;
+                        ReadMaterialVertexInputBinding(instanceMatNode, s);
+                    }
+                }
+            }
+        }
+    }
+
+    // store it
+    pNode->mMeshes.push_back(instance);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the collada scene
+void ColladaParser::ReadScene(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "instance_visual_scene") {
+            // should be the first and only occurrence
+            if (mRootNode) {
+                throw DeadlyImportError("Invalid scene containing multiple root nodes in <instance_visual_scene> element");
+            }
+
+            // read the url of the scene to instance. Should be of format "#some_name"
+            std::string url;
+            XmlParser::getStdStrAttribute(currentNode, "url", url);
+            if (url[0] != '#') {
+                throw DeadlyImportError("Unknown reference format in <instance_visual_scene> element");
+            }
+
+            // find the referred scene, skip the leading #
+            NodeLibrary::const_iterator sit = mNodeLibrary.find(url.c_str() + 1);
+            if (sit == mNodeLibrary.end()) {
+                throw DeadlyImportError("Unable to resolve visual_scene reference \"", std::string(url), "\" in <instance_visual_scene> element.");
+            }
+            mRootNode = sit->second;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates the resulting transformation from all the given transform steps
+aiMatrix4x4 ColladaParser::CalculateResultTransform(const std::vector<Transform> &pTransforms) const {
+    aiMatrix4x4 res;
+
+    for (std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it) {
+        const Transform &tf = *it;
+        switch (tf.mType) {
+        case TF_LOOKAT: {
+            aiVector3D pos(tf.f[0], tf.f[1], tf.f[2]);
+            aiVector3D dstPos(tf.f[3], tf.f[4], tf.f[5]);
+            aiVector3D up = aiVector3D(tf.f[6], tf.f[7], tf.f[8]).Normalize();
+            aiVector3D dir = aiVector3D(dstPos - pos).Normalize();
+            aiVector3D right = (dir ^ up).Normalize();
+
+            res *= aiMatrix4x4(
+                    right.x, up.x, -dir.x, pos.x,
+                    right.y, up.y, -dir.y, pos.y,
+                    right.z, up.z, -dir.z, pos.z,
+                    0, 0, 0, 1);
+            break;
+        }
+        case TF_ROTATE: {
+            aiMatrix4x4 rot;
+            ai_real angle = tf.f[3] * ai_real(AI_MATH_PI) / ai_real(180.0);
+            aiVector3D axis(tf.f[0], tf.f[1], tf.f[2]);
+            aiMatrix4x4::Rotation(angle, axis, rot);
+            res *= rot;
+            break;
+        }
+        case TF_TRANSLATE: {
+            aiMatrix4x4 trans;
+            aiMatrix4x4::Translation(aiVector3D(tf.f[0], tf.f[1], tf.f[2]), trans);
+            res *= trans;
+            break;
+        }
+        case TF_SCALE: {
+            aiMatrix4x4 scale(tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
+                    0.0f, 0.0f, 0.0f, 1.0f);
+            res *= scale;
+            break;
+        }
+        case TF_SKEW:
+            // TODO: (thom)
+            ai_assert(false);
+            break;
+        case TF_MATRIX: {
+            aiMatrix4x4 mat(tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
+                    tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
+            res *= mat;
+            break;
+        }
+        default:
+            ai_assert(false);
+            break;
+        }
+    }
+
+    return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Determines the input data type for the given semantic string
+Collada::InputType ColladaParser::GetTypeForSemantic(const std::string &semantic) {
+    if (semantic.empty()) {
+        ASSIMP_LOG_WARN("Vertex input type is empty.");
+        return IT_Invalid;
+    }
+
+    if (semantic == "POSITION")
+        return IT_Position;
+    else if (semantic == "TEXCOORD")
+        return IT_Texcoord;
+    else if (semantic == "NORMAL")
+        return IT_Normal;
+    else if (semantic == "COLOR")
+        return IT_Color;
+    else if (semantic == "VERTEX")
+        return IT_Vertex;
+    else if (semantic == "BINORMAL" || semantic == "TEXBINORMAL")
+        return IT_Bitangent;
+    else if (semantic == "TANGENT" || semantic == "TEXTANGENT")
+        return IT_Tangent;
+
+    ASSIMP_LOG_WARN("Unknown vertex input type \"", semantic, "\". Ignoring.");
+    return IT_Invalid;
+}
+
+#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

+ 348 - 0
Engine/lib/assimp/code/AssetLib/Collada/ColladaParser.h

@@ -0,0 +1,348 @@
+/*
+ Open Asset Import Library (assimp)
+ ----------------------------------------------------------------------
+
+ Copyright (c) 2006-2022, 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 ColladaParser.h
+ *  @brief Defines the parser helper class for the collada loader
+ */
+
+#pragma once
+#ifndef AI_COLLADAPARSER_H_INC
+#define AI_COLLADAPARSER_H_INC
+
+#include "ColladaHelper.h"
+#include <assimp/TinyFormatter.h>
+#include <assimp/ai_assert.h>
+#include <assimp/XmlParser.h>
+
+#include <map>
+
+namespace Assimp {
+
+class ZipArchiveIOSystem;
+
+// ------------------------------------------------------------------------------------------
+/** Parser helper class for the Collada loader.
+     *
+     *  Does all the XML reading and builds internal data structures from it,
+     *  but leaves the resolving of all the references to the loader.
+     */
+class ColladaParser {
+    friend class ColladaLoader;
+
+    /** Converts a path read from a collada file to the usual representation */
+    static void UriDecodePath(aiString &ss);
+
+protected:
+    /** Map for generic metadata as aiString */
+    typedef std::map<std::string, aiString> StringMetaData;
+
+    /** Constructor from XML file */
+    ColladaParser(IOSystem *pIOHandler, const std::string &pFile);
+
+    /** Destructor */
+    ~ColladaParser();
+
+    /** Attempts to read the ZAE manifest and returns the DAE to open */
+    static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
+
+    /** Reads the contents of the file */
+    void ReadContents(XmlNode &node);
+
+    /** Reads the structure of the file */
+    void ReadStructure(XmlNode &node);
+
+    /** Reads asset information such as coordinate system information and legal blah */
+    void ReadAssetInfo(XmlNode &node);
+
+    /** Reads contributor information such as author and legal blah */
+    void ReadContributorInfo(XmlNode &node);
+
+    /** Reads generic metadata into provided map and renames keys for Assimp */
+    void ReadMetaDataItem(XmlNode &node, StringMetaData &metadata);
+
+    /** Reads the animation library */
+    void ReadAnimationLibrary(XmlNode &node);
+
+    /** Reads the animation clip library */
+    void ReadAnimationClipLibrary(XmlNode &node);
+
+    /** Unwrap controllers dependency hierarchy */
+    void PostProcessControllers();
+
+    /** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */
+    void PostProcessRootAnimations();
+
+    /** Reads an animation into the given parent structure */
+    void ReadAnimation(XmlNode &node, Collada::Animation *pParent);
+
+    /** Reads an animation sampler into the given anim channel */
+    void ReadAnimationSampler(XmlNode &node, Collada::AnimationChannel &pChannel);
+
+    /** Reads the skeleton controller library */
+    void ReadControllerLibrary(XmlNode &node);
+
+    /** Reads a controller into the given mesh structure */
+    void ReadController(XmlNode &node, Collada::Controller &pController);
+
+    /** Reads the joint definitions for the given controller */
+    void ReadControllerJoints(XmlNode &node, Collada::Controller &pController);
+
+    /** Reads the joint weights for the given controller */
+    void ReadControllerWeights(XmlNode &node, Collada::Controller &pController);
+
+    /** Reads the image library contents */
+    void ReadImageLibrary(XmlNode &node);
+
+    /** Reads an image entry into the given image */
+    void ReadImage(XmlNode &node, Collada::Image &pImage);
+
+    /** Reads the material library */
+    void ReadMaterialLibrary(XmlNode &node);
+
+    /** Reads a material entry into the given material */
+    void ReadMaterial(XmlNode &node, Collada::Material &pMaterial);
+
+    /** Reads the camera library */
+    void ReadCameraLibrary(XmlNode &node);
+
+    /** Reads a camera entry into the given camera */
+    void ReadCamera(XmlNode &node, Collada::Camera &pCamera);
+
+    /** Reads the light library */
+    void ReadLightLibrary(XmlNode &node);
+
+    /** Reads a light entry into the given light */
+    void ReadLight(XmlNode &node, Collada::Light &pLight);
+
+    /** Reads the effect library */
+    void ReadEffectLibrary(XmlNode &node);
+
+    /** Reads an effect entry into the given effect*/
+    void ReadEffect(XmlNode &node, Collada::Effect &pEffect);
+
+    /** Reads an COMMON effect profile */
+    void ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect);
+
+    /** Read sampler properties */
+    void ReadSamplerProperties(XmlNode &node, Collada::Sampler &pSampler);
+
+    /** Reads an effect entry containing a color or a texture defining that color */
+    void ReadEffectColor(XmlNode &node, aiColor4D &pColor, Collada::Sampler &pSampler);
+
+    /** Reads an effect entry containing a float */
+    void ReadEffectFloat(XmlNode &node, ai_real &pFloat);
+
+    /** Reads an effect parameter specification of any kind */
+    void ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam);
+
+    /** Reads the geometry library contents */
+    void ReadGeometryLibrary(XmlNode &node);
+
+    /** Reads a geometry from the geometry library. */
+    void ReadGeometry(XmlNode &node, Collada::Mesh &pMesh);
+
+    /** Reads a mesh from the geometry library */
+    void ReadMesh(XmlNode &node, Collada::Mesh &pMesh);
+
+    /** Reads a source element - a combination of raw data and an accessor defining
+         * things that should not be redefinable. Yes, that's another rant.
+         */
+    void ReadSource(XmlNode &node);
+
+    /** Reads a data array holding a number of elements, and stores it in the global library.
+         * Currently supported are array of floats and arrays of strings.
+         */
+    void ReadDataArray(XmlNode &node);
+
+    /** Reads an accessor and stores it in the global library under the given ID -
+         * accessors use the ID of the parent <source> element
+         */
+    void ReadAccessor(XmlNode &node, const std::string &pID);
+
+    /** Reads input declarations of per-vertex mesh data into the given mesh */
+    void ReadVertexData(XmlNode &node, Collada::Mesh &pMesh);
+
+    /** Reads input declarations of per-index mesh data into the given mesh */
+    void ReadIndexData(XmlNode &node, Collada::Mesh &pMesh);
+
+    /** Reads a single input channel element and stores it in the given array, if valid */
+    void ReadInputChannel(XmlNode &node, std::vector<Collada::InputChannel> &poChannels);
+
+    /** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
+    size_t ReadPrimitives(XmlNode &node, Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
+            size_t pNumPrimitives, const std::vector<size_t> &pVCount, Collada::PrimitiveType pPrimType);
+
+    /** Copies the data for a single primitive into the mesh, based on the InputChannels */
+    void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset,
+            Collada::Mesh &pMesh, std::vector<Collada::InputChannel> &pPerIndexChannels,
+            size_t currentPrimitive, const std::vector<size_t> &indices);
+
+    /** Reads one triangle of a tristrip into the mesh */
+    void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh &pMesh,
+            std::vector<Collada::InputChannel> &pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t> &indices);
+
+    /** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
+    void ExtractDataObjectFromChannel(const Collada::InputChannel &pInput, size_t pLocalIndex, Collada::Mesh &pMesh);
+
+    /** Reads the library of node hierarchies and scene parts */
+    void ReadSceneLibrary(XmlNode &node);
+
+    /** Reads a scene node's contents including children and stores it in the given node */
+    void ReadSceneNode(XmlNode &node, Collada::Node *pNode);
+
+    /** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */
+    void ReadNodeTransformation(XmlNode &node, Collada::Node *pNode, Collada::TransformType pType);
+
+    /** Reads a mesh reference in a node and adds it to the node's mesh list */
+    void ReadNodeGeometry(XmlNode &node, Collada::Node *pNode);
+
+    /** Reads the collada scene */
+    void ReadScene(XmlNode &node);
+
+    // Processes bind_vertex_input and bind elements
+    void ReadMaterialVertexInputBinding(XmlNode &node, Collada::SemanticMappingTable &tbl);
+
+    /** Reads embedded textures from a ZAE archive*/
+    void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
+
+protected:
+    /** Calculates the resulting transformation from all the given transform steps */
+    aiMatrix4x4 CalculateResultTransform(const std::vector<Collada::Transform> &pTransforms) const;
+
+    /** Determines the input data type for the given semantic string */
+    Collada::InputType GetTypeForSemantic(const std::string &pSemantic);
+
+    /** Finds the item in the given library by its reference, throws if not found */
+    template <typename Type>
+    const Type &ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const;
+
+protected:
+    // Filename, for a verbose error message
+    std::string mFileName;
+
+    // XML reader, member for everyday use
+    XmlParser mXmlParser;
+
+    /** All data arrays found in the file by ID. Might be referred to by actually
+         everyone. Collada, you are a steaming pile of indirection. */
+    using DataLibrary = std::map<std::string, Collada::Data> ;
+    DataLibrary mDataLibrary;
+
+    /** Same for accessors which define how the data in a data array is accessed. */
+    using AccessorLibrary = std::map<std::string, Collada::Accessor> ;
+    AccessorLibrary mAccessorLibrary;
+
+    /** Mesh library: mesh by ID */
+    using MeshLibrary = std::map<std::string, Collada::Mesh *>;
+    MeshLibrary mMeshLibrary;
+
+    /** node library: root node of the hierarchy part by ID */
+    using NodeLibrary = std::map<std::string, Collada::Node *>;
+    NodeLibrary mNodeLibrary;
+
+    /** Image library: stores texture properties by ID */
+    using ImageLibrary = std::map<std::string, Collada::Image> ;
+    ImageLibrary mImageLibrary;
+
+    /** Effect library: surface attributes by ID */
+    using EffectLibrary = std::map<std::string, Collada::Effect> ;
+    EffectLibrary mEffectLibrary;
+
+    /** Material library: surface material by ID */
+    using MaterialLibrary = std::map<std::string, Collada::Material> ;
+    MaterialLibrary mMaterialLibrary;
+
+    /** Light library: surface light by ID */
+    using LightLibrary = std::map<std::string, Collada::Light> ;
+    LightLibrary mLightLibrary;
+
+    /** Camera library: surface material by ID */
+    using CameraLibrary = std::map<std::string, Collada::Camera> ;
+    CameraLibrary mCameraLibrary;
+
+    /** Controller library: joint controllers by ID */
+    using ControllerLibrary = std::map<std::string, Collada::Controller> ;
+    ControllerLibrary mControllerLibrary;
+
+    /** Animation library: animation references by ID */
+    using AnimationLibrary = std::map<std::string, Collada::Animation *> ;
+    AnimationLibrary mAnimationLibrary;
+
+    /** Animation clip library: clip animation references by ID */
+    using AnimationClipLibrary = std::vector<std::pair<std::string, std::vector<std::string>>> ;
+    AnimationClipLibrary mAnimationClipLibrary;
+
+    /** Pointer to the root node. Don't delete, it just points to one of
+         the nodes in the node library. */
+    Collada::Node *mRootNode;
+
+    /** Root animation container */
+    Collada::Animation mAnims;
+
+    /** Size unit: how large compared to a meter */
+    ai_real mUnitSize;
+
+    /** Which is the up vector */
+    enum { UP_X,
+        UP_Y,
+        UP_Z } mUpDirection;
+
+    /** Asset metadata (global for scene) */
+    StringMetaData mAssetMetaData;
+
+    /** Collada file format version */
+    Collada::FormatVersion mFormat;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Finds the item in the given library by its reference, throws if not found
+template <typename Type>
+const Type &ColladaParser::ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const {
+    typename std::map<std::string, Type>::const_iterator it = pLibrary.find(pURL);
+    if (it == pLibrary.end()) {
+        throw DeadlyImportError("Unable to resolve library reference \"", pURL, "\".");
+    }
+    return it->second;
+}
+
+} // end of namespace Assimp
+
+#endif // AI_COLLADAPARSER_H_INC

+ 2 - 2
Engine/lib/assimp/code/DXF/DXFHelper.h → Engine/lib/assimp/code/AssetLib/DXF/DXFHelper.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -135,7 +135,7 @@ public:
                 for(;splitter->length() && splitter->at(0) != '}'; splitter++, cnt++);
 
                 splitter++;
-                ASSIMP_LOG_DEBUG((Formatter::format("DXF: skipped over control group ("),cnt," lines)"));
+                ASSIMP_LOG_VERBOSE_DEBUG("DXF: skipped over control group (",cnt," lines)");
             }
         } catch(std::logic_error&) {
             ai_assert(!splitter);

+ 24 - 30
Engine/lib/assimp/code/DXF/DXFLoader.cpp → Engine/lib/assimp/code/AssetLib/DXF/DXFLoader.cpp

@@ -3,9 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -48,8 +46,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>
@@ -63,11 +61,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 
 // AutoCAD Binary DXF<CR><LF><SUB><NULL>
-const std::string AI_DXF_BINARY_IDENT = std::string("AutoCAD Binary DXF\r\n\x1a\0");
-const size_t AI_DXF_BINARY_IDENT_LEN = 24u;
+static constexpr char AI_DXF_BINARY_IDENT[] = "AutoCAD Binary DXF\r\n\x1a";
+static constexpr size_t AI_DXF_BINARY_IDENT_LEN = sizeof AI_DXF_BINARY_IDENT;
 
 // default vertex color that all uncolored vertices will receive
-const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f));
+static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f));
 
 // color indices for DXF - 16 are supported, the table is
 // taken directly from the DXF spec.
@@ -125,18 +123,9 @@ DXFImporter::~DXFImporter() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool checkSig ) const {
-    const std::string& extension = GetExtension( filename );
-    if ( extension == desc.mFileExtensions ) {
-        return true;
-    }
-
-    if ( extension.empty() || checkSig ) {
-        const char *pTokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" };
-        return BaseImporter::SearchFileHeaderForToken(pIOHandler, filename, pTokens, 4, 32 );
-    }
-
-    return false;
+bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
+    static const char *tokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" };
+    return SearchFileHeaderForToken(pIOHandler, filename, tokens, AI_COUNT_OF(tokens), 32);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -152,14 +141,14 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene,
 
     // Check whether we can read the file
     if( file.get() == nullptr ) {
-        throw DeadlyImportError( "Failed to open DXF file " + filename + "");
+        throw DeadlyImportError( "Failed to open DXF file ", filename, "");
     }
 
     // Check whether this is a binary DXF file - we can't read binary DXF files :-(
-    char buff[AI_DXF_BINARY_IDENT_LEN+1] = {0};
+    char buff[AI_DXF_BINARY_IDENT_LEN] = {0};
     file->Read(buff,AI_DXF_BINARY_IDENT_LEN,1);
 
-    if (0 == strncmp(AI_DXF_BINARY_IDENT.c_str(),buff,AI_DXF_BINARY_IDENT_LEN)) {
+    if (0 == memcmp(AI_DXF_BINARY_IDENT,buff,AI_DXF_BINARY_IDENT_LEN)) {
         throw DeadlyImportError("DXF: Binary files are not supported at the moment");
     }
 
@@ -202,7 +191,7 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene,
 
         // comments
         else if (reader.Is(999)) {
-            ASSIMP_LOG_INFO_F("DXF Comment: ", reader.Value());
+            ASSIMP_LOG_INFO("DXF Comment: ", reader.Value());
         }
 
         // don't read past the official EOF sign
@@ -241,7 +230,7 @@ void DXFImporter::ConvertMeshes(aiScene* pScene, DXF::FileData& output) {
             }
         }
 
-        ASSIMP_LOG_DEBUG_F("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount);
+        ASSIMP_LOG_VERBOSE_DEBUG("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount);
     }
 
     if (! output.blocks.size()  ) {
@@ -372,7 +361,7 @@ void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& bloc
         // first check if the referenced blocks exists ...
         const DXF::BlockMap::const_iterator it = blocks_by_name.find(insert.name);
         if (it == blocks_by_name.end()) {
-            ASSIMP_LOG_ERROR_F("DXF: Failed to resolve block reference: ", insert.name,"; skipping" );
+            ASSIMP_LOG_ERROR("DXF: Failed to resolve block reference: ", insert.name,"; skipping" );
             continue;
         }
 
@@ -380,6 +369,11 @@ void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& bloc
         const DXF::Block& bl_src = *(*it).second;
 
         for (std::shared_ptr<const DXF::PolyLine> pl_in : bl_src.lines) {
+            if (!pl_in) {
+                ASSIMP_LOG_ERROR("DXF: PolyLine instance is nullptr, skipping.");
+                continue;
+            }
+            
             std::shared_ptr<DXF::PolyLine> pl_out = std::shared_ptr<DXF::PolyLine>(new DXF::PolyLine(*pl_in));
 
             if (bl_src.base.Length() || insert.scale.x!=1.f || insert.scale.y!=1.f || insert.scale.z!=1.f || insert.angle || insert.pos.Length()) {
@@ -473,7 +467,7 @@ void DXFImporter::ParseBlocks(DXF::LineReader& reader, DXF::FileData& output) {
         ++reader;
     }
 
-    ASSIMP_LOG_DEBUG_F("DXF: got ", output.blocks.size()," entries in BLOCKS" );
+    ASSIMP_LOG_VERBOSE_DEBUG("DXF: got ", output.blocks.size()," entries in BLOCKS" );
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -549,7 +543,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output)
         ++reader;
     }
 
-    ASSIMP_LOG_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), 
+    ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(),
         " inserted blocks in ENTITIES" );
 }
 
@@ -654,7 +648,7 @@ void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output)
     //}
 
     if (vguess && line.positions.size() != vguess) {
-        ASSIMP_LOG_WARN_F("DXF: unexpected vertex count in polymesh: ",
+        ASSIMP_LOG_WARN("DXF: unexpected vertex count in polymesh: ",
             line.positions.size(),", expected ", vguess );
     }
 
@@ -670,7 +664,7 @@ void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output)
         // to set the 71 and 72 fields, respectively, to valid values.
         // So just fire a warning.
         if (iguess && line.counts.size() != iguess) {
-            ASSIMP_LOG_WARN_F( "DXF: unexpected face count in polymesh: ", line.counts.size(),", expected ", iguess );
+            ASSIMP_LOG_WARN( "DXF: unexpected face count in polymesh: ", line.counts.size(),", expected ", iguess );
         }
     }
     else if (!line.indices.size() && !line.counts.size()) {

+ 8 - 11
Engine/lib/assimp/code/DXF/DXFLoader.h → Engine/lib/assimp/code/AssetLib/DXF/DXFLoader.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file  DXFLoader.h
  *  @brief Declaration of the .dxf importer class.
  */
+#pragma once
 #ifndef AI_DXFLOADER_H_INCLUDED
 #define AI_DXFLOADER_H_INCLUDED
 
@@ -59,39 +59,36 @@ namespace DXF {
     struct Block;
     struct InsertBlock;
 
-    typedef std::map<std::string, const DXF::Block*> BlockMap;
+    using BlockMap = std::map<std::string, const DXF::Block*>;
 }
 
 // ---------------------------------------------------------------------------
-/** 
+/**
  *  @brief  DXF importer implementation.
  */
 class DXFImporter : public BaseImporter {
 public:
     DXFImporter();
-    ~DXFImporter();
+    ~DXFImporter() override;
 
     // -------------------------------------------------------------------
     /** 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 checkSig) const override;
 
 protected:
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details*/
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
     // -------------------------------------------------------------------
     /** 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) override;
 
 private:
-
     // -----------------------------------------------------
     void SkipSection(DXF::LineReader& reader);
 

+ 78 - 93
Engine/lib/assimp/code/FBX/FBXAnimation.cpp → Engine/lib/assimp/code/AssetLib/FBX/FBXAnimation.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -47,10 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
 
-#include "FBXParser.h"
 #include "FBXDocument.h"
-#include "FBXImporter.h"
 #include "FBXDocumentUtil.h"
+#include "FBXImporter.h"
+#include "FBXParser.h"
 
 namespace Assimp {
 namespace FBX {
@@ -58,65 +58,60 @@ namespace FBX {
 using namespace Util;
 
 // ------------------------------------------------------------------------------------------------
-AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/)
-: Object(id, element, name)
-{
-    const Scope& sc = GetRequiredScope(element);
-    const Element& KeyTime = GetRequiredElement(sc,"KeyTime");
-    const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat");
+AnimationCurve::AnimationCurve(uint64_t id, const Element &element, const std::string &name, const Document & /*doc*/) :
+        Object(id, element, name) {
+    const Scope &sc = GetRequiredScope(element);
+    const Element &KeyTime = GetRequiredElement(sc, "KeyTime");
+    const Element &KeyValueFloat = GetRequiredElement(sc, "KeyValueFloat");
 
     ParseVectorDataArray(keys, KeyTime);
     ParseVectorDataArray(values, KeyValueFloat);
 
-    if(keys.size() != values.size()) {
-        DOMError("the number of key times does not match the number of keyframe values",&KeyTime);
+    if (keys.size() != values.size()) {
+        DOMError("the number of key times does not match the number of keyframe values", &KeyTime);
     }
 
     // check if the key times are well-ordered
-    if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less<KeyTimeList::value_type>())) {
-        DOMError("the keyframes are not in ascending order",&KeyTime);
+    if (!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less<KeyTimeList::value_type>())) {
+        DOMError("the keyframes are not in ascending order", &KeyTime);
     }
 
-    const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"];
-    if(KeyAttrDataFloat) {
+    const Element *KeyAttrDataFloat = sc["KeyAttrDataFloat"];
+    if (KeyAttrDataFloat) {
         ParseVectorDataArray(attributes, *KeyAttrDataFloat);
     }
 
-    const Element* KeyAttrFlags = sc["KeyAttrFlags"];
-    if(KeyAttrFlags) {
+    const Element *KeyAttrFlags = sc["KeyAttrFlags"];
+    if (KeyAttrFlags) {
         ParseVectorDataArray(flags, *KeyAttrFlags);
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationCurve::~AnimationCurve()
-{
+AnimationCurve::~AnimationCurve() {
     // empty
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, 
-        const Document& doc, const char* const * target_prop_whitelist /*= NULL*/, 
-        size_t whitelist_size /*= 0*/)
-: Object(id, element, name)
-, target()
-, doc(doc)
-{
-    const Scope& sc = GetRequiredScope(element);
+AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, const std::string &name,
+        const Document &doc, const char *const *target_prop_whitelist /*= nullptr*/,
+        size_t whitelist_size /*= 0*/) :
+        Object(id, element, name), target(), doc(doc) {
+    const Scope &sc = GetRequiredScope(element);
 
     // find target node
-    const char* whitelist[] = {"Model","NodeAttribute","Deformer"};
-    const std::vector<const Connection*>& conns = doc.GetConnectionsBySourceSequenced(ID(),whitelist,3);
+    const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
+    const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
 
-    for(const Connection* con : conns) {
+    for (const Connection *con : conns) {
 
         // link should go for a property
         if (!con->PropertyName().length()) {
             continue;
         }
 
-        if(target_prop_whitelist) {
-            const char* const s = con->PropertyName().c_str();
+        if (target_prop_whitelist) {
+            const char *const s = con->PropertyName().c_str();
             bool ok = false;
             for (size_t i = 0; i < whitelist_size; ++i) {
                 if (!strcmp(s, target_prop_whitelist[i])) {
@@ -130,16 +125,14 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, cons
             }
         }
 
-        const Object* const ob = con->DestinationObject();
-        if(!ob) {
-            DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element);
+        const Object *const ob = con->DestinationObject();
+        if (!ob) {
+            DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring", &element);
             continue;
         }
 
-        // XXX support constraints as DOM class
-        //ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob));
         target = ob;
-        if(!target) {
+        if (!target) {
             continue;
         }
 
@@ -147,42 +140,40 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, cons
         break;
     }
 
-    if(!target) {
-        DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element);
+    if (!target) {
+        DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode", &element);
     }
 
-    props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false);
+    props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationCurveNode::~AnimationCurveNode()
-{
+AnimationCurveNode::~AnimationCurveNode() {
     // empty
 }
 
 // ------------------------------------------------------------------------------------------------
-const AnimationCurveMap& AnimationCurveNode::Curves() const
-{
-    if ( curves.empty() ) {
+const AnimationCurveMap &AnimationCurveNode::Curves() const {
+    if (curves.empty()) {
         // resolve attached animation curves
-        const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
+        const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
 
-        for(const Connection* con : conns) {
+        for (const Connection *con : conns) {
 
             // link should go for a property
             if (!con->PropertyName().length()) {
                 continue;
             }
 
-            const Object* const ob = con->SourceObject();
-            if(!ob) {
-                DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element);
+            const Object *const ob = con->SourceObject();
+            if (nullptr == ob) {
+                DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring", &element);
                 continue;
             }
 
-            const AnimationCurve* const anim = dynamic_cast<const AnimationCurve*>(ob);
-            if(!anim) {
-                DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element);
+            const AnimationCurve *const anim = dynamic_cast<const AnimationCurve *>(ob);
+            if (nullptr == anim) {
+                DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve", &element);
                 continue;
             }
 
@@ -194,53 +185,49 @@ const AnimationCurveMap& AnimationCurveNode::Curves() const
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc)
-: Object(id, element, name)
-, doc(doc)
-{
-    const Scope& sc = GetRequiredScope(element);
+AnimationLayer::AnimationLayer(uint64_t id, const Element &element, const std::string &name, const Document &doc) :
+        Object(id, element, name), doc(doc) {
+    const Scope &sc = GetRequiredScope(element);
 
     // note: the props table here bears little importance and is usually absent
-    props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true);
+    props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationLayer::~AnimationLayer()
-{
+AnimationLayer::~AnimationLayer() {
     // empty
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/,
-    size_t whitelist_size /*= 0*/) const
-{
+AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist /*= nullptr*/,
+        size_t whitelist_size /*= 0*/) const {
     AnimationCurveNodeList nodes;
 
     // resolve attached animation nodes
-    const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurveNode");
+    const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurveNode");
     nodes.reserve(conns.size());
 
-    for(const Connection* con : conns) {
+    for (const Connection *con : conns) {
 
         // link should not go to a property
         if (con->PropertyName().length()) {
             continue;
         }
 
-        const Object* const ob = con->SourceObject();
-        if(!ob) {
-            DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element);
+        const Object *const ob = con->SourceObject();
+        if (!ob) {
+            DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring", &element);
             continue;
         }
 
-        const AnimationCurveNode* const anim = dynamic_cast<const AnimationCurveNode*>(ob);
-        if(!anim) {
-            DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode",&element);
+        const AnimationCurveNode *const anim = dynamic_cast<const AnimationCurveNode *>(ob);
+        if (!anim) {
+            DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode", &element);
             continue;
         }
 
-        if(target_prop_whitelist) {
-            const char* s = anim->TargetProperty().c_str();
+        if (target_prop_whitelist) {
+            const char *s = anim->TargetProperty().c_str();
             bool ok = false;
             for (size_t i = 0; i < whitelist_size; ++i) {
                 if (!strcmp(s, target_prop_whitelist[i])) {
@@ -248,7 +235,7 @@ AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whi
                     break;
                 }
             }
-            if(!ok) {
+            if (!ok) {
                 continue;
             }
         }
@@ -259,34 +246,33 @@ AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whi
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc)
-: Object(id, element, name)
-{
-    const Scope& sc = GetRequiredScope(element);
+AnimationStack::AnimationStack(uint64_t id, const Element &element, const std::string &name, const Document &doc) :
+        Object(id, element, name) {
+    const Scope &sc = GetRequiredScope(element);
 
     // note: we don't currently use any of these properties so we shouldn't bother if it is missing
-    props = GetPropertyTable(doc,"AnimationStack.FbxAnimStack",element,sc, true);
+    props = GetPropertyTable(doc, "AnimationStack.FbxAnimStack", element, sc, true);
 
     // resolve attached animation layers
-    const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationLayer");
+    const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer");
     layers.reserve(conns.size());
 
-    for(const Connection* con : conns) {
+    for (const Connection *con : conns) {
 
         // link should not go to a property
         if (con->PropertyName().length()) {
             continue;
         }
 
-        const Object* const ob = con->SourceObject();
-        if(!ob) {
-            DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element);
+        const Object *const ob = con->SourceObject();
+        if (!ob) {
+            DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring", &element);
             continue;
         }
 
-        const AnimationLayer* const anim = dynamic_cast<const AnimationLayer*>(ob);
-        if(!anim) {
-            DOMWarning("source object for ->AnimationStack link is not an AnimationLayer",&element);
+        const AnimationLayer *const anim = dynamic_cast<const AnimationLayer *>(ob);
+        if (!anim) {
+            DOMWarning("source object for ->AnimationStack link is not an AnimationLayer", &element);
             continue;
         }
         layers.push_back(anim);
@@ -294,12 +280,11 @@ AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::s
 }
 
 // ------------------------------------------------------------------------------------------------
-AnimationStack::~AnimationStack()
-{
+AnimationStack::~AnimationStack() {
     // empty
 }
 
-} //!FBX
-} //!Assimp
+} // namespace FBX
+} // namespace Assimp
 
 #endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 25 - 6
Engine/lib/assimp/code/FBX/FBXBinaryTokenizer.cpp → Engine/lib/assimp/code/AssetLib/FBX/FBXBinaryTokenizer.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -53,6 +53,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <stdint.h>
 #include <assimp/Exceptional.h>
 #include <assimp/ByteSwapper.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/StringUtils.h>
 
 namespace Assimp {
 namespace FBX {
@@ -125,7 +127,7 @@ namespace {
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset)
 {
-    throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
+    throw DeadlyImportError("FBX-Tokenize", Util::GetOffsetText(offset), message);
 }
 
 
@@ -373,6 +375,11 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 
     // now come the individual properties
     const char* begin_cursor = cursor;
+
+    if ((begin_cursor + prop_length) > end) {
+        TokenizeError("property length out of bounds reading length ", input, cursor);
+    }
+
     for (unsigned int i = 0; i < prop_count; ++i) {
         ReadData(sbeg, send, input, cursor, begin_cursor + prop_length);
 
@@ -426,7 +433,8 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 // TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
 void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
 {
-    ai_assert(input);
+	ai_assert(input);
+	ASSIMP_LOG_DEBUG("Tokenizing binary FBX file");
 
     if(length < 0x1b) {
         TokenizeError("file is too short",0);
@@ -451,12 +459,23 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
 	/*Result ignored*/ ReadByte(input, cursor, input + length);
 	/*Result ignored*/ ReadByte(input, cursor, input + length);
 	const uint32_t version = ReadWord(input, cursor, input + length);
+	ASSIMP_LOG_DEBUG("FBX version: ", version);
 	const bool is64bits = version >= 7500;
     const char *end = input + length;
-    while (cursor < end ) {
-		if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
-            break;
+    try
+    {
+        while (cursor < end ) {
+		    if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
+                break;
+            }
+        }
+    }
+    catch (const DeadlyImportError& e)
+    {
+        if (!is64bits && (length > std::numeric_limits<std::uint32_t>::max())) {
+            throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (", ai_to_string(version), ") of the FBX format. (", e.what(), ")");
         }
+        throw;
     }
 }
 

+ 37 - 34
Engine/lib/assimp/code/FBX/FBXCommon.h → Engine/lib/assimp/code/AssetLib/FBX/FBXCommon.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2019, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -48,39 +48,42 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 
 namespace Assimp {
-namespace FBX
-{
-    const std::string NULL_RECORD = { // 13 null bytes
-        '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'
-    }; // who knows why
-    const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings
-    const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
-    const int64_t SECOND = 46186158000; // FBX's kTime unit
-
-    // rotation order. We'll probably use EulerXYZ for everything
-    enum RotOrder {
-        RotOrder_EulerXYZ = 0,
-        RotOrder_EulerXZY,
-        RotOrder_EulerYZX,
-        RotOrder_EulerYXZ,
-        RotOrder_EulerZXY,
-        RotOrder_EulerZYX,
-
-        RotOrder_SphericXYZ,
-
-        RotOrder_MAX // end-of-enum sentinel
-    };
-
-    // transformation inheritance method. Most of the time RSrs
-    enum TransformInheritance {
-        TransformInheritance_RrSs = 0,
-        TransformInheritance_RSrs,
-        TransformInheritance_Rrs,
-
-        TransformInheritance_MAX // end-of-enum sentinel
-    };
-}
-}
+namespace FBX {
+
+const std::string NULL_RECORD = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
+    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+    '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
+}; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)
+const std::string SEPARATOR = { '\x00', '\x01' }; // for use inside strings
+const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
+const int64_t SECOND = 46186158000; // FBX's kTime unit
+
+// rotation order. We'll probably use EulerXYZ for everything
+enum RotOrder {
+    RotOrder_EulerXYZ = 0,
+    RotOrder_EulerXZY,
+    RotOrder_EulerYZX,
+    RotOrder_EulerYXZ,
+    RotOrder_EulerZXY,
+    RotOrder_EulerZYX,
+
+    RotOrder_SphericXYZ,
+
+    RotOrder_MAX // end-of-enum sentinel
+};
+
+// transformation inheritance method. Most of the time RSrs
+enum TransformInheritance {
+    TransformInheritance_RrSs = 0,
+    TransformInheritance_RSrs,
+    TransformInheritance_Rrs,
+
+    TransformInheritance_MAX // end-of-enum sentinel
+};
+
+} // namespace FBX
+} // namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 #endif // AI_FBXCOMMON_H_INC

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio