Browse Source

Rewrite FBX Importer to convert directly to Godot scene format

Co-authored-by: Gordon MacPherson <[email protected]>
Co-authored-by: Andrea Catania <[email protected]>
Co-authored-by: K. S. Ernest (iFire) Lee <[email protected]>

This is a complete rewrite of the importer. It will give more deterministic behaviour and has been sponsored by IMVU inc, over 1 year has gone into the development of this importer to remove the burden of the FBX SDK.
This was my project for 1 entire year and I really enjoyed the opportunity to add to Godot.

Along the road of implementing fixes we implemented fbx pivots, animations and inheritance type handling, which in most cases works properly.
We have implemented animation and mesh skinning too this should work out of the box, if there are issues let us know.
It's designed so that you can expand this with ease, and fix bugs easily too.
It can import from Autodesk Maya and import into Godot, with pivots.
There are bits we could polish but for now this is good enough.

Additional fixes made before upstreaming:
- fixed memory leaks
- ensure consistent ordering on mac linux and windows for fbx tree. (very important for material import to be deterministic)
- disabled incorrect warnings for fbx_material
- added compatibility code for /RootNode/ so compat is not broken
- Optimise FBX - directly import triangles
- remove debug messages
- add messages for mesh id, mesh re-import is sometimes slow and we need to know what mesh is being worked on
- Document no longer uses unordered maps
- Removed some usages of &GetRequiredToken replaced with safe *GetRequiredToken() function
- Added parser debugging
- Added ERR_FAIL_CONDS for unsupported mesh formats (we can add these later super easy to do now)
- Add memory debugging for the Tokens and the TokenParser to make it safe
- Add memory initialisation to mesh.cpp surface_tool.h and mesh.h
- Initialise boolean flags properly
- Refactored to correct naming for the fbx_mesh_data.h so you know what data you are working on
- Disabled corruption caused by the FIXME:
- Fixed document reading indexes and index_to_direct vs indexes mode
- Fixed UV1 and UV2 coordinates
- Fixed importer failing to import version 7700 files
- Replaced memory handling in the FBX Document with pointers, before it was dereferencing invalid memory.
- Fixed typed properties
- Improved Document API
- Fixed bug with ProcessDOMConnection() not working with the bool flag set to true.
- Fixed FBX skinning not deforming for more than one single mesh
- Fixed FBX skeleton mapping and skin mapping not being applied properly (now retrieved from document skin list)
- Fixed set_bone_pose being used in final version()
- Fixed material properties exceeding 1.0.
- FBX Document parser revamped to use safe memory practices, and with graceful error messages.
- ScopePtr, TokenPtr and various internal types have been fleshed out to use proper typedefs across the codebase.
- Fixed memory leaks caused by token cleanup failing (now explicit cleanup step, no shared_ptr, etc)
- Fixed bug with PropertyTable not reading all properties and not cleaning up properly.
- Fixed smoothing groups not working
- Fixed normal duplications
- Fixed duplication check for pre-existing coordinates.
- Fixed performance of vertex lookup in large meshes being slow, using lookup table separate to the data for indexing, this reduces import time from 10 minutes of bistro down to 30 seconds.
- Fixed includes requiring absolute path in headers and cpp files using CPPPath.

Bugs/Features wish list:
- locator bones
- quat anim key interpolation (most fbx maya files have euler rotations from blender and maya, nobody uses this)
- some rigs skins scale up when SSC enabled inconsistently per bone
- some skins can disappear entirely
- material mapping needs expanded, but this will be done for 4.0 as it requires rewrite.

Workarounds for issues found until we patch them:
- mesh -> clear skin can resolve most of the bugs above.
- locators can be worked around by removing them before exporting your rig.
- some material properties wont always import, this is okay to override in the material properties.

**If you are having issues or need support fear not!**
Please provide minimal rigs which can reproduce issues as we can't spend a lot of time investigating each rig. We need a small example which breaks and we can then sort the problem. In some cases this is not possible so its okay to privately send models to us via IRC or a ticket and we can provide an email address, we won't reveal or disclose privately sent rig files to any companies, or to companies I work for, they will not be shared, only tested and bugs will be drawn up from the conclusions. Also include identifying information about what you did and how it didn't work. Please file each file separately in a bug report, unless the problem is the same.
This was sponsored by IMVU, and a special thanks to everyone who supported this project.

Signed-off-by: Gordon MacPherson <[email protected]>
Gordon MacPherson 4 years ago
parent
commit
8197a611fa
100 changed files with 13849 additions and 15110 deletions
  1. 7 5
      COPYRIGHT.txt
  2. 0 94
      modules/assimp/SCsub
  3. 0 1517
      modules/assimp/editor_scene_importer_assimp.cpp
  4. 0 262
      modules/assimp/godot_update_assimp.sh
  5. 0 447
      modules/assimp/import_utils.h
  6. 15 0
      modules/fbx/SCsub
  7. 0 0
      modules/fbx/config.py
  8. 46 0
      modules/fbx/data/fbx_anim_container.h
  9. 93 0
      modules/fbx/data/fbx_bone.cpp
  10. 95 0
      modules/fbx/data/fbx_bone.h
  11. 487 0
      modules/fbx/data/fbx_material.cpp
  12. 233 0
      modules/fbx/data/fbx_material.h
  13. 1366 0
      modules/fbx/data/fbx_mesh_data.cpp
  14. 175 0
      modules/fbx/data/fbx_mesh_data.h
  15. 63 0
      modules/fbx/data/fbx_node.h
  16. 124 0
      modules/fbx/data/fbx_skeleton.cpp
  17. 53 0
      modules/fbx/data/fbx_skeleton.h
  18. 55 78
      modules/fbx/data/import_state.h
  19. 52 0
      modules/fbx/data/model_abstraction.h
  20. 257 0
      modules/fbx/data/pivot_transform.cpp
  21. 112 0
      modules/fbx/data/pivot_transform.h
  22. 1428 0
      modules/fbx/editor_scene_importer_fbx.cpp
  23. 64 81
      modules/fbx/editor_scene_importer_fbx.h
  24. 283 0
      modules/fbx/fbx_parser/ByteSwapper.h
  25. 2 2
      modules/fbx/fbx_parser/CREDITS
  26. 294 0
      modules/fbx/fbx_parser/FBXAnimation.cpp
  27. 470 0
      modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
  28. 110 0
      modules/fbx/fbx_parser/FBXCommon.h
  29. 279 0
      modules/fbx/fbx_parser/FBXDeformer.cpp
  30. 699 0
      modules/fbx/fbx_parser/FBXDocument.cpp
  31. 1311 0
      modules/fbx/fbx_parser/FBXDocument.h
  32. 172 0
      modules/fbx/fbx_parser/FBXDocumentUtil.cpp
  33. 142 0
      modules/fbx/fbx_parser/FBXDocumentUtil.h
  34. 174 0
      modules/fbx/fbx_parser/FBXImportSettings.h
  35. 406 0
      modules/fbx/fbx_parser/FBXMaterial.cpp
  36. 451 0
      modules/fbx/fbx_parser/FBXMeshGeometry.cpp
  37. 263 0
      modules/fbx/fbx_parser/FBXMeshGeometry.h
  38. 181 0
      modules/fbx/fbx_parser/FBXModel.cpp
  39. 184 0
      modules/fbx/fbx_parser/FBXNodeAttribute.cpp
  40. 111 0
      modules/fbx/fbx_parser/FBXParseTools.h
  41. 1295 0
      modules/fbx/fbx_parser/FBXParser.cpp
  42. 264 0
      modules/fbx/fbx_parser/FBXParser.h
  43. 105 0
      modules/fbx/fbx_parser/FBXPose.cpp
  44. 238 0
      modules/fbx/fbx_parser/FBXProperties.cpp
  45. 222 0
      modules/fbx/fbx_parser/FBXProperties.h
  46. 249 0
      modules/fbx/fbx_parser/FBXTokenizer.cpp
  47. 204 0
      modules/fbx/fbx_parser/FBXTokenizer.h
  48. 221 0
      modules/fbx/fbx_parser/FBXUtil.cpp
  49. 45 51
      modules/fbx/fbx_parser/FBXUtil.h
  50. 2 4
      modules/fbx/fbx_parser/LICENSE
  51. 197 0
      modules/fbx/readme.md
  52. 7 7
      modules/fbx/register_types.cpp
  53. 5 5
      modules/fbx/register_types.h
  54. 152 0
      modules/fbx/tools/import_utils.cpp
  55. 386 0
      modules/fbx/tools/import_utils.h
  56. 0 18
      thirdparty/README.md
  57. 0 156
      thirdparty/assimp/code/CApi/AssimpCExport.cpp
  58. 0 136
      thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp
  59. 0 99
      thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h
  60. 0 695
      thirdparty/assimp/code/Common/Assimp.cpp
  61. 0 656
      thirdparty/assimp/code/Common/BaseImporter.cpp
  62. 0 107
      thirdparty/assimp/code/Common/BaseProcess.cpp
  63. 0 290
      thirdparty/assimp/code/Common/BaseProcess.h
  64. 0 155
      thirdparty/assimp/code/Common/Bitmap.cpp
  65. 0 88
      thirdparty/assimp/code/Common/CreateAnimMesh.cpp
  66. 0 154
      thirdparty/assimp/code/Common/DefaultIOStream.cpp
  67. 0 216
      thirdparty/assimp/code/Common/DefaultIOSystem.cpp
  68. 0 418
      thirdparty/assimp/code/Common/DefaultLogger.cpp
  69. 0 65
      thirdparty/assimp/code/Common/DefaultProgressHandler.h
  70. 0 636
      thirdparty/assimp/code/Common/Exporter.cpp
  71. 0 107
      thirdparty/assimp/code/Common/FileLogStream.h
  72. 0 345
      thirdparty/assimp/code/Common/FileSystemFilter.h
  73. 0 102
      thirdparty/assimp/code/Common/IFF.h
  74. 0 1174
      thirdparty/assimp/code/Common/Importer.cpp
  75. 0 247
      thirdparty/assimp/code/Common/Importer.h
  76. 0 377
      thirdparty/assimp/code/Common/ImporterRegistry.cpp
  77. 0 229
      thirdparty/assimp/code/Common/PolyTools.h
  78. 0 265
      thirdparty/assimp/code/Common/PostStepRegistry.cpp
  79. 0 113
      thirdparty/assimp/code/Common/RemoveComments.cpp
  80. 0 168
      thirdparty/assimp/code/Common/SGSpatialSort.cpp
  81. 0 1350
      thirdparty/assimp/code/Common/SceneCombiner.cpp
  82. 0 261
      thirdparty/assimp/code/Common/ScenePreprocessor.cpp
  83. 0 125
      thirdparty/assimp/code/Common/ScenePreprocessor.h
  84. 0 105
      thirdparty/assimp/code/Common/ScenePrivate.h
  85. 0 270
      thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp
  86. 0 342
      thirdparty/assimp/code/Common/SpatialSort.cpp
  87. 0 407
      thirdparty/assimp/code/Common/SplitByBoneCountProcess.cpp
  88. 0 111
      thirdparty/assimp/code/Common/SplitByBoneCountProcess.h
  89. 0 507
      thirdparty/assimp/code/Common/StandardShapes.cpp
  90. 0 101
      thirdparty/assimp/code/Common/StdOStreamLogStream.h
  91. 0 589
      thirdparty/assimp/code/Common/Subdivision.cpp
  92. 0 248
      thirdparty/assimp/code/Common/TargetAnimation.cpp
  93. 0 183
      thirdparty/assimp/code/Common/TargetAnimation.h
  94. 0 181
      thirdparty/assimp/code/Common/Version.cpp
  95. 0 134
      thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp
  96. 0 117
      thirdparty/assimp/code/Common/VertexTriangleAdjacency.h
  97. 0 95
      thirdparty/assimp/code/Common/Win32DebugLogStream.h
  98. 0 196
      thirdparty/assimp/code/Common/assbin_chunks.h
  99. 0 140
      thirdparty/assimp/code/Common/scene.cpp
  100. 0 79
      thirdparty/assimp/code/Common/simd.cpp

+ 7 - 5
COPYRIGHT.txt

@@ -55,6 +55,13 @@ Comment: Godot Engine logo
 Copyright: 2017, Andrea Calabró
 Copyright: 2017, Andrea Calabró
 License: CC-BY-3.0
 License: CC-BY-3.0
 
 
+Files: ./modules/fbx/fbx_parser/
+Comment: Open Asset Import Library (assimp)
+Copyright: 2006-2016, assimp team
+ 2007-2020, Juan Linietsky, Ariel Manzur.
+ 2014-2020, Godot Engine contributors.
+License: BSD-3-clause and Expat
+
 Files: ./platform/android/java/aidl/com/android/vending/billing/IInAppBillingService.aidl
 Files: ./platform/android/java/aidl/com/android/vending/billing/IInAppBillingService.aidl
  ./platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml
  ./platform/android/java/res/layout/status_bar_ongoing_event_progress_bar.xml
  ./platform/android/java/src/com/google/android/vending/expansion/downloader/*
  ./platform/android/java/src/com/google/android/vending/expansion/downloader/*
@@ -110,11 +117,6 @@ Copyright: 2007, Starbreeze Studios
  2014-2020, Godot Engine contributors.
  2014-2020, Godot Engine contributors.
 License: Expat and Zlib
 License: Expat and Zlib
 
 
-Files: ./thirdparty/assimp/
-Comment: Open Asset Import Library (assimp)
-Copyright: 2006-2016, assimp team
-License: BSD-3-clause
-
 Files: ./thirdparty/bullet/
 Files: ./thirdparty/bullet/
 Comment: Bullet Continuous Collision Detection and Physics Library
 Comment: Bullet Continuous Collision Detection and Physics Library
 Copyright: 2003-2013, Erwin Coumans
 Copyright: 2003-2013, Erwin Coumans

+ 0 - 94
modules/assimp/SCsub

@@ -1,94 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_assimp = env_modules.Clone()
-
-# Force bundled version for now, there's no released version of Assimp with
-# support for ArmaturePopulate which we use from their master branch.
-if True:  # env['builtin_assimp']:
-    thirdparty_dir = "#thirdparty/assimp"
-
-    env_assimp.Prepend(CPPPATH=["#thirdparty/assimp"])
-    env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/code"])
-    env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/include"])
-
-    # env_assimp.Append(CPPDEFINES=['ASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_SINGLETHREADED"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_BOOST_WORKAROUND"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OWN_ZLIB"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_EXPORT"])
-
-    # Importers we don't need
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3D_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3DS_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3MF_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AC_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AMF_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASE_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASSBIN_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_B3D_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BLEND_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BVH_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_C4D_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COB_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COLLADA_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_CSM_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_DXF_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF2_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_HMP_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IFC_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRR_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRRMESH_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWO_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWS_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_M3D_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD2_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD3_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDC_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDL_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MMD_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MS3D_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NDO_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NFF_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OBJ_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OFF_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OGRE_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OPENGEX_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_PLY_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3BSP_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3D_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_RAW_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SIB_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SMD_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STEP_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STL_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_TERRAGEN_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X3D_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_XGL_IMPORTER"])
-    env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X_IMPORTER"])
-
-    if env["platform"] == "windows":
-        env_assimp.Append(CPPDEFINES=["PLATFORM_WINDOWS"])
-        env_assimp.Append(CPPDEFINES=[("PLATFORM", "WINDOWS")])
-    elif env["platform"] == "x11":
-        env_assimp.Append(CPPDEFINES=["PLATFORM_LINUX"])
-        env_assimp.Append(CPPDEFINES=[("PLATFORM", "LINUX")])
-    elif env["platform"] == "osx":
-        env_assimp.Append(CPPDEFINES=["PLATFORM_DARWIN"])
-        env_assimp.Append(CPPDEFINES=[("PLATFORM", "DARWIN")])
-
-    env_thirdparty = env_assimp.Clone()
-    env_thirdparty.disable_warnings()
-    env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/CApi/*.cpp"))
-    env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Common/*.cpp"))
-    env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/PostProcessing/*.cpp"))
-    env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Material/*.cpp"))
-    env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/FBX/*.cpp"))
-
-# Godot's own source files
-env_assimp.add_source_files(env.modules_sources, "*.cpp")

+ 0 - 1517
modules/assimp/editor_scene_importer_assimp.cpp

@@ -1,1517 +0,0 @@
-/*************************************************************************/
-/*  editor_scene_importer_assimp.cpp                                     */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "editor_scene_importer_assimp.h"
-#include "core/io/image_loader.h"
-#include "editor/import/resource_importer_scene.h"
-#include "import_utils.h"
-#include "scene/3d/camera.h"
-#include "scene/3d/light.h"
-#include "scene/3d/mesh_instance.h"
-#include "scene/main/node.h"
-#include "scene/resources/material.h"
-#include "scene/resources/surface_tool.h"
-
-#include <assimp/matrix4x4.h>
-#include <assimp/postprocess.h>
-#include <assimp/scene.h>
-#include <assimp/Importer.hpp>
-#include <assimp/LogStream.hpp>
-
-// move into assimp
-aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) {
-	for (unsigned int mesh_id = 0; mesh_id < scene->mNumMeshes; ++mesh_id) {
-		aiMesh *mesh = scene->mMeshes[mesh_id];
-
-		// iterate over all the bones on the mesh for this node only!
-		for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
-
-			aiBone *bone = mesh->mBones[boneIndex];
-			if (bone->mName == bone_name) {
-				printf("matched bone by name: %s\n", bone->mName.C_Str());
-				return bone;
-			}
-		}
-	}
-
-	return NULL;
-}
-
-void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const {
-
-	const String import_setting_string = "filesystem/import/open_asset_import/";
-
-	Map<String, ImportFormat> import_format;
-	{
-		Vector<String> exts;
-		exts.push_back("fbx");
-		ImportFormat import = { exts, true };
-		import_format.insert("fbx", import);
-	}
-	for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) {
-		_register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions,
-				E->get().is_default);
-	}
-}
-
-void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string,
-		const Vector<String> &exts, List<String> *r_extensions,
-		const bool p_enabled) const {
-	const String use_generic = "use_" + generic;
-	_GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true);
-	if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) {
-		for (int32_t i = 0; i < exts.size(); i++) {
-			r_extensions->push_back(exts[i]);
-		}
-	}
-}
-
-uint32_t EditorSceneImporterAssimp::get_import_flags() const {
-	return IMPORT_SCENE;
-}
-
-void EditorSceneImporterAssimp::_bind_methods() {
-}
-
-Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
-		List<String> *r_missing_deps, Error *r_err) {
-	Assimp::Importer importer;
-	importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true);
-	// Cannot remove pivot points because the static mesh will be in the wrong place
-	importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
-	int32_t max_bone_weights = 4;
-	//if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) {
-	//	const int eight_bones = 8;
-	//	importer.SetPropertyBool(AI_CONFIG_PP_LBW_MAX_WEIGHTS, eight_bones);
-	//	max_bone_weights = eight_bones;
-	//}
-
-	importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT);
-
-	//importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f);
-	int32_t post_process_Steps = aiProcess_CalcTangentSpace |
-								 aiProcess_GlobalScale |
-								 // imports models and listens to their file scale for CM to M conversions
-								 //aiProcess_FlipUVs |
-								 aiProcess_FlipWindingOrder |
-								 // very important for culling so that it is done in the correct order.
-								 //aiProcess_DropNormals |
-								 //aiProcess_GenSmoothNormals |
-								 //aiProcess_JoinIdenticalVertices |
-								 aiProcess_ImproveCacheLocality |
-								 //aiProcess_RemoveRedundantMaterials | // Causes a crash
-								 //aiProcess_SplitLargeMeshes |
-								 aiProcess_Triangulate |
-								 aiProcess_GenUVCoords |
-								 //aiProcess_FindDegenerates |
-								 //aiProcess_SortByPType |
-								 // aiProcess_FindInvalidData |
-								 aiProcess_TransformUVCoords |
-								 aiProcess_FindInstances |
-								 //aiProcess_FixInfacingNormals |
-								 //aiProcess_ValidateDataStructure |
-								 aiProcess_OptimizeMeshes |
-								 aiProcess_PopulateArmatureData |
-								 //aiProcess_OptimizeGraph |
-								 //aiProcess_Debone |
-								 // aiProcess_EmbedTextures |
-								 //aiProcess_SplitByBoneCount |
-								 0;
-	String g_path = ProjectSettings::get_singleton()->globalize_path(p_path);
-	aiScene *scene = (aiScene *)importer.ReadFile(g_path.utf8().ptr(), post_process_Steps);
-
-	ERR_FAIL_COND_V_MSG(scene == NULL, NULL, String("Open Asset Import failed to open: ") + String(importer.GetErrorString()));
-
-	return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights);
-}
-
-template <class T>
-struct EditorSceneImporterAssetImportInterpolate {
-
-	T lerp(const T &a, const T &b, float c) const {
-
-		return a + (b - a) * c;
-	}
-
-	T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
-
-		float t2 = t * t;
-		float t3 = t2 * t;
-
-		return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
-							  (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
-	}
-
-	T bezier(T start, T control_1, T control_2, T end, float t) {
-		/* Formula from Wikipedia article on Bezier curves. */
-		real_t omt = (1.0 - t);
-		real_t omt2 = omt * omt;
-		real_t omt3 = omt2 * omt;
-		real_t t2 = t * t;
-		real_t t3 = t2 * t;
-
-		return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
-	}
-};
-
-//thank you for existing, partial specialization
-template <>
-struct EditorSceneImporterAssetImportInterpolate<Quat> {
-
-	Quat lerp(const Quat &a, const Quat &b, float c) const {
-		ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quat(), "The quaternion \"a\" must be normalized.");
-		ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quat(), "The quaternion \"b\" must be normalized.");
-
-		return a.slerp(b, c).normalized();
-	}
-
-	Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) {
-		ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quat(), "The quaternion \"p1\" must be normalized.");
-		ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quat(), "The quaternion \"p2\" must be normalized.");
-
-		return p1.slerp(p2, c).normalized();
-	}
-
-	Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) {
-		ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quat(), "The start quaternion must be normalized.");
-		ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quat(), "The end quaternion must be normalized.");
-
-		return start.slerp(end, t).normalized();
-	}
-};
-
-template <class T>
-T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time,
-		AssetImportAnimation::Interpolation p_interp) {
-	//could use binary search, worth it?
-	int idx = -1;
-	for (int i = 0; i < p_times.size(); i++) {
-		if (p_times[i] > p_time)
-			break;
-		idx++;
-	}
-
-	EditorSceneImporterAssetImportInterpolate<T> interp;
-
-	switch (p_interp) {
-		case AssetImportAnimation::INTERP_LINEAR: {
-
-			if (idx == -1) {
-				return p_values[0];
-			} else if (idx >= p_times.size() - 1) {
-				return p_values[p_times.size() - 1];
-			}
-
-			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
-			return interp.lerp(p_values[idx], p_values[idx + 1], c);
-
-		} break;
-		case AssetImportAnimation::INTERP_STEP: {
-
-			if (idx == -1) {
-				return p_values[0];
-			} else if (idx >= p_times.size() - 1) {
-				return p_values[p_times.size() - 1];
-			}
-
-			return p_values[idx];
-
-		} break;
-		case AssetImportAnimation::INTERP_CATMULLROMSPLINE: {
-
-			if (idx == -1) {
-				return p_values[1];
-			} else if (idx >= p_times.size() - 1) {
-				return p_values[1 + p_times.size() - 1];
-			}
-
-			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
-			return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c);
-
-		} break;
-		case AssetImportAnimation::INTERP_CUBIC_SPLINE: {
-
-			if (idx == -1) {
-				return p_values[1];
-			} else if (idx >= p_times.size() - 1) {
-				return p_values[(p_times.size() - 1) * 3 + 1];
-			}
-
-			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
-			T from = p_values[idx * 3 + 1];
-			T c1 = from + p_values[idx * 3 + 2];
-			T to = p_values[idx * 3 + 4];
-			T c2 = to + p_values[idx * 3 + 3];
-
-			return interp.bezier(from, c1, c2, to, c);
-
-		} break;
-	}
-
-	ERR_FAIL_V(p_values[0]);
-}
-
-aiBone *EditorSceneImporterAssimp::get_bone_from_stack(ImportState &state, aiString name) {
-	List<aiBone *>::Element *iter;
-	aiBone *bone = NULL;
-	for (iter = state.bone_stack.front(); iter; iter = iter->next()) {
-		bone = (aiBone *)iter->get();
-
-		if (bone && bone->mName == name) {
-			state.bone_stack.erase(bone);
-			return bone;
-		}
-	}
-
-	return NULL;
-}
-
-Spatial *
-EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps,
-		const int32_t p_max_bone_weights) {
-	ERR_FAIL_COND_V(scene == NULL, NULL);
-
-	ImportState state;
-	state.path = p_path;
-	state.assimp_scene = scene;
-	state.max_bone_weights = p_max_bone_weights;
-	state.animation_player = NULL;
-	state.import_flags = p_flags;
-
-	// populate light map
-	for (unsigned int l = 0; l < scene->mNumLights; l++) {
-
-		aiLight *ai_light = scene->mLights[l];
-		ERR_CONTINUE(ai_light == NULL);
-		state.light_cache[AssimpUtils::get_assimp_string(ai_light->mName)] = l;
-	}
-
-	// fill camera cache
-	for (unsigned int c = 0; c < scene->mNumCameras; c++) {
-		aiCamera *ai_camera = scene->mCameras[c];
-		ERR_CONTINUE(ai_camera == NULL);
-		state.camera_cache[AssimpUtils::get_assimp_string(ai_camera->mName)] = c;
-	}
-
-	if (scene->mRootNode) {
-		state.nodes.push_back(scene->mRootNode);
-
-		// make flat node tree - in order to make processing deterministic
-		for (unsigned int i = 0; i < scene->mRootNode->mNumChildren; i++) {
-			_generate_node(state, scene->mRootNode->mChildren[i]);
-		}
-
-		RegenerateBoneStack(state);
-
-		Node *last_valid_parent = NULL;
-
-		List<const aiNode *>::Element *iter;
-		for (iter = state.nodes.front(); iter; iter = iter->next()) {
-			const aiNode *element_assimp_node = iter->get();
-			const aiNode *parent_assimp_node = element_assimp_node->mParent;
-
-			String node_name = AssimpUtils::get_assimp_string(element_assimp_node->mName);
-			//print_verbose("node: " + node_name);
-
-			Spatial *spatial = NULL;
-			Transform transform = AssimpUtils::assimp_matrix_transform(element_assimp_node->mTransformation);
-
-			// retrieve this node bone
-			aiBone *bone = get_bone_from_stack(state, element_assimp_node->mName);
-
-			if (state.light_cache.has(node_name)) {
-				spatial = create_light(state, node_name, transform);
-			} else if (state.camera_cache.has(node_name)) {
-				spatial = create_camera(state, node_name, transform);
-			} else if (state.armature_nodes.find(element_assimp_node)) {
-				// create skeleton
-				print_verbose("Making skeleton: " + node_name);
-				Skeleton *skeleton = memnew(Skeleton);
-				spatial = skeleton;
-				if (!state.armature_skeletons.has(element_assimp_node)) {
-					state.armature_skeletons.insert(element_assimp_node, skeleton);
-				}
-			} else if (bone != NULL) {
-				continue;
-			} else {
-				spatial = memnew(Spatial);
-			}
-
-			ERR_CONTINUE_MSG(spatial == NULL, "FBX Import - are we out of ram?");
-			// we on purpose set the transform and name after creating the node.
-
-			spatial->set_name(node_name);
-			spatial->set_global_transform(transform);
-
-			// first element is root
-			if (iter == state.nodes.front()) {
-				state.root = spatial;
-			}
-
-			// flat node map parent lookup tool
-			state.flat_node_map.insert(element_assimp_node, spatial);
-
-			Map<const aiNode *, Spatial *>::Element *parent_lookup = state.flat_node_map.find(parent_assimp_node);
-
-			// note: this always fails on the root node :) keep that in mind this is by design
-			if (parent_lookup) {
-				Spatial *parent_node = parent_lookup->value();
-
-				ERR_FAIL_COND_V_MSG(parent_node == NULL, state.root,
-						"Parent node invalid even though lookup successful, out of ram?")
-
-				if (spatial != state.root) {
-					parent_node->add_child(spatial);
-					spatial->set_owner(state.root);
-				} else {
-					// required - think about it root never has a parent yet is valid, anything else without a parent is not valid.
-				}
-			} else if (spatial != state.root) {
-				// if the ainode is not in the tree
-				// parent it to the last good parent found
-				if (last_valid_parent) {
-					last_valid_parent->add_child(spatial);
-					spatial->set_owner(state.root);
-				} else {
-					// this is a serious error?
-					memdelete(spatial);
-				}
-			}
-
-			// update last valid parent
-			last_valid_parent = spatial;
-		}
-		print_verbose("node counts: " + itos(state.nodes.size()));
-
-		// make clean bone stack
-		RegenerateBoneStack(state);
-
-		print_verbose("generating godot bone data");
-
-		print_verbose("Godot bone stack count: " + itos(state.bone_stack.size()));
-
-		// This is a list of bones, duplicates are from other meshes and must be dealt with properly
-		for (List<aiBone *>::Element *element = state.bone_stack.front(); element; element = element->next()) {
-			aiBone *bone = element->get();
-
-			ERR_CONTINUE_MSG(!bone, "invalid bone read from assimp?");
-
-			// Utilities for armature lookup - for now only FBX makes these
-			aiNode *armature_for_bone = bone->mArmature;
-
-			// Utilities for bone node lookup - for now only FBX makes these
-			aiNode *bone_node = bone->mNode;
-			aiNode *parent_node = bone_node->mParent;
-
-			String bone_name = AssimpUtils::get_anim_string_from_assimp(bone->mName);
-			ERR_CONTINUE_MSG(armature_for_bone == NULL, "Armature for bone invalid: " + bone_name);
-			Skeleton *skeleton = state.armature_skeletons[armature_for_bone];
-
-			state.skeleton_bone_map[bone] = skeleton;
-
-			if (bone_name.empty()) {
-				bone_name = "untitled_bone_name";
-				WARN_PRINT("Untitled bone name detected... report with file please");
-			}
-
-			// todo: this is where skin support goes
-			if (skeleton && skeleton->find_bone(bone_name) == -1) {
-				print_verbose("[Godot Glue] Imported bone" + bone_name);
-				int boneIdx = skeleton->get_bone_count();
-
-				Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation);
-				skeleton->add_bone(bone_name);
-				skeleton->set_bone_rest(boneIdx, pform);
-				skeleton->set_bone_pose(boneIdx, pform);
-
-				if (parent_node != NULL) {
-					int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName));
-					int current_bone_id = boneIdx;
-					skeleton->set_bone_parent(current_bone_id, parent_bone_id);
-				}
-			}
-		}
-
-		print_verbose("generating mesh phase from skeletal mesh");
-
-		List<Spatial *> cleanup_template_nodes;
-
-		for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
-			const aiNode *assimp_node = key_value_pair->key();
-			Spatial *mesh_template = key_value_pair->value();
-
-			ERR_CONTINUE(assimp_node == NULL);
-			ERR_CONTINUE(mesh_template == NULL);
-
-			Node *parent_node = mesh_template->get_parent();
-
-			if (mesh_template == state.root) {
-				continue;
-			}
-
-			if (parent_node == NULL) {
-				print_error("Found invalid parent node!");
-				continue; // root node
-			}
-
-			String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
-			Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
-
-			if (assimp_node->mNumMeshes > 0) {
-				MeshInstance *mesh = create_mesh(state, assimp_node, node_name, parent_node, node_transform);
-				if (mesh) {
-
-					parent_node->remove_child(mesh_template);
-
-					// re-parent children
-					List<Node *> children;
-					// re-parent all children to new node
-					// note: since get_child_count will change during execution we must build a list first to be safe.
-					for (int childId = 0; childId < mesh_template->get_child_count(); childId++) {
-						// get child
-						Node *child = mesh_template->get_child(childId);
-						children.push_back(child);
-					}
-
-					for (List<Node *>::Element *element = children.front(); element; element = element->next()) {
-						// reparent the children to the real mesh node.
-						mesh_template->remove_child(element->get());
-						mesh->add_child(element->get());
-						element->get()->set_owner(state.root);
-					}
-
-					// update mesh in list so that each mesh node is available
-					// this makes the template unavailable which is the desired behaviour
-					state.flat_node_map[assimp_node] = mesh;
-
-					cleanup_template_nodes.push_back(mesh_template);
-
-					// clean up this list we don't need it
-					children.clear();
-				}
-			}
-		}
-
-		for (List<Spatial *>::Element *element = cleanup_template_nodes.front(); element; element = element->next()) {
-			if (element->get()) {
-				memdelete(element->get());
-			}
-		}
-	}
-
-	if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) {
-
-		state.animation_player = memnew(AnimationPlayer);
-		state.root->add_child(state.animation_player);
-		state.animation_player->set_owner(state.root);
-
-		for (uint32_t i = 0; i < scene->mNumAnimations; i++) {
-			_import_animation(state, i, p_bake_fps);
-		}
-	}
-
-	//
-	// Cleanup operations
-	//
-
-	state.mesh_cache.clear();
-	state.material_cache.clear();
-	state.light_cache.clear();
-	state.camera_cache.clear();
-	state.assimp_node_map.clear();
-	state.path_to_image_cache.clear();
-	state.nodes.clear();
-	state.flat_node_map.clear();
-	state.armature_skeletons.clear();
-	state.bone_stack.clear();
-	return state.root;
-}
-
-void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
-		int anim_fps, Ref<Animation> animation, float ticks_per_second,
-		Skeleton *skeleton, const NodePath &node_path,
-		const String &node_name, aiBone *track_bone) {
-	const aiNodeAnim *assimp_track = assimp_anim->mChannels[track_id];
-	//make transform track
-	int track_idx = animation->get_track_count();
-	animation->add_track(Animation::TYPE_TRANSFORM);
-	animation->track_set_path(track_idx, node_path);
-	//first determine animation length
-
-	float increment = 1.0 / float(anim_fps);
-	float time = 0.0;
-
-	bool last = false;
-
-	Vector<Vector3> pos_values;
-	Vector<float> pos_times;
-	Vector<Vector3> scale_values;
-	Vector<float> scale_times;
-	Vector<Quat> rot_values;
-	Vector<float> rot_times;
-
-	for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) {
-		aiVector3D pos = assimp_track->mPositionKeys[p].mValue;
-		pos_values.push_back(Vector3(pos.x, pos.y, pos.z));
-		pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second);
-	}
-
-	for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) {
-		aiQuaternion quat = assimp_track->mRotationKeys[r].mValue;
-		rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized());
-		rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second);
-	}
-
-	for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) {
-		aiVector3D scale = assimp_track->mScalingKeys[sc].mValue;
-		scale_values.push_back(Vector3(scale.x, scale.y, scale.z));
-		scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second);
-	}
-
-	while (true) {
-		Vector3 pos;
-		Quat rot;
-		Vector3 scale(1, 1, 1);
-
-		if (pos_values.size()) {
-			pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR);
-		}
-
-		if (rot_values.size()) {
-			rot = _interpolate_track<Quat>(rot_times, rot_values, time,
-					AssetImportAnimation::INTERP_LINEAR)
-						  .normalized();
-		}
-
-		if (scale_values.size()) {
-			scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR);
-		}
-
-		if (skeleton) {
-			int skeleton_bone = skeleton->find_bone(node_name);
-
-			if (skeleton_bone >= 0 && track_bone) {
-
-				Transform xform;
-				xform.basis.set_quat_scale(rot, scale);
-				xform.origin = pos;
-
-				xform = skeleton->get_bone_pose(skeleton_bone).inverse() * xform;
-
-				rot = xform.basis.get_rotation_quat();
-				rot.normalize();
-				scale = xform.basis.get_scale();
-				pos = xform.origin;
-			} else {
-				ERR_FAIL_MSG("Skeleton bone lookup failed for skeleton: " + skeleton->get_name());
-			}
-		}
-
-		animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR);
-		animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
-
-		if (last) { //done this way so a key is always inserted past the end (for proper interpolation)
-			break;
-		}
-		time += increment;
-		if (time >= animation->get_length()) {
-			last = true;
-		}
-	}
-}
-
-// I really do not like this but need to figure out a better way of removing it later.
-Node *EditorSceneImporterAssimp::get_node_by_name(ImportState &state, String name) {
-	for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
-		const aiNode *assimp_node = key_value_pair->key();
-		Spatial *node = key_value_pair->value();
-
-		String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
-		if (name == node_name && node) {
-			return node;
-		}
-	}
-	return NULL;
-}
-
-/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
-void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state) {
-
-	state.bone_stack.clear();
-	// build bone stack list
-	for (unsigned int mesh_id = 0; mesh_id < state.assimp_scene->mNumMeshes; ++mesh_id) {
-		aiMesh *mesh = state.assimp_scene->mMeshes[mesh_id];
-
-		// iterate over all the bones on the mesh for this node only!
-		for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
-			aiBone *bone = mesh->mBones[boneIndex];
-
-			// doubtful this is required right now but best to check
-			if (!state.bone_stack.find(bone)) {
-				//print_verbose("[assimp] bone stack added: " + String(bone->mName.C_Str()) );
-				state.bone_stack.push_back(bone);
-			}
-		}
-	}
-}
-
-/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
-void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state, aiMesh *mesh) {
-	state.bone_stack.clear();
-	// iterate over all the bones on the mesh for this node only!
-	for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
-		aiBone *bone = mesh->mBones[boneIndex];
-		if (state.bone_stack.find(bone) == NULL) {
-			state.bone_stack.push_back(bone);
-		}
-	}
-}
-
-// animation tracks are per bone
-
-void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) {
-
-	ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations);
-
-	const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index];
-	String name = AssimpUtils::get_anim_string_from_assimp(anim->mName);
-	if (name == String()) {
-		name = "Animation " + itos(p_animation_index + 1);
-	}
-	print_verbose("import animation: " + name);
-	float ticks_per_second = anim->mTicksPerSecond;
-
-	if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) {
-		int32_t time_mode = 0;
-		state.assimp_scene->mMetaData->Get("TimeMode", time_mode);
-		ticks_per_second = AssimpUtils::get_fbx_fps(time_mode, state.assimp_scene);
-	}
-
-	//?
-	//if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) {
-	//	ticks_per_second = 1000.0f;
-	//}
-
-	if (Math::is_equal_approx(ticks_per_second, 0.0f)) {
-		ticks_per_second = 25.0f;
-	}
-
-	Ref<Animation> animation;
-	animation.instance();
-	animation->set_name(name);
-	animation->set_length(anim->mDuration / ticks_per_second);
-
-	if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
-		animation->set_loop(true);
-	}
-
-	// generate bone stack for animation import
-	RegenerateBoneStack(state);
-
-	//regular tracks
-	for (size_t i = 0; i < anim->mNumChannels; i++) {
-		const aiNodeAnim *track = anim->mChannels[i];
-		String node_name = AssimpUtils::get_assimp_string(track->mNodeName);
-		print_verbose("track name import: " + node_name);
-		if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) {
-			continue; //do not bother
-		}
-
-		Skeleton *skeleton = NULL;
-		NodePath node_path;
-		aiBone *bone = NULL;
-
-		// Import skeleton bone animation for this track
-		// Any bone will do, no point in processing more than just what is in the skeleton
-		{
-			bone = get_bone_from_stack(state, track->mNodeName);
-
-			if (bone) {
-				// get skeleton by bone
-				skeleton = state.armature_skeletons[bone->mArmature];
-
-				if (skeleton) {
-					String path = state.root->get_path_to(skeleton);
-					path += ":" + node_name;
-					node_path = path;
-
-					if (node_path != NodePath()) {
-						_insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
-								node_path, node_name, bone);
-					} else {
-						print_error("Failed to find valid node path for animation");
-					}
-				}
-			}
-		}
-
-		// not a bone
-		// note this is flaky it uses node names which is unreliable
-		Node *allocated_node = get_node_by_name(state, node_name);
-		// todo: implement skeleton grabbing for node based animations too :)
-		// check if node exists, if it does then also apply animation track for node and bones above are all handled.
-		// this is now inclusive animation handling so that
-		// we import all the data and do not miss anything.
-		if (allocated_node) {
-			node_path = state.root->get_path_to(allocated_node);
-
-			if (node_path != NodePath()) {
-				_insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
-						node_path, node_name, nullptr);
-			}
-		}
-	}
-
-	//blend shape tracks
-
-	for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) {
-
-		const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i];
-
-		const String prop_name = AssimpUtils::get_assimp_string(anim_mesh->mName);
-		const String mesh_name = prop_name.split("*")[0];
-
-		ERR_CONTINUE(prop_name.split("*").size() != 2);
-
-		Node *item = get_node_by_name(state, mesh_name);
-		ERR_CONTINUE_MSG(!item, "failed to look up node by name");
-		const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(item);
-		ERR_CONTINUE(mesh_instance == NULL);
-
-		String base_path = state.root->get_path_to(mesh_instance);
-
-		Ref<Mesh> mesh = mesh_instance->get_mesh();
-		ERR_CONTINUE(mesh.is_null());
-
-		//add the tracks for this mesh
-		int base_track = animation->get_track_count();
-		for (int j = 0; j < mesh->get_blend_shape_count(); j++) {
-
-			animation->add_track(Animation::TYPE_VALUE);
-			animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j));
-		}
-
-		for (size_t k = 0; k < anim_mesh->mNumKeys; k++) {
-			for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) {
-
-				float t = anim_mesh->mKeys[k].mTime / ticks_per_second;
-				float w = anim_mesh->mKeys[k].mWeights[j];
-
-				animation->track_insert_key(base_track + j, t, w);
-			}
-		}
-	}
-
-	if (animation->get_track_count()) {
-		state.animation_player->add_animation(name, animation);
-	}
-}
-//
-// Mesh Generation from indices ? why do we need so much mesh code
-// [debt needs looked into]
-Ref<Mesh>
-EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
-		const aiNode *assimp_node, Ref<Skin> &skin,
-		Skeleton *&skeleton_assigned) {
-
-	Ref<ArrayMesh> mesh;
-	mesh.instance();
-	bool has_uvs = false;
-	bool compress_vert_data = state.import_flags & IMPORT_USE_COMPRESSION;
-	uint32_t mesh_flags = compress_vert_data ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
-
-	Map<String, uint32_t> morph_mesh_string_lookup;
-
-	for (int i = 0; i < p_surface_indices.size(); i++) {
-		const unsigned int mesh_idx = p_surface_indices[0];
-		const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
-		for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
-			String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
-			if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) {
-				morph_mesh_string_lookup.insert(ai_anim_mesh_name, j);
-				mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
-				if (ai_anim_mesh_name.empty()) {
-					ai_anim_mesh_name = String("morph_") + itos(j);
-				}
-				mesh->add_blend_shape(ai_anim_mesh_name);
-			}
-		}
-	}
-	//
-	// Process Vertex Weights
-	//
-	for (int i = 0; i < p_surface_indices.size(); i++) {
-		const unsigned int mesh_idx = p_surface_indices[i];
-		const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
-
-		Map<uint32_t, Vector<BoneInfo> > vertex_weights;
-
-		if (ai_mesh->mNumBones > 0) {
-			for (size_t b = 0; b < ai_mesh->mNumBones; b++) {
-				aiBone *bone = ai_mesh->mBones[b];
-
-				if (!skeleton_assigned) {
-					print_verbose("Assigned mesh skeleton during mesh creation");
-					skeleton_assigned = state.skeleton_bone_map[bone];
-
-					if (!skin.is_valid()) {
-						print_verbose("Configured new skin");
-						skin.instance();
-					} else {
-						print_verbose("Reusing existing skin!");
-					}
-				}
-				//                skeleton_assigned =
-				String bone_name = AssimpUtils::get_assimp_string(bone->mName);
-				int bone_index = skeleton_assigned->find_bone(bone_name);
-				ERR_CONTINUE(bone_index == -1);
-				for (size_t w = 0; w < bone->mNumWeights; w++) {
-
-					aiVertexWeight ai_weights = bone->mWeights[w];
-
-					BoneInfo bi;
-					uint32_t vertex_index = ai_weights.mVertexId;
-					bi.bone = bone_index;
-					bi.weight = ai_weights.mWeight;
-
-					if (!vertex_weights.has(vertex_index)) {
-						vertex_weights[vertex_index] = Vector<BoneInfo>();
-					}
-
-					vertex_weights[vertex_index].push_back(bi);
-				}
-			}
-		}
-
-		//
-		// Create mesh from data from assimp
-		//
-
-		Ref<SurfaceTool> st;
-		st.instance();
-		st->begin(Mesh::PRIMITIVE_TRIANGLES);
-
-		for (size_t j = 0; j < ai_mesh->mNumVertices; j++) {
-
-			// Get the texture coordinates if they exist
-			if (ai_mesh->HasTextureCoords(0)) {
-				has_uvs = true;
-				st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
-			}
-
-			if (ai_mesh->HasTextureCoords(1)) {
-				has_uvs = true;
-				st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
-			}
-
-			// Assign vertex colors
-			if (ai_mesh->HasVertexColors(0)) {
-				Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b,
-						ai_mesh->mColors[0]->a);
-				st->add_color(color);
-			}
-
-			// Work out normal calculations? - this needs work it doesn't work properly on huestos
-			if (ai_mesh->mNormals != NULL) {
-				const aiVector3D normals = ai_mesh->mNormals[j];
-				const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
-				st->add_normal(godot_normal);
-				if (ai_mesh->HasTangentsAndBitangents()) {
-					const aiVector3D tangents = ai_mesh->mTangents[j];
-					const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z);
-					const aiVector3D bitangent = ai_mesh->mBitangents[j];
-					const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
-					float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
-					st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d));
-				}
-			}
-
-			// We have vertex weights right?
-			if (vertex_weights.has(j)) {
-
-				Vector<BoneInfo> bone_info = vertex_weights[j];
-				Vector<int> bones;
-				bones.resize(bone_info.size());
-				Vector<float> weights;
-				weights.resize(bone_info.size());
-
-				// todo? do we really need to loop over all bones? - assimp may have helper to find all influences on this vertex.
-				for (int k = 0; k < bone_info.size(); k++) {
-					bones.write[k] = bone_info[k].bone;
-					weights.write[k] = bone_info[k].weight;
-				}
-
-				st->add_bones(bones);
-				st->add_weights(weights);
-			}
-
-			// Assign vertex
-			const aiVector3D pos = ai_mesh->mVertices[j];
-
-			// note we must include node offset transform as this is relative to world space not local space.
-			Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z);
-			st->add_vertex(godot_pos);
-		}
-
-		// fire replacement for face handling
-		for (size_t j = 0; j < ai_mesh->mNumFaces; j++) {
-			const aiFace face = ai_mesh->mFaces[j];
-			for (unsigned int k = 0; k < face.mNumIndices; k++) {
-				st->add_index(face.mIndices[k]);
-			}
-		}
-
-		if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) {
-			st->generate_tangents();
-		}
-
-		aiMaterial *ai_material = state.assimp_scene->mMaterials[ai_mesh->mMaterialIndex];
-		Ref<SpatialMaterial> mat;
-		mat.instance();
-
-		int32_t mat_two_sided = 0;
-		if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
-			if (mat_two_sided > 0) {
-				mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
-			} else {
-				mat->set_cull_mode(SpatialMaterial::CULL_BACK);
-			}
-		}
-
-		aiString mat_name;
-		if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) {
-			mat->set_name(AssimpUtils::get_assimp_string(mat_name));
-		}
-
-		// Culling handling for meshes
-
-		// cull all back faces
-		mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
-
-		// Now process materials
-		aiTextureType base_color = aiTextureType_BASE_COLOR;
-		{
-			String filename, path;
-			AssimpImageData image_data;
-
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, base_color, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-
-				// anything transparent must be culled
-				if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
-					mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
-					mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
-					mat->set_cull_mode(
-							SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
-				}
-
-				mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture);
-			}
-		}
-
-		aiTextureType tex_diffuse = aiTextureType_DIFFUSE;
-		{
-			String filename, path;
-			AssimpImageData image_data;
-
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_diffuse, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-
-				// anything transparent must be culled
-				if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
-					mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
-					mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
-					mat->set_cull_mode(
-							SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
-				}
-
-				mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture);
-			}
-
-			aiColor4D clr_diffuse;
-			if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) {
-				if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) {
-					mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
-					mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
-					mat->set_cull_mode(
-							SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
-				}
-				mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
-			}
-		}
-
-		aiTextureType tex_normal = aiTextureType_NORMALS;
-		{
-			String filename, path;
-			Ref<ImageTexture> texture;
-			AssimpImageData image_data;
-
-			// Process texture normal map
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
-				mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
-			} else {
-				aiString texture_path;
-				if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, AI_PROPERTIES, texture_path)) {
-					if (AssimpUtils::CreateAssimpTexture(state, texture_path, filename, path, image_data)) {
-						mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
-						mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
-					}
-				}
-			}
-		}
-
-		aiTextureType tex_normal_camera = aiTextureType_NORMAL_CAMERA;
-		{
-			String filename, path;
-			Ref<ImageTexture> texture;
-			AssimpImageData image_data;
-
-			// Process texture normal map
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal_camera, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
-				mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
-			}
-		}
-
-		aiTextureType tex_emission_color = aiTextureType_EMISSION_COLOR;
-		{
-			String filename, path;
-			Ref<ImageTexture> texture;
-			AssimpImageData image_data;
-
-			// Process texture normal map
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emission_color, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
-				mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
-			}
-		}
-
-		aiTextureType tex_metalness = aiTextureType_METALNESS;
-		{
-			String filename, path;
-			Ref<ImageTexture> texture;
-			AssimpImageData image_data;
-
-			// Process texture normal map
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_metalness, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, image_data.texture);
-			}
-		}
-
-		aiTextureType tex_roughness = aiTextureType_DIFFUSE_ROUGHNESS;
-		{
-			String filename, path;
-			Ref<ImageTexture> texture;
-			AssimpImageData image_data;
-
-			// Process texture normal map
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_roughness, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, image_data.texture);
-			}
-		}
-
-		aiTextureType tex_emissive = aiTextureType_EMISSIVE;
-		{
-			String filename = "";
-			String path = "";
-			Ref<Image> texture;
-			AssimpImageData image_data;
-
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emissive, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
-				mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, image_data.texture);
-			} else {
-				// Process emission textures
-				aiString texture_emissive_path;
-				if (AI_SUCCESS ==
-						ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE, AI_PROPERTIES, texture_emissive_path)) {
-					if (AssimpUtils::CreateAssimpTexture(state, texture_emissive_path, filename, path, image_data)) {
-						mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
-						mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, image_data.texture);
-					}
-				} else {
-					float pbr_emission = 0.0f;
-					if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR, AI_NULL, pbr_emission)) {
-						mat->set_emission(Color(pbr_emission, pbr_emission, pbr_emission, 1.0f));
-					}
-				}
-			}
-		}
-
-		aiTextureType tex_specular = aiTextureType_SPECULAR;
-		{
-			String filename, path;
-			Ref<ImageTexture> texture;
-			AssimpImageData image_data;
-
-			// Process texture normal map
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_specular, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, image_data.texture);
-			}
-		}
-
-		aiTextureType tex_ao_map = aiTextureType_AMBIENT_OCCLUSION;
-		{
-			String filename, path;
-			Ref<ImageTexture> texture;
-			AssimpImageData image_data;
-
-			// Process texture normal map
-			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_ao_map, filename, path, image_data)) {
-				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-				mat->set_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION, true);
-				mat->set_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION, image_data.texture);
-			}
-		}
-
-		Array array_mesh = st->commit_to_arrays();
-		Array morphs;
-		morphs.resize(ai_mesh->mNumAnimMeshes);
-		Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES;
-
-		for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
-
-			String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
-
-			if (ai_anim_mesh_name.empty()) {
-				ai_anim_mesh_name = String("morph_") + itos(j);
-			}
-
-			Array array_copy;
-			array_copy.resize(VisualServer::ARRAY_MAX);
-
-			for (int l = 0; l < VisualServer::ARRAY_MAX; l++) {
-				array_copy[l] = array_mesh[l].duplicate(true);
-			}
-
-			const size_t num_vertices = ai_mesh->mAnimMeshes[j]->mNumVertices;
-			array_copy[Mesh::ARRAY_INDEX] = Variant();
-			if (ai_mesh->mAnimMeshes[j]->HasPositions()) {
-				PoolVector3Array vertices;
-				vertices.resize(num_vertices);
-				for (size_t l = 0; l < num_vertices; l++) {
-					const aiVector3D ai_pos = ai_mesh->mAnimMeshes[j]->mVertices[l];
-					Vector3 position = Vector3(ai_pos.x, ai_pos.y, ai_pos.z);
-					vertices.write()[l] = position;
-				}
-				PoolVector3Array new_vertices = array_copy[VisualServer::ARRAY_VERTEX].duplicate(true);
-				ERR_CONTINUE(vertices.size() != new_vertices.size());
-				for (int32_t l = 0; l < new_vertices.size(); l++) {
-					PoolVector3Array::Write w = new_vertices.write();
-					w[l] = vertices[l];
-				}
-				array_copy[VisualServer::ARRAY_VERTEX] = new_vertices;
-			}
-
-			int32_t color_set = 0;
-			if (ai_mesh->mAnimMeshes[j]->HasVertexColors(color_set)) {
-				PoolColorArray colors;
-				colors.resize(num_vertices);
-				for (size_t l = 0; l < num_vertices; l++) {
-					const aiColor4D ai_color = ai_mesh->mAnimMeshes[j]->mColors[color_set][l];
-					Color color = Color(ai_color.r, ai_color.g, ai_color.b, ai_color.a);
-					colors.write()[l] = color;
-				}
-				PoolColorArray new_colors = array_copy[VisualServer::ARRAY_COLOR].duplicate(true);
-				ERR_CONTINUE(colors.size() != new_colors.size());
-				for (int32_t l = 0; l < colors.size(); l++) {
-					PoolColorArray::Write w = new_colors.write();
-					w[l] = colors[l];
-				}
-				array_copy[VisualServer::ARRAY_COLOR] = new_colors;
-			}
-
-			if (ai_mesh->mAnimMeshes[j]->HasNormals()) {
-				PoolVector3Array normals;
-				normals.resize(num_vertices);
-				for (size_t l = 0; l < num_vertices; l++) {
-					const aiVector3D ai_normal = ai_mesh->mAnimMeshes[j]->mNormals[l];
-					Vector3 normal = Vector3(ai_normal.x, ai_normal.y, ai_normal.z);
-					normals.write()[l] = normal;
-				}
-				PoolVector3Array new_normals = array_copy[VisualServer::ARRAY_NORMAL].duplicate(true);
-				ERR_CONTINUE(normals.size() != new_normals.size());
-				for (int l = 0; l < normals.size(); l++) {
-					PoolVector3Array::Write w = new_normals.write();
-					w[l] = normals[l];
-				}
-				array_copy[VisualServer::ARRAY_NORMAL] = new_normals;
-			}
-
-			if (ai_mesh->mAnimMeshes[j]->HasTangentsAndBitangents()) {
-				PoolColorArray tangents;
-				tangents.resize(num_vertices);
-				PoolColorArray::Write w = tangents.write();
-				for (size_t l = 0; l < num_vertices; l++) {
-					AssimpUtils::calc_tangent_from_mesh(ai_mesh, j, l, l, w);
-				}
-				PoolRealArray new_tangents = array_copy[VisualServer::ARRAY_TANGENT].duplicate(true);
-				ERR_CONTINUE(new_tangents.size() != tangents.size() * 4);
-				for (int32_t l = 0; l < tangents.size(); l++) {
-					new_tangents.write()[l + 0] = tangents[l].r;
-					new_tangents.write()[l + 1] = tangents[l].g;
-					new_tangents.write()[l + 2] = tangents[l].b;
-					new_tangents.write()[l + 3] = tangents[l].a;
-				}
-				array_copy[VisualServer::ARRAY_TANGENT] = new_tangents;
-			}
-
-			morphs[j] = array_copy;
-		}
-		mesh->add_surface_from_arrays(primitive, array_mesh, morphs, mesh_flags);
-		mesh->surface_set_material(i, mat);
-		mesh->surface_set_name(i, AssimpUtils::get_assimp_string(ai_mesh->mName));
-	}
-
-	return mesh;
-}
-
-/**
- * Create a new mesh for the node supplied
- */
-MeshInstance *
-EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform) {
-	/* MESH NODE */
-	Ref<Mesh> mesh;
-	Ref<Skin> skin;
-	// see if we have mesh cache for this.
-	Vector<int> surface_indices;
-
-	RegenerateBoneStack(state);
-
-	// Configure indices
-	for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
-		int mesh_index = assimp_node->mMeshes[i];
-		// create list of mesh indexes
-		surface_indices.push_back(mesh_index);
-	}
-
-	//surface_indices.sort();
-	String mesh_key;
-	for (int i = 0; i < surface_indices.size(); i++) {
-		if (i > 0) {
-			mesh_key += ":";
-		}
-		mesh_key += itos(surface_indices[i]);
-	}
-
-	Skeleton *skeleton = NULL;
-	aiNode *armature = NULL;
-
-	if (!state.mesh_cache.has(mesh_key)) {
-		mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skin, skeleton);
-		state.mesh_cache[mesh_key] = mesh;
-	}
-
-	MeshInstance *mesh_node = memnew(MeshInstance);
-	mesh = state.mesh_cache[mesh_key];
-	mesh_node->set_mesh(mesh);
-
-	// if we have a valid skeleton set it up
-	if (skin.is_valid()) {
-		for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
-			unsigned int mesh_index = assimp_node->mMeshes[i];
-			const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_index];
-
-			// please remember bone id relative to the skin is NOT the mesh relative index.
-			// it is the index relative to the skeleton that is why
-			// we have state.bone_id_map, it allows for duplicate bone id's too :)
-			// hope this makes sense
-
-			int bind_count = 0;
-			for (unsigned int boneId = 0; boneId < ai_mesh->mNumBones; ++boneId) {
-				aiBone *iterBone = ai_mesh->mBones[boneId];
-
-				// used to reparent mesh to the correct armature later on if assigned.
-				if (!armature) {
-					print_verbose("Configured mesh armature, will reparent later to armature");
-					armature = iterBone->mArmature;
-				}
-
-				if (skeleton) {
-					int id = skeleton->find_bone(AssimpUtils::get_assimp_string(iterBone->mName));
-					if (id != -1) {
-						print_verbose("Set bind bone: mesh: " + itos(mesh_index) + " bone index: " + itos(id));
-						Transform t = AssimpUtils::assimp_matrix_transform(iterBone->mOffsetMatrix);
-
-						skin->add_bind(bind_count, t);
-						skin->set_bind_bone(bind_count, id);
-						bind_count++;
-					}
-				}
-			}
-		}
-
-		print_verbose("Finished configuring bind pose for skin mesh");
-	}
-
-	// this code parents all meshes with bones to the armature they are for
-	// GLTF2 specification relies on this and we are enforcing it for FBX.
-	if (armature && state.flat_node_map[armature]) {
-		Node *armature_parent = state.flat_node_map[armature];
-		print_verbose("Parented mesh " + node_name + " to armature " + armature_parent->get_name());
-		// static mesh handling
-		armature_parent->add_child(mesh_node);
-		// transform must be identity
-		mesh_node->set_global_transform(Transform());
-		mesh_node->set_name(node_name);
-		mesh_node->set_owner(state.root);
-	} else {
-		// static mesh handling
-		active_node->add_child(mesh_node);
-		mesh_node->set_global_transform(node_transform);
-		mesh_node->set_name(node_name);
-		mesh_node->set_owner(state.root);
-	}
-
-	if (skeleton) {
-		print_verbose("Attempted to set skeleton path!");
-		mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton));
-		mesh_node->set_skin(skin);
-	}
-
-	return mesh_node;
-}
-
-/**
- * Create a light for the scene
- * Automatically caches lights for lookup later
- */
-Spatial *EditorSceneImporterAssimp::create_light(
-		ImportState &state,
-		const String &node_name,
-		Transform &look_at_transform) {
-	Light *light = NULL;
-	aiLight *assimp_light = state.assimp_scene->mLights[state.light_cache[node_name]];
-	ERR_FAIL_COND_V(!assimp_light, NULL);
-
-	if (assimp_light->mType == aiLightSource_DIRECTIONAL) {
-		light = memnew(DirectionalLight);
-	} else if (assimp_light->mType == aiLightSource_POINT) {
-		light = memnew(OmniLight);
-	} else if (assimp_light->mType == aiLightSource_SPOT) {
-		light = memnew(SpotLight);
-	}
-	ERR_FAIL_COND_V(light == NULL, NULL);
-
-	if (assimp_light->mType != aiLightSource_POINT) {
-		Vector3 pos = Vector3(
-				assimp_light->mPosition.x,
-				assimp_light->mPosition.y,
-				assimp_light->mPosition.z);
-		Vector3 look_at = Vector3(
-				assimp_light->mDirection.y,
-				assimp_light->mDirection.x,
-				assimp_light->mDirection.z)
-								  .normalized();
-		Vector3 up = Vector3(
-				assimp_light->mUp.x,
-				assimp_light->mUp.y,
-				assimp_light->mUp.z);
-
-		look_at_transform.set_look_at(pos, look_at, up);
-	}
-	// properties for light variables should be put here.
-	// not really hugely important yet but we will need them in the future
-
-	light->set_color(
-			Color(assimp_light->mColorDiffuse.r, assimp_light->mColorDiffuse.g, assimp_light->mColorDiffuse.b));
-
-	return light;
-}
-
-/**
- * Create camera for the scene
- */
-Spatial *EditorSceneImporterAssimp::create_camera(
-		ImportState &state,
-		const String &node_name,
-		Transform &look_at_transform) {
-	aiCamera *camera = state.assimp_scene->mCameras[state.camera_cache[node_name]];
-	ERR_FAIL_COND_V(!camera, NULL);
-
-	Camera *camera_node = memnew(Camera);
-	ERR_FAIL_COND_V(!camera_node, NULL);
-	float near = camera->mClipPlaneNear;
-	if (Math::is_equal_approx(near, 0.0f)) {
-		near = 0.1f;
-	}
-	camera_node->set_perspective(Math::rad2deg(camera->mHorizontalFOV) * 2.0f, near, camera->mClipPlaneFar);
-	Vector3 pos = Vector3(camera->mPosition.x, camera->mPosition.y, camera->mPosition.z);
-	Vector3 look_at = Vector3(camera->mLookAt.y, camera->mLookAt.x, camera->mLookAt.z).normalized();
-	Vector3 up = Vector3(camera->mUp.x, camera->mUp.y, camera->mUp.z);
-
-	look_at_transform.set_look_at(pos + look_at_transform.origin, look_at, up);
-	return camera_node;
-}
-
-/**
- * Generate node
- * Recursive call to iterate over all nodes
- */
-void EditorSceneImporterAssimp::_generate_node(
-		ImportState &state,
-		const aiNode *assimp_node) {
-
-	ERR_FAIL_COND(assimp_node == NULL);
-	state.nodes.push_back(assimp_node);
-	String parent_name = AssimpUtils::get_assimp_string(assimp_node->mParent->mName);
-
-	// please note
-	// duplicate bone names exist
-	// this is why we only check if the bone exists
-	// so everything else is useless but the name
-	// please do not copy any other values from get_bone_by_name.
-	aiBone *parent_bone = get_bone_by_name(state.assimp_scene, assimp_node->mParent->mName);
-	aiBone *current_bone = get_bone_by_name(state.assimp_scene, assimp_node->mName);
-
-	// is this an armature
-	// parent null
-	// and this is the first bone :)
-	if (parent_bone == NULL && current_bone) {
-		state.armature_nodes.push_back(assimp_node->mParent);
-		print_verbose("found valid armature: " + parent_name);
-	}
-
-	for (size_t i = 0; i < assimp_node->mNumChildren; i++) {
-		_generate_node(state, assimp_node->mChildren[i]);
-	}
-}

+ 0 - 262
modules/assimp/godot_update_assimp.sh

@@ -1,262 +0,0 @@
-rm -rf ../../thirdparty/assimp
-cd ../../thirdparty/
-git clone https://github.com/assimp/assimp.git
-cd assimp
-rm -rf code/3DSExporter.h
-rm -rf code/3DSLoader.h
-rm -rf code/3MFXmlTags.h
-rm -rf code/ABCImporter.h
-rm -rf code/ACLoader.h
-rm -rf code/AMFImporter_Macro.hpp
-rm -rf code/ASELoader.h
-rm -rf code/assbin_chunks.h
-rm -rf code/AssbinExporter.h
-rm -rf code/AssbinLoader.h
-rm -rf code/AssimpCExport.cpp
-rm -rf code/AssxmlExporter.h
-rm -rf code/B3DImporter.h
-# rm -rf code/BaseProcess.cpp
-# rm -rf code/BaseProcess.h
-# rm -rf code/Bitmap.cpp
-rm -rf code/BlenderBMesh.cpp
-rm -rf code/BlenderBMesh.h
-rm -rf code/BlenderCustomData.cpp
-rm -rf code/BlenderCustomData.h
-rm -rf code/BlenderIntermediate.h
-rm -rf code/BlenderLoader.h
-rm -rf code/BlenderModifier.h
-rm -rf code/BlenderSceneGen.h
-rm -rf code/BlenderTessellator.h
-rm -rf code/BVHLoader.h
-rm -rf code/C4DImporter.h
-# rm -rf code/CalcTangentsProcess.h
-# rm -rf code/CInterfaceIOWrapper.cpp
-# rm -rf code/CInterfaceIOWrapper.h
-rm -rf code/COBLoader.h
-rm -rf code/COBScene.h
-rm -rf code/ColladaExporter.h
-rm -rf code/ColladaLoader.h
-# rm -rf code/ComputeUVMappingProcess.h
-# rm -rf code/ConvertToLHProcess.h
-# rm -rf code/CreateAnimMesh.cpp
-rm -rf code/CSMLoader.h
-rm -rf code/D3MFExporter.h
-rm -rf code/D3MFImporter.h
-rm -rf code/D3MFOpcPackage.h
-# rm -rf code/DeboneProcess.h
-# rm -rf code/DefaultIOStream.cpp
-# rm -rf code/DefaultIOSystem.cpp
-# rm -rf code/DefaultProgressHandler.h
-# rm -rf code/DropFaceNormalsProcess.cpp
-# rm -rf code/DropFaceNormalsProcess.h
-rm -rf code/DXFHelper.h
-rm -rf code/DXFLoader.h
-# rm -rf code/EmbedTexturesProcess.cpp
-# rm -rf code/EmbedTexturesProcess.h
-# rm -rf code/FBXCommon.h
-# rm -rf code/FBXCompileConfig.h
-# rm -rf code/FBXDeformer.cpp
-# rm -rf code/FBXDocumentUtil.cpp
-# rm -rf code/FBXDocumentUtil.h
-# rm -rf code/FBXExporter.h
-# rm -rf code/FBXExportNode.h
-# rm -rf code/FBXExportProperty.h
-# rm -rf code/FBXImporter.cpp
-# rm -rf code/FBXImporter.h
-# rm -rf code/FBXImportSettings.h
-# rm -rf code/FBXMeshGeometry.h
-# rm -rf code/FBXModel.cpp
-# rm -rf code/FBXNodeAttribute.cpp
-# rm -rf code/FBXParser.h
-# rm -rf code/FBXProperties.cpp
-# rm -rf code/FBXProperties.h
-# rm -rf code/FBXTokenizer.cpp
-# rm -rf code/FBXTokenizer.h
-# rm -rf code/FBXUtil.cpp
-# rm -rf code/FBXUtil.h
-# rm -rf code/FileLogStream.h
-# rm -rf code/FindDegenerates.h
-# rm -rf code/FindInstancesProcess.h
-# rm -rf code/FindInvalidDataProcess.h
-rm -rf code/FIReader.hpp
-# rm -rf code/FixNormalsStep.cpp
-# rm -rf code/FixNormalsStep.h
-# rm -rf code/GenFaceNormalsProcess.cpp
-# rm -rf code/GenFaceNormalsProcess.h
-# rm -rf code/GenVertexNormalsProcess.cpp
-# rm -rf code/GenVertexNormalsProcess.h
-rm -rf code/glTF2Asset.h
-rm -rf code/glTF2Asset.inl
-rm -rf code/glTF2AssetWriter.inl
-rm -rf code/glTF2Exporter.cpp
-rm -rf code/glTF2Importer.cpp
-rm -rf code/glTF2AssetWriter.h
-rm -rf code/glTFAsset.h
-rm -rf code/glTFAsset.inl
-rm -rf code/glTFAssetWriter.inl
-rm -rf code/glTFExporter.cpp
-rm -rf code/glTFImporter.cpp
-rm -rf code/glTF2Exporter.h
-rm -rf code/glTF2Importer.h
-rm -rf code/glTFAssetWriter.h
-rm -rf code/glTFExporter.h
-rm -rf code/glTFImporter.h
-rm -rf code/HalfLifeFileData.h
-rm -rf code/HMPFileData.h
-rm -rf code/HMPLoader.h
-rm -rf code/HMPLoader.cpp
-rm -rf code/IFF.h
-# rm -rf code/Importer.h
-# rm -rf code/ImproveCacheLocality.h
-rm -rf code/IRRLoader.h
-rm -rf code/IRRMeshLoader.h
-rm -rf code/IRRShared.h
-# rm -rf code/JoinVerticesProcess.h
-# rm -rf code/LimitBoneWeightsProcess.cpp
-# rm -rf code/LimitBoneWeightsProcess.h
-rm -rf code/LWSLoader.h
-rm -rf code/makefile.mingw
-# rm -rf code/MakeVerboseFormat.cpp
-# rm -rf code/MakeVerboseFormat.h
-# rm -rf code/MaterialSystem.h
-rm -rf code/MD2FileData.h
-rm -rf code/MD2Loader.h
-rm -rf code/MD2NormalTable.h
-rm -rf code/MD3FileData.h
-rm -rf code/MD3Loader.h
-rm -rf code/MD4FileData.h
-rm -rf code/MD5Loader.h
-rm -rf code/MD5Parser.cpp
-rm -rf code/MDCFileData.h
-rm -rf code/MDCLoader.h
-rm -rf code/MDLDefaultColorMap.h
-# rm -rf code/MMDCpp14.h
-# rm -rf code/MMDImporter.h
-rm -rf code/MS3DLoader.h
-rm -rf code/NDOLoader.h
-rm -rf code/NFFLoader.h
-rm -rf code/ObjExporter.h
-rm -rf code/ObjFileImporter.h
-rm -rf code/ObjFileMtlImporter.h
-rm -rf code/ObjFileParser.h
-rm -rf code/ObjTools.h
-rm -rf code/ObjExporter.cpp
-rm -rf code/ObjFileImporter.cpp
-rm -rf code/ObjFileMtlImporter.cpp
-rm -rf code/ObjFileParser.cpp
-rm -rf code/OFFLoader.h
-rm -rf code/OFFLoader.cpp
-rm -rf code/OgreImporter.cpp
-rm -rf code/OgreImporter.h
-rm -rf code/OgreParsingUtils.h
-rm -rf code/OgreXmlSerializer.h
-rm -rf code/OgreXmlSerializer.cpp
-rm -rf code/OgreBinarySerializer.cpp
-rm -rf code/OpenGEXExporter.cpp
-rm -rf code/OpenGEXExporter.h
-rm -rf code/OpenGEXImporter.h
-rm -rf code/OpenGEXStructs.h
-rm -rf code/OpenGEXImporter.cpp
-# rm -rf code/OptimizeGraph.h
-# rm -rf code/OptimizeMeshes.cpp
-# rm -rf code/OptimizeMeshes.h
-rm -rf code/PlyExporter.h
-rm -rf code/PlyLoader.h
-# rm -rf code/PolyTools.h
-# rm -rf code/PostStepRegistry.cpp
-# rm -rf code/PretransformVertices.h
-rm -rf code/Q3BSPFileData.h
-rm -rf code/Q3BSPFileImporter.h
-rm -rf code/Q3BSPFileParser.cpp
-rm -rf code/Q3BSPFileParser.h
-rm -rf code/Q3BSPZipArchive.cpp
-rm -rf code/Q3BSPZipArchive.h
-rm -rf code/Q3DLoader.h
-rm -rf code/Q3DLoader.cpp
-rm -rf code/Q3BSPFileImporter.cpp
-rm -rf code/RawLoader.h
-# rm -rf code/RemoveComments.cpp
-# rm -rf code/RemoveRedundantMaterials.cpp
-# rm -rf code/RemoveRedundantMaterials.h
-# rm -rf code/RemoveVCProcess.h
-# rm -rf code/ScaleProcess.cpp
-# rm -rf code/ScaleProcess.h
-# rm -rf code/scene.cpp
-# rm -rf code/ScenePreprocessor.cpp
-# rm -rf code/ScenePreprocessor.h
-# rm -rf code/ScenePrivate.h
-# rm -rf code/SGSpatialSort.cpp
-rm -rf code/SIBImporter.h
-rm -rf code/SMDLoader.cpp
-# rm -rf code/simd.cpp
-# rm -rf code/simd.h
-# rm -rf code/SortByPTypeProcess.h
-# rm -rf code/SplitByBoneCountProcess.h
-# rm -rf code/SplitLargeMeshes.h
-# rm -rf code/StdOStreamLogStream.h
-rm -rf code/StepExporter.h
-rm -rf code/StepExporter.cpp
-rm -rf code/STLExporter.cpp
-rm -rf code/STLExporter.h
-rm -rf code/STLLoader.h
-rm -rf code/STLLoader.cpp
-# rm -rf code/TargetAnimation.cpp
-# rm -rf code/TargetAnimation.h
-rm -rf code/TerragenLoader.h
-rm -rf code/TerragenLoader.cpp
-# rm -rf code/TextureTransform.h
-# rm -rf code/TriangulateProcess.h
-rm -rf code/UnrealLoader.h
-# rm -rf code/ValidateDataStructure.h
-# rm -rf code/Version.cpp
-# rm -rf code/VertexTriangleAdjacency.cpp
-# rm -rf code/VertexTriangleAdjacency.h
-# rm -rf code/Win32DebugLogStream.h
-rm -rf code/X3DImporter_Macro.hpp
-rm -rf code/X3DImporter_Metadata.cpp
-rm -rf code/X3DImporter_Networking.cpp
-rm -rf code/X3DImporter_Texturing.cpp
-rm -rf code/X3DImporter_Shape.cpp
-rm -rf code/X3DImporter_Rendering.cpp
-rm -rf code/X3DImporter_Postprocess.cpp
-rm -rf code/X3DImporter_Light.cpp
-rm -rf code/X3DImporter_Group.cpp
-rm -rf code/X3DImporter_Geometry3D.cpp
-rm -rf code/X3DImporter_Geometry2D.cpp
-rm -rf code/X3DImporter.cpp
-rm -rf code/X3DExporter.cpp
-rm -rf code/X3DVocabulary.cpp
-rm -rf code/XFileExporter.h
-rm -rf code/XFileExporter.cpp
-rm -rf code/XFileHelper.h
-rm -rf code/XFileHelper.cpp
-rm -rf code/XFileImporter.h
-rm -rf code/XFileImporter.cpp
-rm -rf code/XFileParser.h
-rm -rf code/XFileParser.cpp
-rm -rf code/XGLLoader.h
-rm -rf code/XGLLoader.cpp
-rm -rf code/Importer
-rm -rf .git
-rm -rf cmake-modules
-rm -rf doc
-rm -rf packaging
-rm -rf port
-rm -rf samples
-rm -rf scripts
-rm -rf test
-rm -rf tools
-rm -rf contrib/zlib
-rm -rf contrib/android-cmake
-rm -rf contrib/gtest
-rm -rf contrib/clipper
-rm -rf contrib/irrXML
-rm -rf contrib/Open3DGC
-rm -rf contrib/openddlparser
-rm -rf contrib/poly2tri
-#rm -rf contrib/rapidjson
-rm -rf contrib/unzip
-rm -rf contrib/zip
-rm -rf contrib/stb_image
-rm .travis*
-

+ 0 - 447
modules/assimp/import_utils.h

@@ -1,447 +0,0 @@
-/*************************************************************************/
-/*  import_utils.h                                                       */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef IMPORT_UTILS_IMPORTER_ASSIMP_H
-#define IMPORT_UTILS_IMPORTER_ASSIMP_H
-
-#include "core/io/image_loader.h"
-#include "import_state.h"
-
-#include <assimp/SceneCombiner.h>
-#include <assimp/cexport.h>
-#include <assimp/cimport.h>
-#include <assimp/matrix4x4.h>
-#include <assimp/pbrmaterial.h>
-#include <assimp/postprocess.h>
-#include <assimp/scene.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/Importer.hpp>
-#include <assimp/LogStream.hpp>
-#include <assimp/Logger.hpp>
-#include <string>
-
-using namespace AssimpImporter;
-
-#define AI_PROPERTIES aiTextureType_UNKNOWN, 0
-#define AI_NULL 0, 0
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor"
-#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness"
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness"
-
-#define AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE "$raw.Maya|emissionColor|file"
-#define AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR "$raw.Maya|emission"
-#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file"
-#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file"
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file"
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
-#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
-
-#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
-#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity"
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo"
-
-/**
- * Assimp Utils
- * Conversion tools / glue code to convert from assimp to godot
-*/
-class AssimpUtils {
-public:
-	/**
-	 * calculate tangents for mesh data from assimp data
-	 */
-	static void calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) {
-		const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index];
-		const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
-		const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index];
-		const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z);
-		const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index];
-		const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
-		float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
-		Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d);
-		w[index] = plane_tangent;
-	}
-
-	struct AssetImportFbx {
-		enum ETimeMode {
-			TIME_MODE_DEFAULT = 0,
-			TIME_MODE_120 = 1,
-			TIME_MODE_100 = 2,
-			TIME_MODE_60 = 3,
-			TIME_MODE_50 = 4,
-			TIME_MODE_48 = 5,
-			TIME_MODE_30 = 6,
-			TIME_MODE_30_DROP = 7,
-			TIME_MODE_NTSC_DROP_FRAME = 8,
-			TIME_MODE_NTSC_FULL_FRAME = 9,
-			TIME_MODE_PAL = 10,
-			TIME_MODE_CINEMA = 11,
-			TIME_MODE_1000 = 12,
-			TIME_MODE_CINEMA_ND = 13,
-			TIME_MODE_CUSTOM = 14,
-			TIME_MODE_TIME_MODE_COUNT = 15
-		};
-		enum UpAxis {
-			UP_VECTOR_AXIS_X = 1,
-			UP_VECTOR_AXIS_Y = 2,
-			UP_VECTOR_AXIS_Z = 3
-		};
-		enum FrontAxis {
-			FRONT_PARITY_EVEN = 1,
-			FRONT_PARITY_ODD = 2,
-		};
-
-		enum CoordAxis {
-			COORD_RIGHT = 0,
-			COORD_LEFT = 1
-		};
-	};
-
-	/** Get assimp string
-    * automatically filters the string data
-    */
-	static String get_assimp_string(const aiString &p_string) {
-		//convert an assimp String to a Godot String
-		String name;
-		name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
-		if (name.find(":") != -1) {
-			String replaced_name = name.split(":")[1];
-			print_verbose("Replacing " + name + " containing : with " + replaced_name);
-			name = replaced_name;
-		}
-
-		return name;
-	}
-
-	static String get_anim_string_from_assimp(const aiString &p_string) {
-
-		String name;
-		name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
-		if (name.find(":") != -1) {
-			String replaced_name = name.split(":")[1];
-			print_verbose("Replacing " + name + " containing : with " + replaced_name);
-			name = replaced_name;
-		}
-		return name;
-	}
-
-	/**
-     * No filter logic get_raw_string_from_assimp
-     * This just convers the aiString to a parsed utf8 string
-     * Without removing special chars etc
-     */
-	static String get_raw_string_from_assimp(const aiString &p_string) {
-		String name;
-		name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
-		return name;
-	}
-
-	static Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
-		return Ref<Animation>();
-	}
-
-	/**
-     * Converts aiMatrix4x4 to godot Transform
-    */
-	static const Transform assimp_matrix_transform(const aiMatrix4x4 p_matrix) {
-		aiMatrix4x4 matrix = p_matrix;
-		Transform xform;
-		xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
-		return xform;
-	}
-
-	/** Get fbx fps for time mode meta data
-     */
-	static float get_fbx_fps(int32_t time_mode, const aiScene *p_scene) {
-		switch (time_mode) {
-			case AssetImportFbx::TIME_MODE_DEFAULT: return 24; //hack
-			case AssetImportFbx::TIME_MODE_120: return 120;
-			case AssetImportFbx::TIME_MODE_100: return 100;
-			case AssetImportFbx::TIME_MODE_60: return 60;
-			case AssetImportFbx::TIME_MODE_50: return 50;
-			case AssetImportFbx::TIME_MODE_48: return 48;
-			case AssetImportFbx::TIME_MODE_30: return 30;
-			case AssetImportFbx::TIME_MODE_30_DROP: return 30;
-			case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f;
-			case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f;
-			case AssetImportFbx::TIME_MODE_PAL: return 25;
-			case AssetImportFbx::TIME_MODE_CINEMA: return 24;
-			case AssetImportFbx::TIME_MODE_1000: return 1000;
-			case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f;
-			case AssetImportFbx::TIME_MODE_CUSTOM:
-				int32_t frame_rate = -1;
-				p_scene->mMetaData->Get("FrameRate", frame_rate);
-				return frame_rate;
-		}
-		return 0;
-	}
-
-	/**
-      * Get global transform for the current node - so we can use world space rather than
-      * local space coordinates
-      * useful if you need global - although recommend using local wherever possible over global
-      * as you could break fbx scaling :)
-      */
-	static Transform _get_global_assimp_node_transform(const aiNode *p_current_node) {
-		aiNode const *current_node = p_current_node;
-		Transform xform;
-		while (current_node != NULL) {
-			xform = assimp_matrix_transform(current_node->mTransformation) * xform;
-			current_node = current_node->mParent;
-		}
-		return xform;
-	}
-
-	/**
-	  * Find hardcoded textures from assimp which could be in many different directories
-	  */
-	static void find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) {
-		Vector<String> paths;
-		paths.push_back(path.get_basename() + extension);
-		paths.push_back(path + extension);
-		paths.push_back(path);
-		paths.push_back(p_path.get_base_dir().plus_file(path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file(path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file(path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file()));
-		paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file().get_basename() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file() + extension));
-		paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file()));
-		for (int i = 0; i < paths.size(); i++) {
-			if (dir.file_exists(paths[i])) {
-				found = true;
-				path = paths[i];
-				return;
-			}
-		}
-	}
-
-	/** find the texture path for the supplied fbx path inside godot
-      * very simple lookup for subfolders etc for a texture which may or may not be in a directory
-      */
-	static void find_texture_path(const String &r_p_path, String &r_path, bool &r_found) {
-		_Directory dir;
-
-		List<String> exts;
-		ImageLoader::get_recognized_extensions(&exts);
-
-		Vector<String> split_path = r_path.get_basename().split("*");
-		if (split_path.size() == 2) {
-			r_found = true;
-			return;
-		}
-
-		if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) {
-			r_path = r_p_path.get_base_dir() + r_path.get_file();
-			r_found = true;
-			return;
-		}
-
-		for (int32_t i = 0; i < exts.size(); i++) {
-			if (r_found) {
-				return;
-			}
-			find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]);
-		}
-	}
-
-	/**
-	  * set_texture_mapping_mode
-	  * Helper to check the mapping mode of the texture (repeat, clamp and mirror)
-	  */
-	static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
-		ERR_FAIL_COND(texture.is_null());
-		ERR_FAIL_COND(map_mode == NULL);
-		aiTextureMapMode tex_mode = map_mode[0];
-
-		int32_t flags = Texture::FLAGS_DEFAULT;
-		if (tex_mode == aiTextureMapMode_Wrap) {
-			//Default
-		} else if (tex_mode == aiTextureMapMode_Clamp) {
-			flags = flags & ~Texture::FLAG_REPEAT;
-		} else if (tex_mode == aiTextureMapMode_Mirror) {
-			flags = flags | Texture::FLAG_MIRRORED_REPEAT;
-		}
-		texture->set_flags(flags);
-	}
-
-	/**
-	  * Load or load from cache image :)
-	  */
-	static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path) {
-
-		Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path);
-
-		// if our cache contains this image then don't bother
-		if (match) {
-			return match->get();
-		}
-
-		Vector<String> split_path = p_path.get_basename().split("*");
-		if (split_path.size() == 2) {
-			size_t texture_idx = split_path[1].to_int();
-			ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
-			aiTexture *tex = p_scene->mTextures[texture_idx];
-			String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
-			filename = filename.get_file();
-			print_verbose("Open Asset Import: Loading embedded texture " + filename);
-			if (tex->mHeight == 0) {
-				if (tex->CheckFormat("png")) {
-					ERR_FAIL_COND_V(Image::_png_mem_loader_func == NULL, Ref<Image>());
-					Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
-					ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
-					state.path_to_image_cache.insert(p_path, img);
-					return img;
-				} else if (tex->CheckFormat("jpg")) {
-					ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == NULL, Ref<Image>());
-					Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
-					ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
-					state.path_to_image_cache.insert(p_path, img);
-					return img;
-				} else if (tex->CheckFormat("dds")) {
-					ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented");
-				}
-			} else {
-				Ref<Image> img;
-				img.instance();
-				PoolByteArray arr;
-				uint32_t size = tex->mWidth * tex->mHeight;
-				arr.resize(size);
-				memcpy(arr.write().ptr(), tex->pcData, size);
-				ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
-				//ARGB8888 to RGBA8888
-				for (int32_t i = 0; i < arr.size() / 4; i++) {
-					arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
-					arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
-					arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
-					arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
-				}
-				img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
-				ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
-				state.path_to_image_cache.insert(p_path, img);
-				return img;
-			}
-			return Ref<Image>();
-		} else {
-			Ref<Texture> texture = ResourceLoader::load(p_path);
-			ERR_FAIL_COND_V(texture.is_null(), Ref<Image>());
-			Ref<Image> image = texture->get_data();
-			ERR_FAIL_COND_V(image.is_null(), Ref<Image>());
-			state.path_to_image_cache.insert(p_path, image);
-			return image;
-		}
-
-		return Ref<Image>();
-	}
-
-	/* create texture from assimp data, if found in path */
-	static bool CreateAssimpTexture(
-			AssimpImporter::ImportState &state,
-			aiString texture_path,
-			String &filename,
-			String &path,
-			AssimpImageData &image_state) {
-		filename = get_raw_string_from_assimp(texture_path);
-		path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
-		bool found = false;
-		find_texture_path(state.path, path, found);
-		if (found) {
-			image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
-			if (image_state.raw_image.is_valid()) {
-				image_state.texture.instance();
-				image_state.texture->create_from_image(image_state.raw_image);
-				image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
-				return true;
-			}
-		}
-
-		return false;
-	}
-	/** GetAssimpTexture
-	  * Designed to retrieve textures for you
-	  */
-	static bool GetAssimpTexture(
-			AssimpImporter::ImportState &state,
-			aiMaterial *ai_material,
-			aiTextureType texture_type,
-			String &filename,
-			String &path,
-			AssimpImageData &image_state) {
-		aiString ai_filename = aiString();
-		if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, NULL, NULL, NULL, NULL, image_state.map_mode)) {
-			return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
-		}
-
-		return false;
-	}
-};
-
-#endif // IMPORT_UTILS_IMPORTER_ASSIMP_H

+ 15 - 0
modules/fbx/SCsub

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_fbx = env_modules.Clone()
+
+# Make includes relative to the folder path specified here so our includes are clean
+env_fbx.Prepend(CPPPATH=["#modules/fbx/"])
+
+# Godot's own source files
+env_fbx.add_source_files(env.modules_sources, "tools/*.cpp")
+env_fbx.add_source_files(env.modules_sources, "data/*.cpp")
+env_fbx.add_source_files(env.modules_sources, "fbx_parser/*.cpp")
+env_fbx.add_source_files(env.modules_sources, "*.cpp")

+ 0 - 0
modules/assimp/config.py → modules/fbx/config.py


+ 46 - 0
modules/fbx/data/fbx_anim_container.h

@@ -0,0 +1,46 @@
+/*************************************************************************/
+/*  fbx_anim_container.h                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FBX_ANIM_CONTAINER_H
+#define FBX_ANIM_CONTAINER_H
+
+#include "core/vector.h"
+
+// Generic keyframes 99.99 percent of files will be vector3, except if quat interp is used, or visibility tracks
+// FBXTrack is used in a map in the implementation in fbx/editor_scene_importer_fbx.cpp
+// to avoid having to rewrite the entire logic I refactored this into the code instead.
+// once it works I can rewrite so we can add the fun misc features / small features
+struct FBXTrack {
+	bool has_default = false;
+	Vector3 default_value;
+	std::map<int64_t, Vector3> keyframes;
+};
+
+#endif //MODEL_ABSTRACTION_ANIM_CONTAINER_H

+ 93 - 0
modules/fbx/data/fbx_bone.cpp

@@ -0,0 +1,93 @@
+/*************************************************************************/
+/*  fbx_bone.cpp                                                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "fbx_bone.h"
+
+#include "fbx_node.h"
+#include "import_state.h"
+
+Ref<FBXNode> FBXBone::get_link(const ImportState &state) const {
+	print_verbose("bone name: " + bone_name);
+	ERR_FAIL_COND_V_MSG(cluster == nullptr, nullptr, "bone has invalid cluster");
+	ERR_FAIL_COND_V_MSG(cluster->TargetNode() == nullptr, nullptr, "bone has invalid target node");
+
+	Ref<FBXNode> link_node;
+	uint64_t id = cluster->TargetNode()->ID();
+	if (state.fbx_target_map.has(id)) {
+		link_node = state.fbx_target_map[id];
+	} else {
+		print_error("link node not found for " + itos(id));
+	}
+
+	// the node in space this is for, like if it's FOR a target.
+	return link_node;
+}
+
+/* right now we just get single skin working and we can patch in the multiple tomorrow - per skin not per bone. */
+// this will work for multiple meshes :) awesomeness.
+// okay so these formula's are complex and need proper understanding of
+// shear, pivots, geometric pivots, pre rotation and post rotation
+// additionally DO NOT EDIT THIS if your blender file isn't working.
+// Contact RevoluPowered Gordon MacPherson if you are contemplating making edits to this.
+Transform FBXBone::get_vertex_skin_xform(const ImportState &state, Transform mesh_global_position, bool &r_valid_pose) {
+	r_valid_pose = false;
+	print_verbose("get_vertex_skin_xform: " + bone_name);
+	ERR_FAIL_COND_V_MSG(cluster == nullptr, Transform(), "[serious] unable to resolve the fbx cluster for this bone " + bone_name);
+	// these methods will ONLY work for Maya.
+	if (cluster->TransformAssociateModelValid()) {
+		//print_error("additive skinning in use");
+		Transform associate_global_init_position = cluster->TransformAssociateModel();
+		Transform associate_global_current_position = Transform();
+		Transform reference_global_init_position = cluster->GetTransform();
+		Transform cluster_global_init_position = cluster->TransformLink();
+		Ref<FBXNode> link_node = get_link(state);
+		ERR_FAIL_COND_V_MSG(link_node.is_null(), Transform(), "invalid link corrupt file detected");
+		r_valid_pose = true;
+		Transform cluster_global_current_position = link_node.is_valid() && link_node->pivot_transform.is_valid() ? link_node->pivot_transform->GlobalTransform : Transform();
+
+		vertex_transform_matrix = reference_global_init_position.affine_inverse() * associate_global_init_position * associate_global_current_position.affine_inverse() *
+								  cluster_global_current_position * cluster_global_init_position.affine_inverse() * reference_global_init_position;
+	} else {
+		//print_error("non additive skinning is in use");
+		Transform reference_global_position = cluster->GetTransform();
+		Transform reference_global_current_position = mesh_global_position;
+		//Transform geometric_pivot = Transform(); // we do not use this - 3ds max only
+		Transform global_init_position = cluster->TransformLink();
+		if (global_init_position.basis.determinant() == 0) {
+			global_init_position = Transform(Basis(), global_init_position.origin);
+		}
+		Transform cluster_relative_init_position = global_init_position.affine_inverse() * reference_global_position;
+		Transform cluster_relative_position_inverse = reference_global_current_position.affine_inverse() * global_init_position;
+		vertex_transform_matrix = cluster_relative_position_inverse * cluster_relative_init_position;
+		r_valid_pose = true;
+	}
+
+	return vertex_transform_matrix;
+}

+ 95 - 0
modules/fbx/data/fbx_bone.h

@@ -0,0 +1,95 @@
+/*************************************************************************/
+/*  fbx_bone.h                                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FBX_BONE_H
+#define FBX_BONE_H
+
+#include "fbx_node.h"
+#include "import_state.h"
+
+#include "fbx_parser/FBXDocument.h"
+
+struct PivotTransform;
+
+struct FBXBone : public Reference {
+	uint64_t parent_bone_id = 0;
+	uint64_t bone_id = 0;
+
+	bool valid_parent = false; // if the parent bone id is set up.
+	String bone_name = String(); // bone name
+
+	bool is_root_bone() const {
+		return !valid_parent;
+	}
+
+	uint64_t target_node_id; // the node target id for the skeleton element
+	bool valid_target = false; // only applies to bones with a mesh / in the skin.
+
+	// Godot specific data
+	int godot_bone_id = -2; // godot internal bone id assigned after import
+
+	// if a bone / armature is the root then FBX skeleton will contain the bone not any other skeleton.
+	// this is to support joints by themselves in scenes
+	bool valid_armature_id = false;
+	uint64_t armature_id = 0;
+
+	// Vertex Weight information
+	Transform transform_link; // todo remove
+	Transform transform_matrix; // todo remove
+
+	/* get associate model - the model can be invalid sometimes */
+	Ref<FBXBone> get_associate_model() const {
+		return parent_bone;
+	}
+
+	/* link node is the parent bone */
+	Ref<FBXNode> get_link(const ImportState &state) const;
+	Transform get_vertex_skin_xform(const ImportState &state, Transform mesh_global_position, bool &valid);
+	Transform vertex_transform_matrix;
+	Transform local_cluster_matrix; // set_bone_pose
+
+	mutable const FBXDocParser::Deformer *skin = nullptr;
+	mutable const FBXDocParser::Cluster *cluster = nullptr;
+	mutable const FBXDocParser::Geometry *geometry = nullptr;
+	mutable const FBXDocParser::ModelLimbNode *limb_node = nullptr;
+
+	void set_pivot_xform(Ref<PivotTransform> p_pivot_xform) {
+		pivot_xform = p_pivot_xform;
+	}
+
+	// pose node / if assigned
+	Transform pose_node = Transform();
+	bool assigned_pose_node = false;
+	Ref<FBXBone> parent_bone = Ref<FBXBone>();
+	Ref<PivotTransform> pivot_xform = Ref<PivotTransform>();
+	Ref<FBXSkeleton> fbx_skeleton = Ref<FBXSkeleton>();
+};
+
+#endif // FBX_BONE_H

+ 487 - 0
modules/fbx/data/fbx_material.cpp

@@ -0,0 +1,487 @@
+/*************************************************************************/
+/*  fbx_material.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "fbx_material.h"
+
+#include "scene/resources/material.h"
+#include "scene/resources/texture.h"
+
+String FBXMaterial::get_material_name() const {
+	return material_name;
+}
+
+void FBXMaterial::set_imported_material(const FBXDocParser::Material *p_material) {
+	material = p_material;
+}
+
+void FBXMaterial::add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths) {
+	if (search_directory.empty()) {
+		texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(p_filename));
+	} else {
+		texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(search_directory + "/" + p_filename));
+		texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file("../" + search_directory + "/" + p_filename));
+	}
+}
+
+String find_file(const String &p_base, const String &p_file_to_find) {
+	_Directory dir;
+	dir.open(p_base);
+
+	dir.list_dir_begin();
+	String n = dir.get_next();
+	while (n != String()) {
+		if (n == "." || n == "..") {
+			n = dir.get_next();
+			continue;
+		}
+		if (dir.current_is_dir()) {
+			// Don't use `path_to` or the returned path will be wrong.
+			const String f = find_file(p_base + "/" + n, p_file_to_find);
+			if (f != "") {
+				return f;
+			}
+		} else if (n == p_file_to_find) {
+			return p_base + "/" + n;
+		}
+		n = dir.get_next();
+	}
+	dir.list_dir_end();
+
+	return String();
+}
+
+// fbx will not give us good path information and let's not regex them to fix them
+// no relative paths are in fbx generally they have a rel field but it's populated incorrectly by the SDK.
+String FBXMaterial::find_texture_path_by_filename(const String p_filename, const String p_current_directory) {
+	_Directory dir;
+	Vector<String> paths;
+	add_search_string(p_filename, p_current_directory, "", paths);
+	add_search_string(p_filename, p_current_directory, "texture", paths);
+	add_search_string(p_filename, p_current_directory, "textures", paths);
+	add_search_string(p_filename, p_current_directory, "Textures", paths);
+	add_search_string(p_filename, p_current_directory, "materials", paths);
+	add_search_string(p_filename, p_current_directory, "mats", paths);
+	add_search_string(p_filename, p_current_directory, "pictures", paths);
+	add_search_string(p_filename, p_current_directory, "images", paths);
+
+	for (int i = 0; i < paths.size(); i++) {
+		if (dir.file_exists(paths[i])) {
+			return paths[i];
+		}
+	}
+
+	// We were not able to find the texture in the common locations,
+	// try to find it into the project globally.
+	// The common textures can be stored into one of those folders:
+	// res://asset
+	// res://texture
+	// res://material
+	// res://mat
+	// res://image
+	// res://picture
+	//
+	// Note the folders can also be called with custom names, like:
+	// res://my_assets
+	// since the keyword `asset` is into the directory name the textures will be
+	// searched there too.
+
+	dir.open("res://");
+	dir.list_dir_begin();
+	String n = dir.get_next();
+	while (n != String()) {
+		if (n == "." || n == "..") {
+			n = dir.get_next();
+			continue;
+		}
+		if (dir.current_is_dir()) {
+			const String lower_n = n.to_lower();
+			if (
+					// Don't need to use plural.
+					lower_n.find("asset") >= 0 ||
+					lower_n.find("texture") >= 0 ||
+					lower_n.find("material") >= 0 ||
+					lower_n.find("mat") >= 0 ||
+					lower_n.find("image") >= 0 ||
+					lower_n.find("picture") >= 0) {
+				// Don't use `path_to` or the returned path will be wrong.
+				const String f = find_file(String("res://") + n, p_filename);
+				if (f != "") {
+					return f;
+				}
+			}
+		}
+		n = dir.get_next();
+	}
+	dir.list_dir_end();
+
+	return "";
+}
+
+FBXMaterial::MaterialInfo FBXMaterial::extract_material_info(const FBXDocParser::Material *material) const {
+	MaterialInfo mat_info;
+
+	// TODO Layered textures are a collection on textures stored into an array.
+	// Extract layered textures is not yet supported. Per each texture in the
+	// layered texture array you want to use the below method to extract those.
+
+	for (std::pair<std::string, const FBXDocParser::Texture *> texture : material->Textures()) {
+		const std::string &fbx_mapping_name = texture.first;
+
+		if (fbx_feature_mapping_desc.count(fbx_mapping_name) > 0) {
+			// This is a feature not a normal texture.
+			mat_info.features.push_back(fbx_feature_mapping_desc.at(fbx_mapping_name));
+			continue;
+		}
+
+		ERR_CONTINUE_MSG(fbx_texture_mapping_desc.count(fbx_mapping_name) <= 0, "This FBX has a material with mapping name: " + String(fbx_mapping_name.c_str()) + " which is not yet supported by this importer. Consider open an issue so we can support it.");
+
+		const String absoulte_fbx_file_path = texture.second->FileName().c_str();
+		const String file_extension = absoulte_fbx_file_path.get_extension().to_upper();
+
+		const String file_extension_uppercase = file_extension.to_upper();
+
+		// TODO: we don't support EMBED for DDS and TGA.
+		ERR_CONTINUE_MSG(
+				file_extension_uppercase != "PNG" &&
+						file_extension_uppercase != "JPEG" &&
+						file_extension_uppercase != "JPG" &&
+						file_extension_uppercase != "TGA" &&
+						file_extension_uppercase != "WEBP" &&
+						file_extension_uppercase != "DDS",
+				"The FBX file contains a texture with an unrecognized extension: " + file_extension_uppercase);
+
+		const String texture_name = absoulte_fbx_file_path.get_file();
+		const SpatialMaterial::TextureParam mapping_mode = fbx_texture_mapping_desc.at(fbx_mapping_name);
+
+		TextureFileMapping file_mapping;
+		file_mapping.map_mode = mapping_mode;
+		file_mapping.name = texture_name;
+		file_mapping.texture = texture.second;
+		mat_info.textures.push_back(file_mapping);
+
+		// Make sure to active the various features.
+		switch (mapping_mode) {
+			case SpatialMaterial::TextureParam::TEXTURE_ALBEDO:
+			case SpatialMaterial::TextureParam::TEXTURE_METALLIC:
+			case SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS:
+			case SpatialMaterial::TextureParam::TEXTURE_FLOWMAP:
+			case SpatialMaterial::TextureParam::TEXTURE_REFRACTION:
+			case SpatialMaterial::TextureParam::TEXTURE_MAX:
+				// No features required.
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_EMISSION:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_EMISSION);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_NORMAL:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_RIM:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_RIM);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_CLEARCOAT:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_CLEARCOAT);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_AMBIENT_OCCLUSION);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_DEPTH:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_DEPTH_MAPPING);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_SUBSURFACE_SCATTERING:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_SUBSURACE_SCATTERING);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_TRANSMISSION:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_TRANSMISSION);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_DETAIL_ALBEDO:
+			case SpatialMaterial::TextureParam::TEXTURE_DETAIL_MASK:
+			case SpatialMaterial::TextureParam::TEXTURE_DETAIL_NORMAL:
+				mat_info.features.push_back(SpatialMaterial::Feature::FEATURE_DETAIL);
+				break;
+		}
+	}
+
+	return mat_info;
+}
+
+template <class T>
+T extract_from_prop(FBXDocParser::PropertyPtr prop, const T &p_default, const std::string &p_name, const String &p_type) {
+	ERR_FAIL_COND_V_MSG(prop == nullptr, p_default, "invalid property passed to extractor");
+	const FBXDocParser::TypedProperty<T> *val = dynamic_cast<const FBXDocParser::TypedProperty<T> *>(prop);
+
+	ERR_FAIL_COND_V_MSG(val == nullptr, p_default, "The FBX is corrupted, the property `" + String(p_name.c_str()) + "` is a `" + String(typeid(*prop).name()) + "` but should be a " + p_type);
+	// Make sure to not lost any eventual opacity.
+	return val->Value();
+}
+
+Ref<SpatialMaterial> FBXMaterial::import_material(ImportState &state) {
+
+	ERR_FAIL_COND_V(material == nullptr, nullptr);
+
+	const String p_fbx_current_directory = state.path;
+
+	Ref<SpatialMaterial> spatial_material;
+
+	// read the material file
+	// is material two sided
+	// read material name
+	print_verbose("[material] material name: " + ImportUtils::FBXNodeToName(material->Name()));
+	material_name = ImportUtils::FBXNodeToName(material->Name());
+
+	// Extract info.
+	MaterialInfo material_info = extract_material_info(material);
+
+	// Extract other parameters info.
+	for (FBXDocParser::LazyPropertyMap::value_type iter : material->Props()->GetLazyProperties()) {
+		const std::string name = iter.first;
+		//const Assimp::FBX::ElementPtr element = iter.second;
+
+		if (name.empty()) {
+			continue;
+		}
+
+		PropertyDesc desc = PROPERTY_DESC_NOT_FOUND;
+		if (fbx_properties_desc.count(name) > 0) {
+			desc = fbx_properties_desc.at(name);
+		}
+
+		if (desc == PROPERTY_DESC_IGNORE) {
+			print_verbose("The FBX material parameter: `" + String(name.c_str()) + "` is ignored.");
+			continue;
+		} else {
+			print_verbose("FBX Material parameter: " + String(name.c_str()));
+		}
+
+		if (desc == PROPERTY_DESC_NOT_FOUND) {
+			continue;
+		}
+
+		ERR_CONTINUE_MSG(desc == PROPERTY_DESC_NOT_FOUND, "The FBX material parameter: `" + String(name.c_str()) + "` was not recognized. Please open an issue so we can add the support to it.");
+
+		FBXDocParser::PropertyPtr prop = material->Props()->Get(name);
+
+		//Assimp::FBX::PropertyPtr prop = prop.second.
+
+		if (prop == nullptr) {
+			continue;
+		}
+		ERR_CONTINUE_MSG(prop == nullptr, "This file may be corrupted because is not possible to extract the material parameter: " + String(name.c_str()));
+
+		if (spatial_material.is_null()) {
+			// Done here so if no data no material is created.
+			spatial_material.instance();
+		}
+
+		switch (desc) {
+			case PROPERTY_DESC_ALBEDO_COLOR: {
+				const Vector3 color = extract_from_prop(prop, Vector3(0, 0, 0), name, "Vector3");
+				// Make sure to not lost any eventual opacity.
+				Color c = spatial_material->get_albedo();
+				c[0] = color[0];
+				c[1] = color[1];
+				c[2] = color[2];
+				spatial_material->set_albedo(c);
+			} break;
+			case PROPERTY_DESC_TRANSPARENT: {
+				const real_t opacity = extract_from_prop(prop, 1.0f, name, "float");
+				if (opacity < (1.0 - CMP_EPSILON)) {
+					Color c = spatial_material->get_albedo();
+					c[3] = opacity;
+					spatial_material->set_albedo(c);
+					material_info.features.push_back(SpatialMaterial::Feature::FEATURE_TRANSPARENT);
+					spatial_material->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
+				}
+			} break;
+			case PROPERTY_DESC_METALLIC: {
+				spatial_material->set_metallic(std::min(1.0f, extract_from_prop(prop, 1.0f, name, "float")));
+			} break;
+			case PROPERTY_DESC_ROUGHNESS: {
+				spatial_material->set_roughness(std::min(1.0f, extract_from_prop(prop, 1.0f, name, "float")));
+			} break;
+			case PROPERTY_DESC_COAT: {
+				spatial_material->set_clearcoat(extract_from_prop(prop, 1.0f, name, "float"));
+				material_info.features.push_back(SpatialMaterial::Feature::FEATURE_CLEARCOAT);
+			} break;
+			case PROPERTY_DESC_COAT_ROUGHNESS: {
+				spatial_material->set_clearcoat_gloss(1.0 - extract_from_prop(prop, 0.5f, name, "float"));
+				material_info.features.push_back(SpatialMaterial::Feature::FEATURE_CLEARCOAT);
+			} break;
+			case PROPERTY_DESC_EMISSIVE: {
+				const real_t emissive = extract_from_prop(prop, 0.0f, name, "float");
+				if (emissive > CMP_EPSILON) {
+					spatial_material->set_emission_energy(emissive);
+					material_info.features.push_back(SpatialMaterial::Feature::FEATURE_EMISSION);
+				}
+			} break;
+			case PROPERTY_DESC_EMISSIVE_COLOR: {
+				const Vector3 color = extract_from_prop(prop, Vector3(0, 0, 0), name, "Vector3");
+				Color c;
+				c[0] = color[0];
+				c[1] = color[1];
+				c[2] = color[2];
+				spatial_material->set_emission(c);
+			} break;
+			case PROPERTY_DESC_NOT_FOUND:
+			case PROPERTY_DESC_IGNORE:
+				// Already checked, can't happen.
+				CRASH_NOW();
+				break;
+		}
+	}
+
+	// Set the material features.
+	for (int x = 0; x < material_info.features.size(); x++) {
+		if (spatial_material.is_null()) {
+			// Done here so if no textures no material is created.
+			spatial_material.instance();
+		}
+		spatial_material->set_feature(material_info.features[x], true);
+	}
+
+	// Set the textures.
+	for (int x = 0; x < material_info.textures.size(); x++) {
+		TextureFileMapping mapping = material_info.textures[x];
+		Ref<Texture> texture;
+		print_verbose("texture mapping name: " + mapping.name);
+
+		if (state.cached_image_searches.has(mapping.name)) {
+			texture = state.cached_image_searches[mapping.name];
+		} else {
+			String path = find_texture_path_by_filename(mapping.name, p_fbx_current_directory);
+			if (!path.empty()) {
+				Error err;
+				Ref<Texture> image_texture = ResourceLoader::load(path, "Texture", false, &err);
+
+				ERR_CONTINUE_MSG(err != OK, "unable to import image file not loaded yet: " + path);
+				ERR_CONTINUE(image_texture == NULL || image_texture.is_null());
+
+				texture = image_texture;
+				state.cached_image_searches.insert(mapping.name, texture);
+				print_verbose("Created texture from loaded image file.");
+
+			} else if (mapping.texture != nullptr && mapping.texture->Media() != nullptr) {
+				// This is an embedded texture. Extract it.
+				Ref<Image> image;
+				image.instance();
+
+				const String extension = mapping.name.get_extension().to_upper();
+				if (extension == "PNG") {
+
+					// The stored file is a PNG.
+					image = Image::_png_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
+					ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded PNG image load fail.");
+
+				} else if (
+						extension == "JPEG" ||
+						extension == "JPG") {
+
+					// The stored file is a JPEG.
+					image = Image::_jpg_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
+					ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded JPEG image load fail.");
+
+				} else if (extension == "TGA") {
+
+					// The stored file is a TGA.
+					//image = Image::_tga_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
+					//ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded TGA image load fail.");
+
+				} else if (extension == "WEBP") {
+
+					// The stored file is a WEBP.
+					image = Image::_webp_mem_loader_func(mapping.texture->Media()->Content(), mapping.texture->Media()->ContentLength());
+					ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded WEBP image load fail.");
+
+					// } else if (extension == "DDS") {
+					// 	// In this moment is not possible to extract a DDS from a buffer, TODO consider add it to godot. See `textureloader_dds.cpp::load().
+					// 	// The stored file is a DDS.
+				} else {
+					ERR_CONTINUE_MSG(true, "The embedded image with extension: " + extension + " is not yet supported. Open an issue please.");
+				}
+
+				Ref<ImageTexture> image_texture;
+				image_texture.instance();
+				image_texture->create_from_image(image);
+
+				const int32_t flags = Texture::FLAGS_DEFAULT;
+				image_texture->set_flags(flags);
+
+				texture = image_texture;
+				state.cached_image_searches[mapping.name] = texture;
+				print_verbose("Created texture from embedded image.");
+			} else {
+				ERR_CONTINUE_MSG(true, "The FBX texture, with name: `" + mapping.name + "`, is not found into the project nor is stored as embedded file. Make sure to insert the texture as embedded file or into the project, then reimport.");
+			}
+		}
+		if (spatial_material.is_null()) {
+			// Done here so if no textures no material is created.
+			spatial_material.instance();
+		}
+
+		switch (mapping.map_mode) {
+			case SpatialMaterial::TextureParam::TEXTURE_METALLIC:
+				if (mapping.name.to_lower().find("ser") >= 0) {
+					// SER shader.
+					spatial_material->set_metallic_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_RED);
+				} else {
+					// Use grayscale as default.
+					spatial_material->set_metallic_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
+				}
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS:
+				if (mapping.name.to_lower().find("ser") >= 0) {
+					// SER shader.
+					spatial_material->set_roughness_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_BLUE);
+				} else {
+					// Use grayscale as default.
+					spatial_material->set_roughness_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
+				}
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION:
+				// Use grayscale as default.
+				spatial_material->set_ao_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
+				break;
+			case SpatialMaterial::TextureParam::TEXTURE_REFRACTION:
+				// Use grayscale as default.
+				spatial_material->set_refraction_texture_channel(SpatialMaterial::TextureChannel::TEXTURE_CHANNEL_GRAYSCALE);
+				break;
+			default:
+				// Nothing to do.
+				break;
+		}
+
+		spatial_material->set_texture(mapping.map_mode, texture);
+	}
+
+	if (spatial_material.is_valid()) {
+		spatial_material->set_name(material_name);
+	}
+
+	return spatial_material;
+}

+ 233 - 0
modules/fbx/data/fbx_material.h

@@ -0,0 +1,233 @@
+/*************************************************************************/
+/*  fbx_material.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FBX_MATERIAL_H
+#define FBX_MATERIAL_H
+
+#include "tools/import_utils.h"
+
+#include "core/reference.h"
+#include "core/ustring.h"
+
+struct FBXMaterial : public Reference {
+	String material_name = String();
+	mutable const FBXDocParser::Material *material = nullptr;
+
+	/* Godot materials
+	 *** Texture Maps:
+	 * Albedo - color, texture
+	 * Metallic - specular, metallic, texture
+	 * Roughness - roughness, texture
+	 * Emission - color, texture
+	 * Normal Map - scale, texture
+	 * Ambient Occlusion - texture
+	 * Refraction - scale, texture
+	 *** Has Settings for:
+	 * UV1 - SCALE, OFFSET
+	 * UV2 - SCALE, OFFSET
+	 *** Flags for
+	 * Transparent
+	 * Cull Mode
+	 */
+
+	enum class MapMode {
+		AlbedoM = 0,
+		MetallicM,
+		SpecularM,
+		EmissionM,
+		RoughnessM,
+		NormalM,
+		AmbientOcclusionM,
+		RefractionM,
+		ReflectionM,
+	};
+
+	// TODO make this static?
+	const std::map<std::string, SpatialMaterial::Feature> fbx_feature_mapping_desc = {
+		/* Transparent */
+		{ "TransparentColor", SpatialMaterial::Feature::FEATURE_TRANSPARENT },
+		{ "Maya|opacity", SpatialMaterial::Feature::FEATURE_TRANSPARENT }
+	};
+
+	// TODO make this static?
+	const std::map<std::string, SpatialMaterial::TextureParam> fbx_texture_mapping_desc = {
+		/* Diffuse */
+		{ "Maya|base", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		{ "DiffuseColor", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		{ "Maya|DiffuseTexture", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		{ "Maya|baseColor", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		{ "Maya|baseColor|file", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		{ "3dsMax|Parameters|base_color_map", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		{ "Maya|TEX_color_map|file", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		{ "Maya|TEX_color_map", SpatialMaterial::TextureParam::TEXTURE_ALBEDO },
+		/* Emission */
+		{ "EmissiveColor", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
+		{ "EmissiveFactor", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
+		{ "Maya|emissionColor", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
+		{ "Maya|emissionColor|file", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
+		{ "3dsMax|Parameters|emission_map", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
+		{ "Maya|TEX_emissive_map", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
+		{ "Maya|TEX_emissive_map|file", SpatialMaterial::TextureParam::TEXTURE_EMISSION },
+		/* Metallic */
+		{ "Maya|metalness", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "Maya|metalness|file", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "3dsMax|Parameters|metalness_map", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "Maya|TEX_metallic_map", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "Maya|TEX_metallic_map|file", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "SpecularColor", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "Maya|specularColor", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "Maya|SpecularTexture", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "Maya|SpecularTexture|file", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		{ "ShininessExponent", SpatialMaterial::TextureParam::TEXTURE_METALLIC },
+		/* Roughness */
+		{ "Maya|diffuseRoughness", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
+		{ "Maya|diffuseRoughness|file", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
+		{ "3dsMax|Parameters|roughness_map", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
+		{ "Maya|TEX_roughness_map", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
+		{ "Maya|TEX_roughness_map|file", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
+		{ "ReflectionFactor", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
+		{ "Maya|specularRoughness", SpatialMaterial::TextureParam::TEXTURE_ROUGHNESS },
+		/* Normal */
+		{ "NormalMap", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		{ "Bump", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		{ "3dsMax|Parameters|bump_map", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		{ "Maya|NormalTexture", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		{ "Maya|normalCamera", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		{ "Maya|normalCamera|file", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		{ "Maya|TEX_normal_map", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		{ "Maya|TEX_normal_map|file", SpatialMaterial::TextureParam::TEXTURE_NORMAL },
+		/* AO */
+		{ "Maya|TEX_ao_map", SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION },
+		{ "Maya|TEX_ao_map|file", SpatialMaterial::TextureParam::TEXTURE_AMBIENT_OCCLUSION },
+		//	{"TransparentColor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA },
+		//	{"TransparencyFactor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA }
+	};
+
+	// TODO make this static?
+	enum PropertyDesc {
+		PROPERTY_DESC_NOT_FOUND,
+		PROPERTY_DESC_ALBEDO_COLOR,
+		PROPERTY_DESC_TRANSPARENT,
+		PROPERTY_DESC_METALLIC,
+		PROPERTY_DESC_ROUGHNESS,
+		PROPERTY_DESC_COAT,
+		PROPERTY_DESC_COAT_ROUGHNESS,
+		PROPERTY_DESC_EMISSIVE,
+		PROPERTY_DESC_EMISSIVE_COLOR,
+		PROPERTY_DESC_IGNORE
+	};
+
+	const std::map<std::string, PropertyDesc> fbx_properties_desc = {
+		/* Albedo */
+		{ "DiffuseColor", PROPERTY_DESC_ALBEDO_COLOR },
+		{ "Maya|baseColor", PROPERTY_DESC_ALBEDO_COLOR },
+
+		/* Transparent */
+		{ "Opacity", PROPERTY_DESC_TRANSPARENT },
+		{ "TransparencyFactor", PROPERTY_DESC_TRANSPARENT },
+		{ "Maya|opacity", PROPERTY_DESC_TRANSPARENT },
+
+		/* Metallic */
+		{ "Shininess", PROPERTY_DESC_METALLIC },
+		{ "Reflectivity", PROPERTY_DESC_METALLIC },
+		{ "Maya|metalness", PROPERTY_DESC_METALLIC },
+
+		/* Roughness */
+		{ "Maya|diffuseRoughness", PROPERTY_DESC_ROUGHNESS },
+
+		/* Coat */
+		{ "Maya|coat", PROPERTY_DESC_COAT },
+
+		/* Coat roughness */
+		{ "Maya|coatRoughness", PROPERTY_DESC_COAT_ROUGHNESS },
+
+		/* Emissive */
+		{ "Maya|emission", PROPERTY_DESC_EMISSIVE },
+
+		/* Emissive color */
+		{ "EmissiveColor", PROPERTY_DESC_EMISSIVE_COLOR },
+		{ "Maya|emissionColor", PROPERTY_DESC_EMISSIVE_COLOR },
+
+		/* Ignore */
+		{ "Maya", PROPERTY_DESC_IGNORE },
+		{ "Diffuse", PROPERTY_DESC_IGNORE },
+		{ "Maya|TypeId", PROPERTY_DESC_IGNORE },
+		{ "Ambient", PROPERTY_DESC_IGNORE },
+		{ "AmbientColor", PROPERTY_DESC_IGNORE },
+		{ "ShininessExponent", PROPERTY_DESC_IGNORE },
+		{ "Specular", PROPERTY_DESC_IGNORE },
+		{ "SpecularColor", PROPERTY_DESC_IGNORE },
+		{ "SpecularFactor", PROPERTY_DESC_IGNORE },
+		//{ "BumpFactor", PROPERTY_DESC_IGNORE },
+		{ "Maya|exitToBackground", PROPERTY_DESC_IGNORE },
+		{ "Maya|indirectDiffuse", PROPERTY_DESC_IGNORE },
+		{ "Maya|indirectSpecular", PROPERTY_DESC_IGNORE },
+		{ "Maya|internalReflections", PROPERTY_DESC_IGNORE },
+		{ "DiffuseFactor", PROPERTY_DESC_IGNORE },
+		{ "AmbientFactor", PROPERTY_DESC_IGNORE },
+		{ "ReflectionColor", PROPERTY_DESC_IGNORE },
+		{ "Emissive", PROPERTY_DESC_IGNORE },
+		{ "Maya|coatColor", PROPERTY_DESC_IGNORE },
+		{ "Maya|coatNormal", PROPERTY_DESC_IGNORE },
+		{ "Maya|coatIOR", PROPERTY_DESC_IGNORE },
+	};
+
+	struct TextureFileMapping {
+		SpatialMaterial::TextureParam map_mode = SpatialMaterial::TEXTURE_ALBEDO;
+		String name = String();
+		const FBXDocParser::Texture *texture = nullptr;
+	};
+
+	/* storing the texture properties like color */
+	template <class T>
+	struct TexturePropertyMapping : Reference {
+		SpatialMaterial::TextureParam map_mode = SpatialMaterial::TextureParam::TEXTURE_ALBEDO;
+		const T property = T();
+	};
+
+	static void add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths);
+
+	static String find_texture_path_by_filename(const String p_filename, const String p_current_directory);
+
+	String get_material_name() const;
+
+	void set_imported_material(const FBXDocParser::Material *p_material);
+
+	struct MaterialInfo {
+		Vector<TextureFileMapping> textures;
+		Vector<SpatialMaterial::Feature> features;
+	};
+	/// Extracts the material information.
+	MaterialInfo extract_material_info(const FBXDocParser::Material *material) const;
+
+	Ref<SpatialMaterial> import_material(ImportState &state);
+};
+
+#endif // FBX_MATERIAL_H

+ 1366 - 0
modules/fbx/data/fbx_mesh_data.cpp

@@ -0,0 +1,1366 @@
+/*************************************************************************/
+/*  fbx_mesh_data.cpp                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "fbx_mesh_data.h"
+
+#include "core/local_vector.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/surface_tool.h"
+
+#include "thirdparty/misc/triangulator.h"
+
+template <class T>
+T collect_first(const Vector<VertexData<T> > *p_data, T p_fall_back) {
+	if (p_data->empty()) {
+		return p_fall_back;
+	}
+
+	return (*p_data)[0].data;
+}
+
+template <class T>
+HashMap<int, T> collect_all(const Vector<VertexData<T> > *p_data, HashMap<int, T> p_fall_back) {
+	if (p_data->empty()) {
+		return p_fall_back;
+	}
+
+	HashMap<int, T> collection;
+	for (int i = 0; i < p_data->size(); i += 1) {
+		const VertexData<T> &vd = (*p_data)[i];
+		collection[vd.polygon_index] = vd.data;
+	}
+	return collection;
+}
+
+template <class T>
+T collect_average(const Vector<VertexData<T> > *p_data, T p_fall_back) {
+	if (p_data->empty()) {
+		return p_fall_back;
+	}
+
+	T combined = (*p_data)[0].data; // Make sure the data is always correctly initialized.
+	print_verbose("size of data: " + itos(p_data->size()));
+	for (int i = 1; i < p_data->size(); i += 1) {
+		combined += (*p_data)[i].data;
+	}
+	combined = combined / real_t(p_data->size());
+
+	return combined.normalized();
+}
+
+HashMap<int, Vector3> collect_normal(const Vector<VertexData<Vector3> > *p_data, HashMap<int, Vector3> p_fall_back) {
+	if (p_data->empty()) {
+		return p_fall_back;
+	}
+
+	HashMap<int, Vector3> collection;
+	for (int i = 0; i < p_data->size(); i += 1) {
+		const VertexData<Vector3> &vd = (*p_data)[i];
+		collection[vd.polygon_index] = vd.data;
+	}
+	return collection;
+}
+
+HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2> > *p_data, HashMap<int, Vector2> p_fall_back) {
+	if (p_data->empty()) {
+		return p_fall_back;
+	}
+
+	HashMap<int, Vector2> collection;
+	for (int i = 0; i < p_data->size(); i += 1) {
+		const VertexData<Vector2> &vd = (*p_data)[i];
+		collection[vd.polygon_index] = vd.data;
+	}
+	return collection;
+}
+
+typedef int Vertex;
+typedef int SurfaceId;
+typedef int PolygonId;
+typedef int DataIndex;
+
+struct SurfaceData {
+	Ref<SurfaceTool> surface_tool;
+	OrderedHashMap<Vertex, int> lookup_table; // proposed fix is to replace lookup_table[vertex_id] to give the position of the vertices_map[int] index.
+	LocalVector<Vertex> vertices_map; // this must be ordered the same as insertion <-- slow to do find() operation.
+	Ref<SpatialMaterial> material;
+	HashMap<PolygonId, Vector<DataIndex> > surface_polygon_vertex;
+	Array morphs;
+};
+
+MeshInstance *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *mesh_geometry, const FBXDocParser::Model *model) {
+
+	// todo: make this just use a uint64_t FBX ID this is a copy of our original materials unfortunately.
+	const std::vector<const FBXDocParser::Material *> &material_lookup = model->GetMaterials();
+
+	std::vector<int> polygon_indices = mesh_geometry->get_polygon_indices();
+	std::vector<Vector3> vertices = mesh_geometry->get_vertices();
+
+	// Phase 1. Parse all FBX data.
+	HashMap<int, Vector3> normals;
+	HashMap<int, HashMap<int, Vector3> > normals_raw = extract_per_vertex_data(
+			vertices.size(),
+			mesh_geometry->get_edge_map(),
+			polygon_indices,
+			mesh_geometry->get_normals(),
+			&collect_all,
+			HashMap<int, Vector3>());
+
+	//	List<int> keys;
+	//	normals.get_key_list(&keys);
+	//
+	//	const std::vector<Assimp::FBX::MeshGeometry::Edge>& edges = mesh_geometry->get_edge_map();
+	//	for (int index = 0; index < keys.size(); index++) {
+	//		const int key = keys[index];
+	//		const int v1 = edges[key].vertex_0;
+	//		const int v2 = edges[key].vertex_1;
+	//		const Vector3& n1 = normals.get(v1);
+	//		const Vector3& n2 = normals.get(v2);
+	//		print_verbose("[" + itos(v1) + "] n1: " + n1 + "\n[" + itos(v2) + "] n2: " + n2);
+	//		//print_verbose("[" + itos(key) + "] n1: " + n1 + ", n2: " + n2) ;
+	//		//print_verbose("vindex: " + itos(edges[key].vertex_0) + ", vindex2: " + itos(edges[key].vertex_1));
+	//		//Vector3 ver1 = vertices[edges[key].vertex_0];
+	//		//Vector3 ver2 = vertices[edges[key].vertex_1];
+	//		/*real_t angle1 = Math::rad2deg(n1.angle_to(n2));
+	//		real_t angle2 = Math::rad2deg(n2.angle_to(n1));
+	//		print_verbose("angle of normals: " + rtos(angle1) + " angle 2" + rtos(angle2));*/
+	//	}
+
+	HashMap<int, Vector2> uvs_0;
+	HashMap<int, HashMap<int, Vector2> > uvs_0_raw = extract_per_vertex_data(
+			vertices.size(),
+			mesh_geometry->get_edge_map(),
+			polygon_indices,
+			mesh_geometry->get_uv_0(),
+			&collect_all,
+			HashMap<int, Vector2>());
+
+	HashMap<int, Vector2> uvs_1;
+	HashMap<int, HashMap<int, Vector2> > uvs_1_raw = extract_per_vertex_data(
+			vertices.size(),
+			mesh_geometry->get_edge_map(),
+			polygon_indices,
+			mesh_geometry->get_uv_1(),
+			&collect_all,
+			HashMap<int, Vector2>());
+
+	HashMap<int, Color> colors = extract_per_vertex_data(
+			vertices.size(),
+			mesh_geometry->get_edge_map(),
+			polygon_indices,
+			mesh_geometry->get_colors(),
+			&collect_first,
+			Color());
+
+	// TODO what about tangents?
+	// TODO what about bi-nomials?
+	// TODO there is other?
+
+	HashMap<int, SurfaceId> polygon_surfaces = extract_per_polygon(
+			vertices.size(),
+			polygon_indices,
+			mesh_geometry->get_material_allocation_id(),
+			-1);
+
+	HashMap<String, MorphVertexData> morphs;
+	extract_morphs(mesh_geometry, morphs);
+
+	// TODO please add skinning.
+	//mesh_id = mesh_geometry->ID();
+
+	sanitize_vertex_weights();
+
+	// Re organize polygon vertices to to correctly take into account strange
+	// UVs.
+	reorganize_vertices(
+			polygon_indices,
+			vertices,
+			normals,
+			uvs_0,
+			uvs_1,
+			colors,
+			morphs,
+			normals_raw,
+			uvs_0_raw,
+			uvs_1_raw);
+
+	// Make sure that from this moment on the mesh_geometry is no used anymore.
+	// This is a safety step, because the mesh_geometry data are no more valid
+	// at this point.
+	mesh_geometry = nullptr;
+
+	const int vertex_count = vertices.size();
+
+	// The map key is the material allocator id that is also used as surface id.
+	HashMap<SurfaceId, SurfaceData> surfaces;
+
+	// Phase 2. For each material create a surface tool (So a different mesh).
+	{
+		if (polygon_surfaces.empty()) {
+			// No material, just use the default one with index -1.
+			// Set -1 to all polygons.
+			const int polygon_count = count_polygons(polygon_indices);
+			for (int p = 0; p < polygon_count; p += 1) {
+				polygon_surfaces[p] = -1;
+			}
+		}
+
+		// Create the surface now.
+		for (const int *polygon_id = polygon_surfaces.next(nullptr); polygon_id != nullptr; polygon_id = polygon_surfaces.next(polygon_id)) {
+			const int surface_id = polygon_surfaces[*polygon_id];
+			if (surfaces.has(surface_id) == false) {
+				SurfaceData sd;
+				sd.surface_tool.instance();
+				sd.surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+				if (surface_id < 0) {
+					// nothing to do
+				} else if (surface_id < (int)material_lookup.size()) {
+					const FBXDocParser::Material *mat_mapping = material_lookup.at(surface_id);
+					const uint64_t mapping_id = mat_mapping->ID();
+					if (state.cached_materials.has(mapping_id)) {
+						sd.material = state.cached_materials[mapping_id];
+					}
+				} else {
+					WARN_PRINT("out of bounds surface detected, FBX file has corrupt material data");
+				}
+
+				surfaces.set(surface_id, sd);
+			}
+		}
+	}
+
+	// Phase 3. Map the vertices relative to each surface, in this way we can
+	// just insert the vertices that we need per each surface.
+	{
+		PolygonId polygon_index = -1;
+		SurfaceId surface_id = -1;
+		SurfaceData *surface_data = nullptr;
+
+		for (size_t polygon_vertex = 0; polygon_vertex < polygon_indices.size(); polygon_vertex += 1) {
+			if (is_start_of_polygon(polygon_indices, polygon_vertex)) {
+				polygon_index += 1;
+				ERR_FAIL_COND_V_MSG(polygon_surfaces.has(polygon_index) == false, nullptr, "The FBX file is currupted, This surface_index is not expected.");
+				surface_id = polygon_surfaces[polygon_index];
+				surface_data = surfaces.getptr(surface_id);
+				CRASH_COND(surface_data == nullptr); // Can't be null.
+			}
+
+			const int vertex = get_vertex_from_polygon_vertex(polygon_indices, polygon_vertex);
+
+			// The vertex position in the surface
+			// Uses a lookup table for speed with large scenes
+			bool has_polygon_vertex_index = surface_data->lookup_table.has(vertex);
+			int surface_polygon_vertex_index = -1;
+
+			if (has_polygon_vertex_index) {
+				surface_polygon_vertex_index = surface_data->lookup_table[vertex];
+			} else {
+				surface_polygon_vertex_index = surface_data->vertices_map.size();
+				surface_data->lookup_table[vertex] = surface_polygon_vertex_index;
+				surface_data->vertices_map.push_back(vertex);
+			}
+
+			surface_data->surface_polygon_vertex[polygon_index].push_back(surface_polygon_vertex_index);
+		}
+	}
+
+	//print_verbose("[debug UV 1] UV1: " + itos(uvs_0.size()));
+	//print_verbose("[debug UV 2] UV2: " + itos(uvs_1.size()));
+
+	// Phase 4. Per each surface just insert the vertices and add the indices.
+	for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) {
+		SurfaceData *surface = surfaces.getptr(*surface_id);
+
+		// Just add the vertices data.
+		for (unsigned int i = 0; i < surface->vertices_map.size(); i += 1) {
+			const Vertex vertex = surface->vertices_map[i];
+
+			// This must be done before add_vertex because the surface tool is
+			// expecting this before the st->add_vertex() call
+			add_vertex(
+					surface->surface_tool,
+					state.scale,
+					vertex,
+					vertices,
+					normals,
+					uvs_0,
+					uvs_1,
+					colors);
+		}
+
+		// Triangulate the various polygons and add the indices.
+		for (const PolygonId *polygon_id = surface->surface_polygon_vertex.next(nullptr); polygon_id != nullptr; polygon_id = surface->surface_polygon_vertex.next(polygon_id)) {
+			const Vector<DataIndex> *indices = surface->surface_polygon_vertex.getptr(*polygon_id);
+
+			triangulate_polygon(
+					surface->surface_tool,
+					*indices,
+					surface->vertices_map,
+					vertices);
+		}
+	}
+
+	// Phase 5. Compose the morphs if any.
+	for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) {
+		SurfaceData *surface = surfaces.getptr(*surface_id);
+
+		for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) {
+			MorphVertexData *morph_data = morphs.getptr(*morph_name);
+
+			// As said by the docs, this is not supposed to be different than
+			// vertex_count.
+			CRASH_COND(morph_data->vertices.size() != vertex_count);
+			CRASH_COND(morph_data->normals.size() != vertex_count);
+
+			Vector3 *vertices_ptr = morph_data->vertices.ptrw();
+			Vector3 *normals_ptr = morph_data->normals.ptrw();
+
+			Ref<SurfaceTool> morph_st;
+			morph_st.instance();
+			morph_st->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+			for (unsigned int vi = 0; vi < surface->vertices_map.size(); vi += 1) {
+				const Vertex vertex = surface->vertices_map[vi];
+				add_vertex(
+						morph_st,
+						state.scale,
+						vertex,
+						vertices,
+						normals,
+						uvs_0,
+						uvs_1,
+						colors,
+						vertices_ptr[vertex],
+						normals_ptr[vertex]);
+			}
+
+			morph_st->generate_tangents();
+			surface->morphs.push_back(morph_st->commit_to_arrays());
+		}
+	}
+
+	// Phase 6. Compose the mesh and return it.
+	Ref<ArrayMesh> mesh;
+	mesh.instance();
+
+	// Add blend shape info.
+	for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) {
+		mesh->add_blend_shape(*morph_name);
+	}
+
+	// TODO always normalized, Why?
+	mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
+
+	// Add surfaces.
+	int in_mesh_surface_id = 0;
+	for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) {
+		SurfaceData *surface = surfaces.getptr(*surface_id);
+
+		surface->surface_tool->generate_tangents();
+
+		mesh->add_surface_from_arrays(
+				Mesh::PRIMITIVE_TRIANGLES,
+				surface->surface_tool->commit_to_arrays(),
+				surface->morphs);
+
+		if (surface->material.is_valid()) {
+			mesh->surface_set_name(in_mesh_surface_id, surface->material->get_name());
+			mesh->surface_set_material(in_mesh_surface_id, surface->material);
+		}
+
+		in_mesh_surface_id += 1;
+	}
+
+	MeshInstance *godot_mesh = memnew(MeshInstance);
+	godot_mesh->set_mesh(mesh);
+	return godot_mesh;
+}
+
+void FBXMeshData::sanitize_vertex_weights() {
+	const int max_bones = VS::ARRAY_WEIGHTS_SIZE;
+
+	for (const Vertex *v = vertex_weights.next(nullptr); v != nullptr; v = vertex_weights.next(v)) {
+		VertexWeightMapping *vm = vertex_weights.getptr(*v);
+		ERR_CONTINUE(vm->bones.size() != vm->weights.size()); // No message, already checked.
+		ERR_CONTINUE(vm->bones_ref.size() != vm->weights.size()); // No message, already checked.
+
+		const int initial_size = vm->weights.size();
+
+		{
+			// Init bone id
+			int *bones_ptr = vm->bones.ptrw();
+			Ref<FBXBone> *bones_ref_ptr = vm->bones_ref.ptrw();
+
+			for (int i = 0; i < vm->weights.size(); i += 1) {
+				// At this point this is not possible because the skeleton is already initialized.
+				CRASH_COND(bones_ref_ptr[i]->godot_bone_id == -2);
+				bones_ptr[i] = bones_ref_ptr[i]->godot_bone_id;
+			}
+
+			// From this point on the data is no more valid.
+			vm->bones_ref.clear();
+		}
+
+		{
+			// Sort
+			real_t *weights_ptr = vm->weights.ptrw();
+			int *bones_ptr = vm->bones.ptrw();
+			for (int i = 0; i < vm->weights.size(); i += 1) {
+				for (int x = i + 1; x < vm->weights.size(); x += 1) {
+					if (weights_ptr[i] < weights_ptr[x]) {
+						SWAP(weights_ptr[i], weights_ptr[x]);
+						SWAP(bones_ptr[i], bones_ptr[x]);
+					}
+				}
+			}
+		}
+
+		{
+			// Resize
+			vm->weights.resize(max_bones);
+			vm->bones.resize(max_bones);
+			real_t *weights_ptr = vm->weights.ptrw();
+			int *bones_ptr = vm->bones.ptrw();
+			for (int i = initial_size; i < max_bones; i += 1) {
+				weights_ptr[i] = 0.0;
+				bones_ptr[i] = 0;
+			}
+
+			// Normalize
+			real_t sum = 0.0;
+			for (int i = 0; i < max_bones; i += 1) {
+				sum += weights_ptr[i];
+			}
+			if (sum > 0.0) {
+				for (int i = 0; i < vm->weights.size(); i += 1) {
+					weights_ptr[i] = weights_ptr[i] / sum;
+				}
+			}
+		}
+	}
+}
+
+void FBXMeshData::reorganize_vertices(
+		std::vector<int> &r_polygon_indices,
+		std::vector<Vector3> &r_vertices,
+		HashMap<int, Vector3> &r_normals,
+		HashMap<int, Vector2> &r_uv_1,
+		HashMap<int, Vector2> &r_uv_2,
+		HashMap<int, Color> &r_color,
+		HashMap<String, MorphVertexData> &r_morphs,
+		HashMap<int, HashMap<int, Vector3> > &r_normals_raw,
+		HashMap<int, HashMap<int, Vector2> > &r_uv_1_raw,
+		HashMap<int, HashMap<int, Vector2> > &r_uv_2_raw) {
+
+	// Key: OldVertex; Value: [New vertices];
+	HashMap<int, Vector<int> > duplicated_vertices;
+
+	PolygonId polygon_index = -1;
+	for (int pv = 0; pv < (int)r_polygon_indices.size(); pv += 1) {
+		if (is_start_of_polygon(r_polygon_indices, pv)) {
+			polygon_index += 1;
+		}
+		const Vertex index = get_vertex_from_polygon_vertex(r_polygon_indices, pv);
+
+		bool need_duplication = false;
+		Vector2 this_vert_poly_uv1 = Vector2();
+		Vector2 this_vert_poly_uv2 = Vector2();
+		Vector3 this_vert_poly_normal = Vector3();
+
+		// Take the normal and see if we need to duplicate this polygon.
+		if (r_normals_raw.has(index)) {
+			const HashMap<PolygonId, Vector3> *nrml_arr = r_normals_raw.getptr(index);
+			if (nrml_arr->has(polygon_index)) {
+				this_vert_poly_normal = nrml_arr->get(polygon_index);
+			}
+			// Now, check if we need to duplicate it.
+			for (const PolygonId *pid = nrml_arr->next(nullptr); pid != nullptr; pid = nrml_arr->next(pid)) {
+				if (*pid == polygon_index) {
+					continue;
+				}
+
+				const Vector3 vert_poly_normal = *nrml_arr->getptr(*pid);
+				if ((this_vert_poly_normal - vert_poly_normal).length_squared() > CMP_EPSILON) {
+					// Yes this polygon need duplication.
+					need_duplication = true;
+					break;
+				}
+			}
+		}
+
+		// Take the UV1 and UV2 and see if we need to duplicate this polygon.
+		{
+			HashMap<int, HashMap<int, Vector2> > *uv_raw = &r_uv_1_raw;
+			Vector2 *this_vert_poly_uv = &this_vert_poly_uv1;
+			for (int kk = 0; kk < 2; kk++) {
+
+				if (uv_raw->has(index)) {
+					const HashMap<PolygonId, Vector2> *uvs = uv_raw->getptr(index);
+
+					if (uvs->has(polygon_index)) {
+						// This Polygon has its own uv.
+						(*this_vert_poly_uv) = *uvs->getptr(polygon_index);
+
+						// Check if we need to duplicate it.
+						for (const PolygonId *pid = uvs->next(nullptr); pid != nullptr; pid = uvs->next(pid)) {
+							if (*pid == polygon_index) {
+								continue;
+							}
+							const Vector2 vert_poly_uv = *uvs->getptr(*pid);
+							if (((*this_vert_poly_uv) - vert_poly_uv).length_squared() > CMP_EPSILON) {
+								// Yes this polygon need duplication.
+								need_duplication = true;
+								break;
+							}
+						}
+					} else if (uvs->has(-1)) {
+						// It has the default UV.
+						(*this_vert_poly_uv) = *uvs->getptr(-1);
+					} else if (uvs->size() > 0) {
+						// No uv, this is strange, just take the first and duplicate.
+						(*this_vert_poly_uv) = *uvs->getptr(*uvs->next(nullptr));
+						WARN_PRINT("No UVs for this polygon, while there is no default and some other polygons have it. This FBX file may be corrupted.");
+					}
+				}
+				uv_raw = &r_uv_2_raw;
+				this_vert_poly_uv = &this_vert_poly_uv2;
+			}
+		}
+
+		// If we want to duplicate it, Let's see if we already duplicated this
+		// vertex.
+		if (need_duplication) {
+			if (duplicated_vertices.has(index)) {
+				Vertex similar_vertex = -1;
+				// Let's see if one of the new vertices has the same data of this.
+				const Vector<int> *new_vertices = duplicated_vertices.getptr(index);
+				for (int j = 0; j < new_vertices->size(); j += 1) {
+					const Vertex new_vertex = (*new_vertices)[j];
+					bool same_uv1 = false;
+					bool same_uv2 = false;
+					bool same_normal = false;
+
+					if (r_uv_1.has(new_vertex)) {
+						if ((this_vert_poly_uv1 - (*r_uv_1.getptr(new_vertex))).length_squared() <= CMP_EPSILON) {
+							same_uv1 = true;
+						}
+					}
+
+					if (r_uv_2.has(new_vertex)) {
+						if ((this_vert_poly_uv2 - (*r_uv_2.getptr(new_vertex))).length_squared() <= CMP_EPSILON) {
+							same_uv2 = true;
+						}
+					}
+
+					if (r_normals.has(new_vertex)) {
+						if ((this_vert_poly_normal - (*r_normals.getptr(new_vertex))).length_squared() <= CMP_EPSILON) {
+							same_uv2 = true;
+						}
+					}
+
+					if (same_uv1 && same_uv2 && same_normal) {
+						similar_vertex = new_vertex;
+						break;
+					}
+				}
+
+				if (similar_vertex != -1) {
+					// Update polygon.
+					if (is_end_of_polygon(r_polygon_indices, pv)) {
+						r_polygon_indices[pv] = ~similar_vertex;
+					} else {
+						r_polygon_indices[pv] = similar_vertex;
+					}
+					need_duplication = false;
+				}
+			}
+		}
+
+		if (need_duplication) {
+			const Vertex old_index = index;
+			const Vertex new_index = r_vertices.size();
+
+			// Polygon index.
+			if (is_end_of_polygon(r_polygon_indices, pv)) {
+				r_polygon_indices[pv] = ~new_index;
+			} else {
+				r_polygon_indices[pv] = new_index;
+			}
+
+			// Vertex position.
+			r_vertices.push_back(r_vertices[old_index]);
+
+			// Normals
+			if (r_normals_raw.has(old_index)) {
+				r_normals.set(new_index, this_vert_poly_normal);
+				r_normals_raw.getptr(old_index)->erase(polygon_index);
+				r_normals_raw[new_index][polygon_index] = this_vert_poly_normal;
+			}
+
+			// UV 0
+			if (r_uv_1_raw.has(old_index)) {
+				r_uv_1.set(new_index, this_vert_poly_uv1);
+				r_uv_1_raw.getptr(old_index)->erase(polygon_index);
+				r_uv_1_raw[new_index][polygon_index] = this_vert_poly_uv1;
+			}
+
+			// UV 1
+			if (r_uv_2_raw.has(old_index)) {
+				r_uv_2.set(new_index, this_vert_poly_uv2);
+				r_uv_2_raw.getptr(old_index)->erase(polygon_index);
+				r_uv_2_raw[new_index][polygon_index] = this_vert_poly_uv2;
+			}
+
+			// Vertex color.
+			if (r_color.has(old_index)) {
+				r_color[new_index] = r_color[old_index];
+			}
+
+			// Morphs
+			for (const String *mname = r_morphs.next(nullptr); mname != nullptr; mname = r_morphs.next(mname)) {
+				MorphVertexData *d = r_morphs.getptr(*mname);
+				// This can't never happen.
+				CRASH_COND(d == nullptr);
+				if (d->vertices.size() > old_index) {
+					d->vertices.push_back(d->vertices[old_index]);
+				}
+				if (d->normals.size() > old_index) {
+					d->normals.push_back(d->normals[old_index]);
+				}
+			}
+
+			if (vertex_weights.has(old_index)) {
+				vertex_weights.set(new_index, vertex_weights[old_index]);
+			}
+
+			duplicated_vertices[old_index].push_back(new_index);
+		} else {
+			if (r_normals_raw.has(index) &&
+					r_normals.has(index) == false) {
+				r_normals.set(index, this_vert_poly_normal);
+			}
+
+			if (r_uv_1_raw.has(index) &&
+					r_uv_1.has(index) == false) {
+				r_uv_1.set(index, this_vert_poly_uv1);
+			}
+
+			if (r_uv_2_raw.has(index) &&
+					r_uv_2.has(index) == false) {
+				r_uv_2.set(index, this_vert_poly_uv2);
+			}
+		}
+	}
+}
+
+void FBXMeshData::add_vertex(
+		Ref<SurfaceTool> p_surface_tool,
+		real_t p_scale,
+		Vertex p_vertex,
+		const std::vector<Vector3> &p_vertices_position,
+		const HashMap<int, Vector3> &p_normals,
+		const HashMap<int, Vector2> &p_uvs_0,
+		const HashMap<int, Vector2> &p_uvs_1,
+		const HashMap<int, Color> &p_colors,
+		const Vector3 &p_morph_value,
+		const Vector3 &p_morph_normal) {
+
+	ERR_FAIL_INDEX_MSG(p_vertex, (Vertex)p_vertices_position.size(), "FBX file is corrupted, the position of the vertex can't be retrieved.");
+
+	if (p_normals.has(p_vertex)) {
+		p_surface_tool->add_normal(p_normals[p_vertex] + p_morph_normal);
+	}
+
+	if (p_uvs_0.has(p_vertex)) {
+		//print_verbose("uv1: [" + itos(p_vertex) + "] " + p_uvs_0[p_vertex]);
+		// Inverts Y UV.
+		p_surface_tool->add_uv(Vector2(p_uvs_0[p_vertex].x, 1 - p_uvs_0[p_vertex].y));
+	}
+
+	if (p_uvs_1.has(p_vertex)) {
+		//print_verbose("uv2: [" + itos(p_vertex) + "] " + p_uvs_1[p_vertex]);
+		// Inverts Y UV.
+		p_surface_tool->add_uv2(Vector2(p_uvs_1[p_vertex].x, 1 - p_uvs_1[p_vertex].y));
+	}
+
+	if (p_colors.has(p_vertex)) {
+		p_surface_tool->add_color(p_colors[p_vertex]);
+	}
+
+	// TODO what about binormals?
+	// TODO there is other?
+
+	gen_weight_info(p_surface_tool, p_vertex);
+
+	// The surface tool want the vertex position as last thing.
+	p_surface_tool->add_vertex((p_vertices_position[p_vertex] + p_morph_value) * p_scale);
+}
+
+void FBXMeshData::triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, const Vector<Vertex> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const {
+	const int polygon_vertex_count = p_polygon_vertex.size();
+	if (polygon_vertex_count == 1) {
+		// point to triangle
+		st->add_index(p_polygon_vertex[0]);
+		st->add_index(p_polygon_vertex[0]);
+		st->add_index(p_polygon_vertex[0]);
+		return;
+	} else if (polygon_vertex_count == 2) {
+		// line to triangle
+		st->add_index(p_polygon_vertex[1]);
+		st->add_index(p_polygon_vertex[1]);
+		st->add_index(p_polygon_vertex[0]);
+		return;
+	} else if (polygon_vertex_count == 3) {
+		// triangle to triangle
+		st->add_index(p_polygon_vertex[0]);
+		st->add_index(p_polygon_vertex[2]);
+		st->add_index(p_polygon_vertex[1]);
+		return;
+	} else if (polygon_vertex_count == 4) {
+		// quad to triangle - this code is awesome for import times
+		// it prevents triangles being generated slowly
+		st->add_index(p_polygon_vertex[0]);
+		st->add_index(p_polygon_vertex[2]);
+		st->add_index(p_polygon_vertex[1]);
+		st->add_index(p_polygon_vertex[2]);
+		st->add_index(p_polygon_vertex[0]);
+		st->add_index(p_polygon_vertex[3]);
+		return;
+	} else {
+		// non triangulated - we must run the triangulation algorithm
+		bool is_simple_convex = false;
+		// this code is 'slow' but required it triangulates all the unsupported geometry.
+		// Doesn't allow for bigger polygons because those are unlikely be convex
+		if (polygon_vertex_count <= 6) {
+			// Start from true, check if it's false.
+			is_simple_convex = true;
+			Vector3 first_vec;
+			for (int i = 0; i < polygon_vertex_count; i += 1) {
+				const Vector3 p1 = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]];
+				const Vector3 p2 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 1) % polygon_vertex_count]]];
+				const Vector3 p3 = p_vertices[p_surface_vertex_map[p_polygon_vertex[(i + 2) % polygon_vertex_count]]];
+
+				const Vector3 edge1 = p1 - p2;
+				const Vector3 edge2 = p3 - p2;
+
+				const Vector3 res = edge1.normalized().cross(edge2.normalized()).normalized();
+				if (i == 0) {
+					first_vec = res;
+				} else {
+					if (first_vec.dot(res) < 0.0) {
+						// Ok we found an angle that is not the same dir of the
+						// others.
+						is_simple_convex = false;
+						break;
+					}
+				}
+			}
+		}
+
+		if (is_simple_convex) {
+			// This is a convex polygon, so just triangulate it.
+			for (int i = 0; i < (polygon_vertex_count - 2); i += 1) {
+				st->add_index(p_polygon_vertex[2 + i]);
+				st->add_index(p_polygon_vertex[1 + i]);
+				st->add_index(p_polygon_vertex[0]);
+			}
+			return;
+		}
+	}
+
+	{
+		// This is a concave polygon.
+
+		std::vector<Vector3> poly_vertices(polygon_vertex_count);
+		for (int i = 0; i < polygon_vertex_count; i += 1) {
+			poly_vertices[i] = p_vertices[p_surface_vertex_map[p_polygon_vertex[i]]];
+		}
+
+		const Vector3 poly_norm = get_poly_normal(poly_vertices);
+		if (poly_norm.length_squared() <= CMP_EPSILON) {
+			ERR_FAIL_COND_MSG(poly_norm.length_squared() <= CMP_EPSILON, "The normal of this poly was not computed. Is this FBX file corrupted.");
+		}
+
+		// Select the plan coordinate.
+		int axis_1_coord = 0;
+		int axis_2_coord = 1;
+		{
+			real_t inv = poly_norm.z;
+
+			const real_t axis_x = ABS(poly_norm.x);
+			const real_t axis_y = ABS(poly_norm.y);
+			const real_t axis_z = ABS(poly_norm.z);
+
+			if (axis_x > axis_y) {
+				if (axis_x > axis_z) {
+					// For the most part the normal point toward X.
+					axis_1_coord = 1;
+					axis_2_coord = 2;
+					inv = poly_norm.x;
+				}
+			} else if (axis_y > axis_z) {
+				// For the most part the normal point toward Y.
+				axis_1_coord = 2;
+				axis_2_coord = 0;
+				inv = poly_norm.y;
+			}
+
+			// Swap projection axes to take the negated projection vector into account
+			if (inv < 0.0f) {
+				SWAP(axis_1_coord, axis_2_coord);
+			}
+		}
+
+		TriangulatorPoly triangulator_poly;
+		triangulator_poly.Init(polygon_vertex_count);
+		std::vector<Vector2> projected_vertices(polygon_vertex_count);
+		for (int i = 0; i < polygon_vertex_count; i += 1) {
+			const Vector2 pv(poly_vertices[i][axis_1_coord], poly_vertices[i][axis_2_coord]);
+			projected_vertices[i] = pv;
+			triangulator_poly.GetPoint(i) = pv;
+		}
+		triangulator_poly.SetOrientation(TRIANGULATOR_CCW);
+
+		List<TriangulatorPoly> out_poly;
+
+		TriangulatorPartition triangulator_partition;
+		if (triangulator_partition.Triangulate_OPT(&triangulator_poly, &out_poly) == 0) { // Good result.
+			if (triangulator_partition.Triangulate_EC(&triangulator_poly, &out_poly) == 0) { // Medium result.
+				if (triangulator_partition.Triangulate_MONO(&triangulator_poly, &out_poly) == 0) { // Really poor result.
+					ERR_FAIL_MSG("The triangulation of this polygon failed, please try to triangulate your mesh or check if it has broken polygons.");
+				}
+			}
+		}
+
+		std::vector<Vector2> tris(out_poly.size());
+		for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) {
+			TriangulatorPoly &tp = I->get();
+
+			ERR_FAIL_COND_MSG(tp.GetNumPoints() != 3, "The triangulator retuned more points, how this is possible?");
+			// Find Index
+			for (int i = 2; i >= 0; i -= 1) {
+				const Vector2 vertex = tp.GetPoint(i);
+				bool done = false;
+				// Find Index
+				for (int y = 0; y < polygon_vertex_count; y += 1) {
+					if ((projected_vertices[y] - vertex).length_squared() <= CMP_EPSILON) {
+						// This seems the right vertex
+						st->add_index(p_polygon_vertex[y]);
+						done = true;
+						break;
+					}
+				}
+				ERR_FAIL_COND(done == false);
+			}
+		}
+	}
+}
+
+void FBXMeshData::gen_weight_info(Ref<SurfaceTool> st, Vertex vertex_id) const {
+	if (vertex_weights.empty()) {
+		return;
+	}
+
+	if (vertex_weights.has(vertex_id)) {
+		// Let's extract the weight info.
+		const VertexWeightMapping *vm = vertex_weights.getptr(vertex_id);
+		st->add_weights(vm->weights);
+		st->add_bones(vm->bones);
+		print_verbose("[doc] Triangle added weights to mesh for bones");
+	} else {
+		// This vertex doesn't have any bone info, while the model is using the
+		// bones.
+		// So nothing more to do.
+	}
+
+	print_verbose("[doc] Triangle added weights to mesh for bones");
+}
+
+int FBXMeshData::get_vertex_from_polygon_vertex(const std::vector<int> &p_polygon_indices, int p_index) const {
+	if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) {
+		return -1;
+	}
+
+	const int vertex = p_polygon_indices[p_index];
+	if (vertex >= 0) {
+		return vertex;
+	} else {
+		// Negative numbers are the end of the face, reversing the bits is
+		// possible to obtain the positive correct vertex number.
+		return ~vertex;
+	}
+}
+
+bool FBXMeshData::is_end_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const {
+	if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) {
+		return false;
+	}
+
+	const int vertex = p_polygon_indices[p_index];
+
+	// If the index is negative this is the end of the Polygon.
+	return vertex < 0;
+}
+
+bool FBXMeshData::is_start_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const {
+	if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) {
+		return false;
+	}
+
+	if (p_index == 0) {
+		return true;
+	}
+
+	// If the previous indices is negative this is the begin of a new Polygon.
+	return p_polygon_indices[p_index - 1] < 0;
+}
+
+int FBXMeshData::count_polygons(const std::vector<int> &p_polygon_indices) const {
+	// The negative numbers define the end of the polygon. Counting the amount of
+	// negatives the numbers of polygons are obtained.
+	int count = 0;
+	for (size_t i = 0; i < p_polygon_indices.size(); i += 1) {
+		if (p_polygon_indices[i] < 0) {
+			count += 1;
+		}
+	}
+	return count;
+}
+
+template <class R, class T>
+HashMap<int, R> FBXMeshData::extract_per_vertex_data(
+		int p_vertex_count,
+		const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edge_map,
+		const std::vector<int> &p_mesh_indices,
+		const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data,
+		R (*collector_function)(const Vector<VertexData<T> > *p_vertex_data, R p_fall_back),
+		R p_fall_back) const {
+
+	/* When index_to_direct is set
+	 * index size is 184 ( contains index for the data array [values 0, 96] )
+	 * data size is 96 (contains uv coordinates)
+	 * this means index is simple data reduction basically
+	 */
+
+	ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "FBX file is missing indexing array");
+	ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "The FBX seems corrupted");
+
+	// Aggregate vertex data.
+	HashMap<Vertex, Vector<VertexData<T> > > aggregate_vertex_data;
+
+	switch (p_mapping_data.map_type) {
+		case FBXDocParser::MeshGeometry::MapType::none: {
+			// No data nothing to do.
+			return (HashMap<int, R>());
+		}
+		case FBXDocParser::MeshGeometry::MapType::vertex: {
+			ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct, (HashMap<int, R>()), "We will support in future");
+
+			if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+				// The data is mapped per vertex directly.
+				ERR_FAIL_COND_V_MSG((int)p_mapping_data.data.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR01");
+				for (size_t vertex_index = 0; vertex_index < p_mapping_data.data.size(); vertex_index += 1) {
+					aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[vertex_index] });
+				}
+			} else {
+				// The data is mapped per vertex using a reference.
+				// The indices array, contains a *reference_id for each vertex.
+				// * Note that the reference_id is the id of data into the data array.
+				//
+				// https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+				ERR_FAIL_COND_V_MSG((int)p_mapping_data.index.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR02");
+				for (size_t vertex_index = 0; vertex_index < p_mapping_data.index.size(); vertex_index += 1) {
+					ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[vertex_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR03.")
+					aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[p_mapping_data.index[vertex_index]] });
+				}
+			}
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::polygon_vertex: {
+			if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) {
+				// The data is mapped using each index from the indexes array then direct to the data (data reduction algorithm)
+				ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04");
+				int polygon_id = -1;
+				for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) {
+					if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+						polygon_id += 1;
+					}
+					const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+					ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05");
+					ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06");
+					const int index_to_direct = p_mapping_data.index[polygon_vertex_index];
+					T value = p_mapping_data.data[index_to_direct];
+					aggregate_vertex_data[vertex_index].push_back({ polygon_id, value });
+				}
+			} else if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+				// The data are mapped per polygon vertex directly.
+				ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04");
+				int polygon_id = -1;
+				for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.data.size(); polygon_vertex_index += 1) {
+					if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+						polygon_id += 1;
+					}
+					const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+					ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05");
+					ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06");
+
+					aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[polygon_vertex_index] });
+				}
+			} else {
+				// The data is mapped per polygon_vertex using a reference.
+				// The indices array, contains a *reference_id for each polygon_vertex.
+				// * Note that the reference_id is the id of data into the data array.
+				//
+				// https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+				ERR_FAIL_COND_V_MSG(p_mesh_indices.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR7");
+				int polygon_id = -1;
+				for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) {
+					if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+						polygon_id += 1;
+					}
+					const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+					ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR8");
+					ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file seems  corrupted: #ERR9.")
+					ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] < 0, (HashMap<int, R>()), "FBX file seems  corrupted: #ERR10.")
+					ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] >= (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR11.")
+					aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[p_mapping_data.index[polygon_vertex_index]] });
+				}
+			}
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::polygon: {
+			if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+				// The data are mapped per polygon directly.
+				const int polygon_count = count_polygons(p_mesh_indices);
+				ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR12");
+
+				// Advance each polygon vertex, each new polygon advance the polygon index.
+				int polygon_index = -1;
+				for (size_t polygon_vertex_index = 0;
+						polygon_vertex_index < p_mesh_indices.size();
+						polygon_vertex_index += 1) {
+
+					if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+						polygon_index += 1;
+						ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR13");
+					}
+
+					const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+					ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR14");
+
+					aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[polygon_index] });
+				}
+				ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR16. Not all Polygons are present in the file.")
+			} else {
+				// The data is mapped per polygon using a reference.
+				// The indices array, contains a *reference_id for each polygon.
+				// * Note that the reference_id is the id of data into the data array.
+				//
+				// https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+				const int polygon_count = count_polygons(p_mesh_indices);
+				ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR17");
+
+				// Advance each polygon vertex, each new polygon advance the polygon index.
+				int polygon_index = -1;
+				for (size_t polygon_vertex_index = 0;
+						polygon_vertex_index < p_mesh_indices.size();
+						polygon_vertex_index += 1) {
+
+					if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+						polygon_index += 1;
+						ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR18");
+						ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[polygon_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR19");
+					}
+
+					const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+					ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR20");
+
+					aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[p_mapping_data.index[polygon_index]] });
+				}
+				ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR22. Not all Polygons are present in the file.")
+			}
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::edge: {
+			if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+				// The data are mapped per edge directly.
+				ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR23");
+				for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) {
+					const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index);
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR24");
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR25");
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR26");
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR27");
+					aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[edge_index] });
+					aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[edge_index] });
+				}
+			} else {
+				// The data is mapped per edge using a reference.
+				// The indices array, contains a *reference_id for each polygon.
+				// * Note that the reference_id is the id of data into the data array.
+				//
+				// https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+				ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR28");
+				for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) {
+					const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index);
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR29");
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR30");
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR31");
+					ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR32");
+					ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_0], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR33");
+					ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_1], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR34");
+					aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] });
+					aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] });
+				}
+			}
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::all_the_same: {
+			// No matter the mode, no matter the data size; The first always win
+			// and is set to all the vertices.
+			ERR_FAIL_COND_V_MSG(p_mapping_data.data.size() <= 0, (HashMap<int, R>()), "FBX file seems corrupted: #ERR35");
+			if (p_mapping_data.data.size() > 0) {
+				for (int vertex_index = 0; vertex_index < p_vertex_count; vertex_index += 1) {
+					aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[0] });
+				}
+			}
+		} break;
+	}
+
+	if (aggregate_vertex_data.size() == 0) {
+		return (HashMap<int, R>());
+	}
+
+	// A map is used because turns out that the some FBX file are not well organized
+	// with vertices well compacted. Using a map allows avoid those issues.
+	HashMap<Vertex, R> result;
+
+	// Aggregate the collected data.
+	for (const Vertex *index = aggregate_vertex_data.next(nullptr); index != nullptr; index = aggregate_vertex_data.next(index)) {
+		Vector<VertexData<T> > *aggregated_vertex = aggregate_vertex_data.getptr(*index);
+		// This can't be null because we are just iterating.
+		CRASH_COND(aggregated_vertex == nullptr);
+
+		ERR_FAIL_INDEX_V_MSG(0, aggregated_vertex->size(), (HashMap<int, R>()), "The FBX file is corrupted, No valid data for this vertex index.");
+		result[*index] = collector_function(aggregated_vertex, p_fall_back);
+	}
+
+	// Sanitize the data now, if the file is broken we can try import it anyway.
+	bool problem_found = false;
+	for (size_t i = 0; i < p_mesh_indices.size(); i += 1) {
+		const Vertex vertex = get_vertex_from_polygon_vertex(p_mesh_indices, i);
+		if (result.has(vertex) == false) {
+			result[vertex] = p_fall_back;
+			problem_found = true;
+		}
+	}
+	if (problem_found) {
+		WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN0.");
+	}
+
+	return result;
+}
+
+template <class T>
+HashMap<int, T> FBXMeshData::extract_per_polygon(
+		int p_vertex_count,
+		const std::vector<int> &p_polygon_indices,
+		const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data,
+		T p_fallback_value) const {
+
+	ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_fbx_data.data.size() == 0, (HashMap<int, T>()), "invalid index to direct array");
+	ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_fbx_data.index.size() == 0, (HashMap<int, T>()), "The FBX seems corrupted");
+
+	const int polygon_count = count_polygons(p_polygon_indices);
+
+	// Aggregate vertex data.
+	HashMap<int, Vector<T> > aggregate_polygon_data;
+
+	switch (p_fbx_data.map_type) {
+		case FBXDocParser::MeshGeometry::MapType::none: {
+			// No data nothing to do.
+			return (HashMap<int, T>());
+		}
+		case FBXDocParser::MeshGeometry::MapType::vertex: {
+			ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per vertex. This should not happen.");
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::polygon_vertex: {
+			ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per polygon vertex. This should not happen.");
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::polygon: {
+			if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) {
+				// The data is stored efficiently index_to_direct allows less data in the FBX file.
+				for (int polygon_index = 0;
+						polygon_index < polygon_count;
+						polygon_index += 1) {
+
+					if (p_fbx_data.index.size() == 0) {
+						ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62");
+						aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]);
+					} else {
+						ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62");
+
+						const int index_to_direct = p_fbx_data.index[polygon_index];
+						T value = p_fbx_data.data[index_to_direct];
+						aggregate_polygon_data[polygon_index].push_back(value);
+					}
+				}
+			} else if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+				// The data are mapped per polygon directly.
+				ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR51");
+
+				// Advance each polygon vertex, each new polygon advance the polygon index.
+				for (int polygon_index = 0;
+						polygon_index < polygon_count;
+						polygon_index += 1) {
+
+					ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR52");
+					aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]);
+				}
+			} else {
+				// The data is mapped per polygon using a reference.
+				// The indices array, contains a *reference_id for each polygon.
+				// * Note that the reference_id is the id of data into the data array.
+				//
+				// https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+				ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file seems corrupted: #ERR52");
+
+				// Advance each polygon vertex, each new polygon advance the polygon index.
+				for (int polygon_index = 0;
+						polygon_index < polygon_count;
+						polygon_index += 1) {
+
+					ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR53");
+					ERR_FAIL_INDEX_V_MSG(p_fbx_data.index[polygon_index], (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR54");
+					aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[p_fbx_data.index[polygon_index]]);
+				}
+			}
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::edge: {
+			ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per edge. This should not happen.");
+		} break;
+		case FBXDocParser::MeshGeometry::MapType::all_the_same: {
+			// No matter the mode, no matter the data size; The first always win
+			// and is set to all the vertices.
+			ERR_FAIL_COND_V_MSG(p_fbx_data.data.size() <= 0, (HashMap<int, T>()), "FBX file seems corrupted: #ERR55");
+			if (p_fbx_data.data.size() > 0) {
+				for (int polygon_index = 0; polygon_index < polygon_count; polygon_index += 1) {
+					aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[0]);
+				}
+			}
+		} break;
+	}
+
+	if (aggregate_polygon_data.size() == 0) {
+		return (HashMap<int, T>());
+	}
+
+	// A map is used because turns out that the some FBX file are not well organized
+	// with vertices well compacted. Using a map allows avoid those issues.
+	HashMap<int, T> polygons;
+
+	// Take the first value for each vertex.
+	for (const Vertex *index = aggregate_polygon_data.next(nullptr); index != nullptr; index = aggregate_polygon_data.next(index)) {
+		Vector<T> *aggregated_polygon = aggregate_polygon_data.getptr(*index);
+		// This can't be null because we are just iterating.
+		CRASH_COND(aggregated_polygon == nullptr);
+
+		ERR_FAIL_INDEX_V_MSG(0, (int)aggregated_polygon->size(), (HashMap<int, T>()), "The FBX file is corrupted, No valid data for this polygon index.");
+
+		// Validate the final value.
+		polygons[*index] = (*aggregated_polygon)[0];
+	}
+
+	// Sanitize the data now, if the file is broken we can try import it anyway.
+	bool problem_found = false;
+	for (int polygon_i = 0; polygon_i < polygon_count; polygon_i += 1) {
+		if (polygons.has(polygon_i) == false) {
+			polygons[polygon_i] = p_fallback_value;
+			problem_found = true;
+		}
+	}
+	if (problem_found) {
+		WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN1.");
+	}
+
+	return polygons;
+}
+
+void FBXMeshData::extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data) {
+
+	r_data.clear();
+
+	const int vertex_count = mesh_geometry->get_vertices().size();
+
+	for (const FBXDocParser::BlendShape *blend_shape : mesh_geometry->get_blend_shapes()) {
+		for (const FBXDocParser::BlendShapeChannel *blend_shape_channel : blend_shape->BlendShapeChannels()) {
+			const std::vector<const FBXDocParser::ShapeGeometry *> &shape_geometries = blend_shape_channel->GetShapeGeometries();
+			for (const FBXDocParser::ShapeGeometry *shape_geometry : shape_geometries) {
+
+				String morph_name = ImportUtils::FBXAnimMeshName(shape_geometry->Name()).c_str();
+				if (morph_name.empty()) {
+					morph_name = "morph";
+				}
+
+				// TODO we have only these??
+				const std::vector<unsigned int> &morphs_vertex_indices = shape_geometry->GetIndices();
+				const std::vector<Vector3> &morphs_vertices = shape_geometry->GetVertices();
+				const std::vector<Vector3> &morphs_normals = shape_geometry->GetNormals();
+
+				ERR_FAIL_COND_MSG((int)morphs_vertex_indices.size() > vertex_count, "The FBX file is corrupted: #ERR103");
+				ERR_FAIL_COND_MSG(morphs_vertex_indices.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR104");
+				ERR_FAIL_COND_MSG((int)morphs_vertices.size() > vertex_count, "The FBX file is corrupted: #ERR105");
+				ERR_FAIL_COND_MSG(morphs_normals.size() != 0 && morphs_normals.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR106");
+
+				if (r_data.has(morph_name) == false) {
+					// This morph doesn't exist yet.
+					// Create it.
+					MorphVertexData md;
+					md.vertices.resize(vertex_count);
+					md.normals.resize(vertex_count);
+					r_data.set(morph_name, md);
+				}
+
+				MorphVertexData *data = r_data.getptr(morph_name);
+				Vector3 *data_vertices_ptr = data->vertices.ptrw();
+				Vector3 *data_normals_ptr = data->normals.ptrw();
+
+				for (int i = 0; i < (int)morphs_vertex_indices.size(); i += 1) {
+					const Vertex vertex = morphs_vertex_indices[i];
+
+					ERR_FAIL_INDEX_MSG(vertex, vertex_count, "The blend shapes of this FBX file are corrupted. It has a not valid vertex.");
+
+					data_vertices_ptr[vertex] = morphs_vertices[i];
+
+					if (morphs_normals.size() != 0) {
+						data_normals_ptr[vertex] = morphs_normals[i];
+					}
+				}
+			}
+		}
+	}
+}

+ 175 - 0
modules/fbx/data/fbx_mesh_data.h

@@ -0,0 +1,175 @@
+/*************************************************************************/
+/*  fbx_mesh_data.h                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FBX_MESH_DATA_H
+#define FBX_MESH_DATA_H
+
+#include "core/hash_map.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/resources/surface_tool.h"
+
+#include "fbx_bone.h"
+#include "fbx_parser/FBXMeshGeometry.h"
+#include "import_state.h"
+#include "tools/import_utils.h"
+
+struct FBXMeshData;
+struct FBXBone;
+struct ImportState;
+
+struct VertexWeightMapping {
+	Vector<real_t> weights;
+	Vector<int> bones;
+	// This extra vector is used because the bone id is computed in a second step.
+	// TODO Get rid of this extra step is a good idea.
+	Vector<Ref<FBXBone> > bones_ref;
+};
+
+template <class T>
+struct VertexData {
+	int polygon_index;
+	T data;
+};
+
+// Caches mesh information and instantiates meshes for you using helper functions.
+struct FBXMeshData : Reference {
+	struct MorphVertexData {
+		// TODO we have only these??
+		/// Each element is a vertex. Not supposed to be void.
+		Vector<Vector3> vertices;
+		/// Each element is a vertex. Not supposed to be void.
+		Vector<Vector3> normals;
+	};
+
+	/// vertex id, Weight Info
+	/// later: perf we can use array here
+	HashMap<int, VertexWeightMapping> vertex_weights;
+
+	// translate fbx mesh data from document context to FBX Mesh Geometry Context
+	bool valid_weight_indexes = false;
+
+	MeshInstance *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *mesh_geometry, const FBXDocParser::Model *model);
+
+	void gen_weight_info(Ref<SurfaceTool> st, int vertex_id) const;
+
+	/* mesh maximum weight count */
+	bool valid_weight_count = false;
+	int max_weight_count = 0;
+	uint64_t armature_id = 0;
+	bool valid_armature_id = false;
+	MeshInstance *godot_mesh_instance = nullptr;
+
+private:
+	void sanitize_vertex_weights();
+
+	/// Make sure to reorganize the vertices so that the correct UV is taken.
+	/// This step is needed because differently from the normal, that can be
+	/// combined, the UV may need its own triangle because sometimes they have
+	/// really different UV for the same vertex but different polygon.
+	/// This function make sure to add another vertex for those UVS.
+	void reorganize_vertices(
+			std::vector<int> &r_polygon_indices,
+			std::vector<Vector3> &r_vertices,
+			HashMap<int, Vector3> &r_normals,
+			HashMap<int, Vector2> &r_uv_1,
+			HashMap<int, Vector2> &r_uv_2,
+			HashMap<int, Color> &r_color,
+			HashMap<String, MorphVertexData> &r_morphs,
+			HashMap<int, HashMap<int, Vector3> > &r_normals_raw,
+			HashMap<int, HashMap<int, Vector2> > &r_uv_1_raw,
+			HashMap<int, HashMap<int, Vector2> > &r_uv_2_raw);
+
+	void add_vertex(
+			Ref<SurfaceTool> p_surface_tool,
+			real_t p_scale,
+			int p_vertex,
+			const std::vector<Vector3> &p_vertices_position,
+			const HashMap<int, Vector3> &p_normals,
+			const HashMap<int, Vector2> &p_uvs_0,
+			const HashMap<int, Vector2> &p_uvs_1,
+			const HashMap<int, Color> &p_colors,
+			const Vector3 &p_morph_value = Vector3(),
+			const Vector3 &p_morph_normal = Vector3());
+
+	void triangulate_polygon(Ref<SurfaceTool> st, Vector<int> p_polygon_vertex, Vector<int> p_surface_vertex_map, const std::vector<Vector3> &p_vertices) const;
+
+	/// This function is responsible to convert the FBX polygon vertex to
+	/// vertex index.
+	/// The polygon vertices are stored in an array with some negative
+	/// values. The negative values define the last face index.
+	/// For example the following `face_array` contains two faces, the former
+	/// with 3 vertices and the latter with a line:
+	/// [0,2,-2,3,-5]
+	/// Parsed as:
+	/// [0, 2, 1, 3, 4]
+	/// The negative values are computed using this formula: `(-value) - 1`
+	///
+	/// Returns the vertex index from the poligon vertex.
+	/// Returns -1 if `p_index` is invalid.
+	int get_vertex_from_polygon_vertex(const std::vector<int> &p_face_indices, int p_index) const;
+
+	/// Retuns true if this polygon_vertex_index is the end of a new polygon.
+	bool is_end_of_polygon(const std::vector<int> &p_face_indices, int p_index) const;
+
+	/// Retuns true if this polygon_vertex_index is the begin of a new polygon.
+	bool is_start_of_polygon(const std::vector<int> &p_face_indices, int p_index) const;
+
+	/// Returns the number of polygons.
+	int count_polygons(const std::vector<int> &p_face_indices) const;
+
+	/// Used to extract data from the `MappingData` alligned with vertex.
+	/// Useful to extract normal/uvs/colors/tangets/etc...
+	/// If the function fails somehow, it returns an hollow vector and print an error.
+	template <class R, class T>
+	HashMap<int, R> extract_per_vertex_data(
+			int p_vertex_count,
+			const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edges,
+			const std::vector<int> &p_mesh_indices,
+			const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data,
+			R (*collector_function)(const Vector<VertexData<T> > *p_vertex_data, R p_fall_back),
+			R p_fall_back) const;
+
+	/// Used to extract data from the `MappingData` organized per polygon.
+	/// Useful to extract the materila
+	/// If the function fails somehow, it returns an hollow vector and print an error.
+	template <class T>
+	HashMap<int, T> extract_per_polygon(
+			int p_vertex_count,
+			const std::vector<int> &p_face_indices,
+			const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data,
+			T p_fallback_value) const;
+
+	/// Extracts the morph data and organizes it per vertices.
+	/// The returned `MorphVertexData` arrays are never something different
+	/// then the `vertex_count`.
+	void extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data);
+};
+
+#endif // FBX_MESH_DATA_H

+ 63 - 0
modules/fbx/data/fbx_node.h

@@ -0,0 +1,63 @@
+/*************************************************************************/
+/*  fbx_node.h                                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FBX_NODE_H
+#define FBX_NODE_H
+
+#include "fbx_skeleton.h"
+#include "model_abstraction.h"
+#include "pivot_transform.h"
+
+#include "fbx_parser/FBXDocument.h"
+
+class Spatial;
+struct PivotTransform;
+
+struct FBXNode : Reference, ModelAbstraction {
+	uint64_t current_node_id = 0;
+	String node_name = String();
+	Spatial *godot_node = nullptr;
+
+	// used to parent the skeleton once the tree is built.
+	Ref<FBXSkeleton> skeleton_node = Ref<FBXSkeleton>();
+
+	void set_parent(Ref<FBXNode> p_parent) {
+		fbx_parent = p_parent;
+	}
+
+	void set_pivot_transform(Ref<PivotTransform> p_pivot_transform) {
+		pivot_transform = p_pivot_transform;
+	}
+
+	Ref<PivotTransform> pivot_transform = Ref<PivotTransform>(); // local and global xform data
+	Ref<FBXNode> fbx_parent = Ref<FBXNode>(); // parent node
+};
+
+#endif // FBX_NODE_H

+ 124 - 0
modules/fbx/data/fbx_skeleton.cpp

@@ -0,0 +1,124 @@
+/*************************************************************************/
+/*  fbx_skeleton.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "fbx_skeleton.h"
+
+#include "import_state.h"
+
+#include "tools/import_utils.h"
+
+void FBXSkeleton::init_skeleton(const ImportState &state) {
+	int skeleton_bone_count = skeleton_bones.size();
+
+	if (skeleton == nullptr && skeleton_bone_count > 0) {
+		skeleton = memnew(Skeleton);
+
+		Ref<FBXNode> skeleton_parent_node;
+		if (fbx_node.is_valid()) {
+			// cache skeleton attachment for later during node creation
+			// can't be done until after node hierarchy is built
+			if (fbx_node->godot_node != state.root) {
+				fbx_node->skeleton_node = Ref<FBXSkeleton>(this);
+				print_verbose("cached armature skeleton attachment for node " + fbx_node->node_name);
+			} else {
+				// root node must never be a skeleton to prevent cyclic skeletons from being allowed (skeleton in a skeleton)
+				fbx_node->godot_node->add_child(skeleton);
+				skeleton->set_owner(state.root_owner);
+				skeleton->set_name("Skeleton");
+				print_verbose("created armature skeleton for root");
+			}
+		} else {
+			memfree(skeleton);
+			skeleton = nullptr;
+			print_error("[doc] skeleton has no valid node to parent nodes to - erasing");
+			skeleton_bones.clear();
+			return;
+		}
+	}
+
+	// Make the bone name uniques.
+	for (int x = 0; x < skeleton_bone_count; x++) {
+		Ref<FBXBone> bone = skeleton_bones[x];
+		if (bone.is_valid()) {
+			// Make sure the bone name is unique.
+			const String bone_name = bone->bone_name;
+			int same_name_count = 0;
+			for (int y = x; y < skeleton_bone_count; y++) {
+				Ref<FBXBone> other_bone = skeleton_bones[y];
+				if (other_bone.is_valid()) {
+					if (other_bone->bone_name == bone_name) {
+						same_name_count += 1;
+						other_bone->bone_name += "_" + itos(same_name_count);
+					}
+				}
+			}
+		}
+	}
+
+	Map<int, Ref<FBXBone> > bone_map;
+	// implement fbx cluster skin logic here this is where it goes
+	int bone_count = 0;
+	for (int x = 0; x < skeleton_bone_count; x++) {
+		Ref<FBXBone> bone = skeleton_bones[x];
+		if (bone.is_valid()) {
+			skeleton->add_bone(bone->bone_name);
+			bone->godot_bone_id = bone_count;
+			bone->fbx_skeleton = Ref<FBXSkeleton>(this);
+			bone_map.insert(bone_count, bone);
+			print_verbose("added bone " + itos(bone->bone_id) + " " + bone->bone_name);
+			bone_count++;
+		}
+	}
+
+	ERR_FAIL_COND_MSG(skeleton->get_bone_count() != bone_count, "Not all bones got added, is the file corrupted?");
+
+	for (Map<int, Ref<FBXBone> >::Element *bone_element = bone_map.front(); bone_element; bone_element = bone_element->next()) {
+		Ref<FBXBone> bone = bone_element->value();
+		int bone_index = bone_element->key();
+		print_verbose("working on bone: " + itos(bone_index) + " bone name:" + bone->bone_name);
+
+		skeleton->set_bone_rest(bone->godot_bone_id, get_unscaled_transform(bone->pivot_xform->LocalTransform, state.scale));
+
+		// lookup parent ID
+		if (bone->valid_parent && state.fbx_bone_map.has(bone->parent_bone_id)) {
+			Ref<FBXBone> parent_bone = state.fbx_bone_map[bone->parent_bone_id];
+			int bone_id = skeleton->find_bone(parent_bone->bone_name);
+			if (bone_id != -1) {
+				skeleton->set_bone_parent(bone_index, bone_id);
+			} else {
+				print_error("invalid bone parent: " + parent_bone->bone_name);
+			}
+		} else {
+			if (bone->godot_bone_id != -1) {
+				skeleton->set_bone_parent(bone_index, -1); // no parent for this bone
+			}
+		}
+	}
+}

+ 53 - 0
modules/fbx/data/fbx_skeleton.h

@@ -0,0 +1,53 @@
+/*************************************************************************/
+/*  fbx_skeleton.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FBX_SKELETON_H
+#define FBX_SKELETON_H
+
+#include "fbx_bone.h"
+#include "fbx_node.h"
+#include "model_abstraction.h"
+
+#include "core/reference.h"
+#include "scene/3d/skeleton.h"
+
+struct FBXNode;
+struct ImportState;
+struct FBXBone;
+
+struct FBXSkeleton : Reference, ModelAbstraction {
+	Ref<FBXNode> fbx_node = Ref<FBXNode>();
+	Vector<Ref<FBXBone> > skeleton_bones = Vector<Ref<FBXBone> >();
+	Skeleton *skeleton = nullptr;
+
+	void init_skeleton(const ImportState &state);
+};
+
+#endif // FBX_SKELETON_H

+ 55 - 78
modules/assimp/import_state.h → modules/fbx/data/import_state.h

@@ -28,8 +28,12 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#ifndef EDITOR_SCENE_IMPORT_STATE_H
-#define EDITOR_SCENE_IMPORT_STATE_H
+#ifndef IMPORT_STATE_H
+#define IMPORT_STATE_H
+
+#include "fbx_mesh_data.h"
+#include "modules/fbx/tools/import_utils.h"
+#include "pivot_transform.h"
 
 
 #include "core/bind/core_bind.h"
 #include "core/bind/core_bind.h"
 #include "core/io/resource_importer.h"
 #include "core/io/resource_importer.h"
@@ -43,91 +47,64 @@
 #include "scene/resources/animation.h"
 #include "scene/resources/animation.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/resources/surface_tool.h"
 
 
-#include <assimp/matrix4x4.h>
-#include <assimp/scene.h>
-#include <assimp/types.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/LogStream.hpp>
-#include <assimp/Logger.hpp>
-
-namespace AssimpImporter {
-/** Import state is for global scene import data
- * This makes the code simpler and contains useful lookups.
- */
-struct ImportState {
+#include "modules/fbx/fbx_parser/FBXDocument.h"
+#include "modules/fbx/fbx_parser/FBXImportSettings.h"
+#include "modules/fbx/fbx_parser/FBXMeshGeometry.h"
+#include "modules/fbx/fbx_parser/FBXParser.h"
+#include "modules/fbx/fbx_parser/FBXTokenizer.h"
+#include "modules/fbx/fbx_parser/FBXUtil.h"
 
 
-	String path;
-	Spatial *root;
-	const aiScene *assimp_scene;
-	uint32_t max_bone_weights;
+struct FBXBone;
+struct FBXMeshData;
+struct FBXNode;
+struct FBXSkeleton;
 
 
-	Map<String, Ref<Mesh> > mesh_cache;
-	Map<int, Ref<Material> > material_cache;
-	Map<String, int> light_cache;
-	Map<String, int> camera_cache;
+struct ImportState {
+	bool enable_material_import = true;
+	bool enable_animation_import = true;
 
 
-	// very useful for when you need to ask assimp for the bone mesh
+	Map<StringName, Ref<Texture> > cached_image_searches;
+	Map<uint64_t, Ref<SpatialMaterial> > cached_materials;
 
 
-	Map<const aiNode *, Node *> assimp_node_map;
-	Map<String, Ref<Image> > path_to_image_cache;
+	String path = String();
+	Spatial *root_owner = nullptr;
+	Spatial *root = nullptr;
+	real_t scale = 0.01;
+	Ref<FBXNode> fbx_root_node = Ref<FBXNode>();
+	// skeleton map - merged automatically when they are on the same x node in the tree so we can merge them automatically.
+	Map<uint64_t, Ref<FBXSkeleton> > skeleton_map = Map<uint64_t, Ref<FBXSkeleton> >();
 
 
-	// Generation 3 - determinisitic iteration
-	// to lower potential recursion errors
-	List<const aiNode *> nodes;
-	Map<const aiNode *, Spatial *> flat_node_map;
-	AnimationPlayer *animation_player;
+	// nodes on the same level get merged automatically.
+	//Map<uint64_t, Skeleton *> armature_map;
+	AnimationPlayer *animation_player = nullptr;
 
 
-	// Generation 3 - deterministic armatures
-	// list of armature nodes - flat and simple to parse
-	// assimp node, node in godot
-	List<aiNode *> armature_nodes;
-	Map<const aiNode *, Skeleton *> armature_skeletons;
-	Map<aiBone *, Skeleton *> skeleton_bone_map;
-	// Generation 3 - deterministic bone handling
-	// bones from the stack are popped when found
-	// this means we can detect
-	// what bones are for other armatures
-	List<aiBone *> bone_stack;
+	// Generation 4 - Raw document accessing for bone/skin/joint/kLocators
+	// joints are not necessarily bones but must be merged into the skeleton
+	// (bone id), bone
+	Map<uint64_t, Ref<FBXBone> > fbx_bone_map = Map<uint64_t, Ref<FBXBone> >(); // this is the bone name and setup information required for joints
+	// this will never contain joints only bones attached to a mesh.
 
 
-	// EditorSceneImporter::ImportFlags
-	uint32_t import_flags;
-};
+	// Generation 4 - Raw document for creating the nodes transforms in the scene
+	// this is a list of the nodes in the scene
+	// (id, node)
+	List<Ref<FBXNode> > fbx_node_list = List<Ref<FBXNode> >();
 
 
-struct AssimpImageData {
-	Ref<Image> raw_image;
-	Ref<ImageTexture> texture;
-	aiTextureMapMode map_mode[2];
-};
+	// All nodes which have been created in the scene
+	// this will not contain the root node of the scene
+	Map<uint64_t, Ref<FBXNode> > fbx_target_map = Map<uint64_t, Ref<FBXNode> >();
 
 
-/** Recursive state is used to push state into functions instead of specifying them
-	* This makes the code easier to handle too and add extra arguments without breaking things
-	*/
-struct RecursiveState {
-	RecursiveState() {} // do not construct :)
-	RecursiveState(
-			Transform &_node_transform,
-			Skeleton *_skeleton,
-			Spatial *_new_node,
-			String &_node_name,
-			aiNode *_assimp_node,
-			Node *_parent_node,
-			aiBone *_bone) :
-			node_transform(_node_transform),
-			skeleton(_skeleton),
-			new_node(_new_node),
-			node_name(_node_name),
-			assimp_node(_assimp_node),
-			parent_node(_parent_node),
-			bone(_bone) {}
+	// mesh nodes which are created in node / mesh step - used for populating skin poses in MeshSkins
+	Map<uint64_t, Ref<FBXNode> > MeshNodes = Map<uint64_t, Ref<FBXNode> >();
+	// mesh skin map
+	Map<uint64_t, Ref<Skin> > MeshSkins = Map<uint64_t, Ref<Skin> >();
 
 
-	Transform node_transform;
-	Skeleton *skeleton = NULL;
-	Spatial *new_node = NULL;
-	String node_name;
-	aiNode *assimp_node = NULL;
-	Node *parent_node = NULL;
-	aiBone *bone = NULL;
+	// this is the container for the mesh weight information and eventually
+	// any mesh data
+	// but not the skin, just stuff important for rendering
+	// skin is applied to mesh instance so not really required to be in here yet.
+	// maybe later
+	// fbx mesh id, FBXMeshData
+	Map<uint64_t, Ref<FBXMeshData> > renderer_mesh_data = Map<uint64_t, Ref<FBXMeshData> >();
 };
 };
-} // namespace AssimpImporter
 
 
-#endif // EDITOR_SCENE_IMPORT_STATE_H
+#endif // IMPORT_STATE_H

+ 52 - 0
modules/fbx/data/model_abstraction.h

@@ -0,0 +1,52 @@
+/*************************************************************************/
+/*  model_abstraction.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef MODEL_ABSTRACTION_H
+#define MODEL_ABSTRACTION_H
+
+#include "modules/fbx/fbx_parser/FBXDocument.h"
+
+struct ModelAbstraction {
+	mutable const FBXDocParser::Model *fbx_model = nullptr;
+
+	void set_model(const FBXDocParser::Model *p_model) {
+		fbx_model = p_model;
+	}
+
+	bool has_model() const {
+		return fbx_model != nullptr;
+	}
+
+	const FBXDocParser::Model *get_model() const {
+		return fbx_model;
+	}
+};
+
+#endif // MODEL_ABSTRACTION_H

+ 257 - 0
modules/fbx/data/pivot_transform.cpp

@@ -0,0 +1,257 @@
+/*************************************************************************/
+/*  pivot_transform.cpp                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "pivot_transform.h"
+
+#include "tools/import_utils.h"
+
+void PivotTransform::ReadTransformChain() {
+	const FBXDocParser::PropertyTable *props = fbx_model->Props();
+	const FBXDocParser::Model::RotOrder &rot = fbx_model->RotationOrder();
+	const FBXDocParser::TransformInheritance &inheritType = fbx_model->InheritType();
+	inherit_type = inheritType; // copy the inherit type we need it in the second step.
+	print_verbose("Model: " + String(fbx_model->Name().c_str()) + " Has inherit type: " + itos(fbx_model->InheritType()));
+	bool ok = false;
+	raw_pre_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", ok));
+	if (ok) {
+		pre_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_pre_rotation));
+		print_verbose("valid pre_rotation: " + raw_pre_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI)));
+	}
+	raw_post_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", ok));
+	if (ok) {
+		post_rotation = ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder_EulerXYZ, ImportUtils::deg2rad(raw_post_rotation));
+		print_verbose("valid post_rotation: " + raw_post_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI)));
+	}
+	const Vector3 &RotationPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationPivot", ok));
+	if (ok) {
+		rotation_pivot = ImportUtils::FixAxisConversions(RotationPivot);
+	}
+	const Vector3 &RotationOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationOffset", ok));
+	if (ok) {
+		rotation_offset = ImportUtils::FixAxisConversions(RotationOffset);
+	}
+	const Vector3 &ScalingOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingOffset", ok));
+	if (ok) {
+		scaling_offset = ImportUtils::FixAxisConversions(ScalingOffset);
+	}
+	const Vector3 &ScalingPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingPivot", ok));
+	if (ok) {
+		scaling_pivot = ImportUtils::FixAxisConversions(ScalingPivot);
+	}
+	const Vector3 &Translation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Translation", ok));
+	if (ok) {
+		translation = ImportUtils::FixAxisConversions(Translation);
+	}
+	raw_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Rotation", ok));
+	if (ok) {
+		rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_rotation));
+	}
+	const Vector3 &Scaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Scaling", ok));
+	if (ok) {
+		scaling = Scaling;
+	}
+	const Vector3 &GeometricScaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricScaling", ok));
+	if (ok) {
+		geometric_scaling = GeometricScaling;
+	}
+	const Vector3 &GeometricRotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricRotation", ok));
+	if (ok) {
+		geometric_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(GeometricRotation));
+	}
+	const Vector3 &GeometricTranslation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricTranslation", ok));
+	if (ok) {
+		geometric_translation = ImportUtils::FixAxisConversions(GeometricTranslation);
+	}
+}
+
+Transform PivotTransform::ComputeLocalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const {
+	Transform T, Roff, Rp, Soff, Sp, S;
+
+	// Here I assume this is the operation which needs done.
+	// Its WorldTransform * V
+
+	// Origin pivots
+	T.set_origin(p_translation);
+	Roff.set_origin(rotation_offset);
+	Rp.set_origin(rotation_pivot);
+	Soff.set_origin(scaling_offset);
+	Sp.set_origin(scaling_pivot);
+
+	// Scaling node
+	S.scale(p_scaling);
+	// Rotation pivots
+	Transform Rpre = Transform(pre_rotation);
+	Transform R = Transform(p_rotation);
+	Transform Rpost = Transform(post_rotation);
+
+	return T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
+}
+
+Transform PivotTransform::ComputeGlobalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const {
+	Transform T, Roff, Rp, Soff, Sp, S;
+
+	// Here I assume this is the operation which needs done.
+	// Its WorldTransform * V
+
+	// Origin pivots
+	T.set_origin(p_translation);
+	Roff.set_origin(rotation_offset);
+	Rp.set_origin(rotation_pivot);
+	Soff.set_origin(scaling_offset);
+	Sp.set_origin(scaling_pivot);
+
+	// Scaling node
+	S.scale(p_scaling);
+
+	// Rotation pivots
+	Transform Rpre = Transform(pre_rotation);
+	Transform R = Transform(p_rotation);
+	Transform Rpost = Transform(post_rotation);
+
+	Transform parent_global_xform;
+	Transform parent_local_scaling_m;
+
+	if (parent_transform.is_valid()) {
+		parent_global_xform = parent_transform->GlobalTransform;
+		parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
+	}
+
+	Transform local_rotation_m, parent_global_rotation_m;
+	Quat parent_global_rotation = parent_global_xform.basis.get_rotation_quat();
+	parent_global_rotation_m.basis.set_quat(parent_global_rotation);
+	local_rotation_m = Rpre * R * Rpost;
+
+	//Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quat().normalized());
+
+	Transform local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation;
+	Vector3 parent_translation = parent_global_xform.get_origin();
+	parent_shear_translation.origin = parent_translation;
+	parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform;
+	parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation;
+	local_shear_scaling = S;
+
+	// Inherit type handler - we don't care about T here, just reordering RSrs etc.
+	Transform global_rotation_scale;
+	if (inherit_type == FBXDocParser::Transform_RrSs) {
+		global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling;
+	} else if (inherit_type == FBXDocParser::Transform_RSrs) {
+		global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling;
+	} else if (inherit_type == FBXDocParser::Transform_Rrs) {
+		Transform parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.affine_inverse();
+		global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling;
+	}
+	Transform local_transform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
+	//Transform local_translation_pivoted = Transform(Basis(), LocalTransform.origin);
+
+	// manual hack to force SSC not to be compensated for - until we can handle it properly with tests
+	return parent_global_xform * local_transform;
+}
+
+void PivotTransform::ComputePivotTransform() {
+	Transform T, Roff, Rp, Soff, Sp, S;
+
+	// Here I assume this is the operation which needs done.
+	// Its WorldTransform * V
+
+	// Origin pivots
+	T.set_origin(translation);
+	Roff.set_origin(rotation_offset);
+	Rp.set_origin(rotation_pivot);
+	Soff.set_origin(scaling_offset);
+	Sp.set_origin(scaling_pivot);
+
+	// Scaling node
+	if (!scaling.is_equal_approx(Vector3())) {
+		S.scale(scaling);
+	} else {
+		S.scale(Vector3(1, 1, 1));
+	}
+	Local_Scaling_Matrix = S; // copy for when node / child is looking for the value of this.
+
+	// Rotation pivots
+	Transform Rpre = Transform(pre_rotation);
+	Transform R = Transform(rotation);
+	Transform Rpost = Transform(post_rotation);
+
+	Transform parent_global_xform;
+	Transform parent_local_scaling_m;
+
+	if (parent_transform.is_valid()) {
+		parent_global_xform = parent_transform->GlobalTransform;
+		parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
+	}
+
+	Transform local_rotation_m, parent_global_rotation_m;
+	Quat parent_global_rotation = parent_global_xform.basis.get_rotation_quat();
+	parent_global_rotation_m.basis.set_quat(parent_global_rotation);
+	local_rotation_m = Rpre * R * Rpost;
+
+	//Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quat().normalized());
+
+	Transform local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation;
+	Vector3 parent_translation = parent_global_xform.get_origin();
+	parent_shear_translation.origin = parent_translation;
+	parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform;
+	parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation;
+	local_shear_scaling = S;
+
+	// Inherit type handler - we don't care about T here, just reordering RSrs etc.
+	Transform global_rotation_scale;
+	if (inherit_type == FBXDocParser::Transform_RrSs) {
+		global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling;
+	} else if (inherit_type == FBXDocParser::Transform_RSrs) {
+		global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling;
+	} else if (inherit_type == FBXDocParser::Transform_Rrs) {
+		Transform parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.inverse();
+		global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling;
+	}
+	LocalTransform = Transform();
+	LocalTransform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
+
+	ERR_FAIL_COND_MSG(LocalTransform.basis.determinant() == 0, "invalid scale reset");
+
+	Transform local_translation_pivoted = Transform(Basis(), LocalTransform.origin);
+	GlobalTransform = Transform();
+	//GlobalTransform = parent_global_xform * LocalTransform;
+	Transform global_origin = Transform(Basis(), parent_translation);
+	GlobalTransform = (global_origin * local_translation_pivoted) * global_rotation_scale;
+
+	ImportUtils::debug_xform("local xform calculation", LocalTransform);
+	print_verbose("scale of node: " + S.basis.get_scale_local());
+	print_verbose("---------------------------------------------------------------");
+}
+
+void PivotTransform::Execute() {
+	ReadTransformChain();
+	ComputePivotTransform();
+
+	ImportUtils::debug_xform("global xform: ", GlobalTransform);
+	computed_global_xform = true;
+}

+ 112 - 0
modules/fbx/data/pivot_transform.h

@@ -0,0 +1,112 @@
+/*************************************************************************/
+/*  pivot_transform.h                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef PIVOT_TRANSFORM_H
+#define PIVOT_TRANSFORM_H
+
+#include "core/reference.h"
+
+#include "model_abstraction.h"
+
+#include "fbx_parser/FBXDocument.h"
+#include "tools/import_utils.h"
+
+enum TransformationComp {
+	TransformationComp_Translation,
+	TransformationComp_Scaling,
+	TransformationComp_Rotation,
+	TransformationComp_RotationOffset,
+	TransformationComp_RotationPivot,
+	TransformationComp_PreRotation,
+	TransformationComp_PostRotation,
+	TransformationComp_ScalingOffset,
+	TransformationComp_ScalingPivot,
+	TransformationComp_GeometricTranslation,
+	TransformationComp_GeometricRotation,
+	TransformationComp_GeometricScaling,
+	TransformationComp_MAXIMUM
+};
+// Abstract away pivot data so its simpler to handle
+struct PivotTransform : Reference, ModelAbstraction {
+
+	// at the end we want to keep geometric_ everything, post and pre rotation
+	// these are used during animation data processing / keyframe ingestion the rest can be simplified down / out.
+	Quat pre_rotation = Quat();
+	Quat post_rotation = Quat();
+	Quat rotation = Quat();
+	Quat geometric_rotation = Quat();
+	Vector3 rotation_pivot = Vector3();
+	Vector3 rotation_offset = Vector3();
+	Vector3 scaling_offset = Vector3(1.0, 1.0, 1.0);
+	Vector3 scaling_pivot = Vector3(1.0, 1.0, 1.0);
+	Vector3 translation = Vector3();
+	Vector3 scaling = Vector3(1.0, 1.0, 1.0);
+	Vector3 geometric_scaling = Vector3(1.0, 1.0, 1.0);
+	Vector3 geometric_translation = Vector3();
+
+	Vector3 raw_rotation = Vector3();
+	Vector3 raw_post_rotation = Vector3();
+	Vector3 raw_pre_rotation = Vector3();
+
+	/* Read pivots from the document */
+	void ReadTransformChain();
+
+	void debug_pivot_xform(String p_name) {
+		print_verbose("debugging node name: " + p_name);
+		print_verbose("raw rotation: " + raw_rotation * (180 / Math_PI));
+		print_verbose("raw pre_rotation " + raw_pre_rotation * (180 / Math_PI));
+		print_verbose("raw post_rotation " + raw_post_rotation * (180 / Math_PI));
+	}
+	Transform ComputeGlobalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const;
+	Transform ComputeLocalTransform(Vector3 p_translation, Quat p_rotation, Vector3 p_scaling) const;
+
+	/* Extract into xforms and calculate once */
+	void ComputePivotTransform();
+
+	/* Execute the command for the pivot generation */
+	void Execute();
+
+	void set_parent(Ref<PivotTransform> p_parent) {
+		parent_transform = p_parent;
+	}
+
+	bool computed_global_xform = false;
+	Ref<PivotTransform> parent_transform = Ref<PivotTransform>();
+	//Transform chain[TransformationComp_MAXIMUM];
+
+	// cached for later use
+	Transform GlobalTransform = Transform();
+	Transform LocalTransform = Transform();
+	Transform Local_Scaling_Matrix = Transform(); // used for inherit type.
+	Transform GeometricTransform = Transform(); // 3DS max only
+	FBXDocParser::TransformInheritance inherit_type = FBXDocParser::TransformInheritance_MAX; // maya fbx requires this - sorry <3
+};
+
+#endif // PIVOT_TRANSFORM_H

+ 1428 - 0
modules/fbx/editor_scene_importer_fbx.cpp

@@ -0,0 +1,1428 @@
+/*************************************************************************/
+/*  editor_scene_importer_fbx.cpp                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "editor_scene_importer_fbx.h"
+
+#include "data/fbx_anim_container.h"
+#include "data/fbx_material.h"
+#include "data/fbx_mesh_data.h"
+#include "data/fbx_skeleton.h"
+#include "tools/import_utils.h"
+
+#include "core/io/image_loader.h"
+#include "editor/editor_log.h"
+#include "editor/editor_node.h"
+#include "editor/import/resource_importer_scene.h"
+#include "scene/3d/bone_attachment.h"
+#include "scene/3d/camera.h"
+#include "scene/3d/light.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/main/node.h"
+#include "scene/resources/material.h"
+
+#include "fbx_parser/FBXDocument.h"
+#include "fbx_parser/FBXImportSettings.h"
+#include "fbx_parser/FBXMeshGeometry.h"
+#include "fbx_parser/FBXParser.h"
+#include "fbx_parser/FBXProperties.h"
+#include "fbx_parser/FBXTokenizer.h"
+
+#include <string>
+
+void EditorSceneImporterFBX::get_extensions(List<String> *r_extensions) const {
+	// register FBX as the one and only format for FBX importing
+	const String import_setting_string = "filesystem/import/fbx/";
+	const String fbx_str = "fbx";
+	Vector<String> exts;
+	exts.push_back(fbx_str);
+	_register_project_setting_import(fbx_str, import_setting_string, exts, r_extensions,
+			true);
+}
+
+void EditorSceneImporterFBX::_register_project_setting_import(const String generic,
+		const String import_setting_string,
+		const Vector<String> &exts,
+		List<String> *r_extensions,
+		const bool p_enabled) const {
+	const String use_generic = "use_" + generic;
+	_GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true);
+	if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) {
+		for (int32_t i = 0; i < exts.size(); i++) {
+			r_extensions->push_back(exts[i]);
+		}
+	}
+}
+
+uint32_t EditorSceneImporterFBX::get_import_flags() const {
+	return IMPORT_SCENE;
+}
+
+Node *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
+		List<String> *r_missing_deps, Error *r_err) {
+	// done for performance when re-importing lots of files when testing importer in verbose only!
+	if (OS::get_singleton()->is_stdout_verbose()) {
+		EditorLog *log = EditorNode::get_log();
+		log->clear();
+	}
+	Error err;
+	FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
+
+	ERR_FAIL_COND_V(!f, NULL);
+
+	{
+
+		PoolByteArray data;
+		// broadphase tokenizing pass in which we identify the core
+		// syntax elements of FBX (brackets, commas, key:value mappings)
+		FBXDocParser::TokenList tokens;
+
+		bool is_binary = false;
+		data.resize(f->get_len());
+		f->get_buffer(data.write().ptr(), data.size());
+		PoolByteArray fbx_header;
+		fbx_header.resize(64);
+		for (int32_t byte_i = 0; byte_i < 64; byte_i++) {
+			fbx_header.write()[byte_i] = data.read()[byte_i];
+		}
+
+		String fbx_header_string;
+		if (fbx_header.size() >= 0) {
+			PoolByteArray::Read r = fbx_header.read();
+			fbx_header_string.parse_utf8((const char *)r.ptr(), fbx_header.size());
+		}
+
+		print_verbose("[doc] opening fbx file: " + p_path);
+		print_verbose("[doc] fbx header: " + fbx_header_string);
+
+		// safer to check this way as there can be different formatted headers
+		if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) {
+			is_binary = true;
+			print_verbose("[doc] is binary");
+			FBXDocParser::TokenizeBinary(tokens, (const char *)data.write().ptr(), (size_t)data.size());
+		} else {
+			print_verbose("[doc] is ascii");
+			FBXDocParser::Tokenize(tokens, (const char *)data.write().ptr());
+		}
+
+		// The import process explained:
+		// 1. Tokens are made, these are then taken into the 'parser' below
+		// 2. The parser constructs 'Elements' and all 'real' FBX Types.
+		// 3. This creates a problem: shared_ptr ownership, should Elements later 'take ownership'
+		// 4. No, it shouldn't so we should either a.) use weak ref for elements; but this is not correct.
+
+		// use this information to construct a very rudimentary
+		// parse-tree representing the FBX scope structure
+		FBXDocParser::Parser parser(tokens, is_binary);
+		FBXDocParser::ImportSettings settings;
+		settings.strictMode = false;
+
+		// this function leaks a lot
+		FBXDocParser::Document doc(parser, settings);
+
+		// yeah so closing the file is a good idea (prevents readonly states)
+		f->close();
+
+		// safety for version handling
+		if (doc.IsSafeToImport()) {
+			Spatial *spatial = _generate_scene(p_path, &doc, p_flags, p_bake_fps, 8);
+
+			// todo: move to document shutdown (will need to be validated after moving; this code has been validated already)
+			for (FBXDocParser::TokenPtr token : tokens) {
+				if (token) {
+					delete token;
+					token = nullptr;
+				}
+			}
+
+			return spatial;
+		} else {
+			print_error("Cannot import file: " + p_path + " version of file is unsupported, please re-export in your modelling package file version is: " + itos(doc.FBXVersion()));
+		}
+	}
+
+	return memnew(Spatial);
+}
+
+template <class T>
+struct EditorSceneImporterAssetImportInterpolate {
+
+	T lerp(const T &a, const T &b, float c) const {
+
+		return a + (b - a) * c;
+	}
+
+	T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
+
+		float t2 = t * t;
+		float t3 = t2 * t;
+
+		return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
+							  (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
+	}
+
+	T bezier(T start, T control_1, T control_2, T end, float t) {
+		/* Formula from Wikipedia article on Bezier curves. */
+		real_t omt = (1.0 - t);
+		real_t omt2 = omt * omt;
+		real_t omt3 = omt2 * omt;
+		real_t t2 = t * t;
+		real_t t3 = t2 * t;
+
+		return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
+	}
+};
+
+//thank you for existing, partial specialization
+template <>
+struct EditorSceneImporterAssetImportInterpolate<Quat> {
+
+	Quat lerp(const Quat &a, const Quat &b, float c) const {
+		ERR_FAIL_COND_V(!a.is_normalized(), Quat());
+		ERR_FAIL_COND_V(!b.is_normalized(), Quat());
+
+		return a.slerp(b, c).normalized();
+	}
+
+	Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) {
+		ERR_FAIL_COND_V(!p1.is_normalized(), Quat());
+		ERR_FAIL_COND_V(!p2.is_normalized(), Quat());
+
+		return p1.slerp(p2, c).normalized();
+	}
+
+	Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) {
+		ERR_FAIL_COND_V(!start.is_normalized(), Quat());
+		ERR_FAIL_COND_V(!end.is_normalized(), Quat());
+
+		return start.slerp(end, t).normalized();
+	}
+};
+
+template <class T>
+T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time,
+		AssetImportAnimation::Interpolation p_interp) {
+	//could use binary search, worth it?
+	int idx = -1;
+	for (int i = 0; i < p_times.size(); i++) {
+		if (p_times[i] > p_time)
+			break;
+		idx++;
+	}
+
+	EditorSceneImporterAssetImportInterpolate<T> interp;
+
+	switch (p_interp) {
+		case AssetImportAnimation::INTERP_LINEAR: {
+
+			if (idx == -1) {
+				return p_values[0];
+			} else if (idx >= p_times.size() - 1) {
+				return p_values[p_times.size() - 1];
+			}
+
+			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+			return interp.lerp(p_values[idx], p_values[idx + 1], c);
+
+		} break;
+		case AssetImportAnimation::INTERP_STEP: {
+
+			if (idx == -1) {
+				return p_values[0];
+			} else if (idx >= p_times.size() - 1) {
+				return p_values[p_times.size() - 1];
+			}
+
+			return p_values[idx];
+
+		} break;
+		case AssetImportAnimation::INTERP_CATMULLROMSPLINE: {
+
+			if (idx == -1) {
+				return p_values[1];
+			} else if (idx >= p_times.size() - 1) {
+				return p_values[1 + p_times.size() - 1];
+			}
+
+			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+			return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c);
+
+		} break;
+		case AssetImportAnimation::INTERP_CUBIC_SPLINE: {
+
+			if (idx == -1) {
+				return p_values[1];
+			} else if (idx >= p_times.size() - 1) {
+				return p_values[(p_times.size() - 1) * 3 + 1];
+			}
+
+			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+			T from = p_values[idx * 3 + 1];
+			T c1 = from + p_values[idx * 3 + 2];
+			T to = p_values[idx * 3 + 4];
+			T c2 = to + p_values[idx * 3 + 3];
+
+			return interp.bezier(from, c1, c2, to, c);
+
+		} break;
+	}
+
+	ERR_FAIL_V(p_values[0]);
+}
+
+void set_owner_recursive(Node *root, Node *current_node) {
+	current_node->set_owner(root);
+
+	for (int child_id = 0; child_id < current_node->get_child_count(); child_id++) {
+		Node *child = current_node->get_child(child_id);
+		set_owner_recursive(root, child); // recursive
+	}
+}
+
+// tool which can get the global transform for a scene which isn't loaded.
+Transform get_global_transform(Spatial *root, Spatial *child_node) {
+	// state.root is armature and you are using this for an armature check.
+	if (root == child_node) {
+		return root->get_transform();
+	}
+
+	Transform t = Transform();
+	Node *iter = child_node;
+
+	while (iter != nullptr && iter != root) {
+		Spatial *spatial = Object::cast_to<Spatial>(iter);
+		if (spatial) {
+			t *= spatial->get_transform();
+		}
+
+		iter = iter->get_parent();
+	}
+
+	return t;
+}
+
+Spatial *EditorSceneImporterFBX::_generate_scene(
+		const String &p_path,
+		const FBXDocParser::Document *p_document,
+		const uint32_t p_flags,
+		int p_bake_fps,
+		const int32_t p_max_bone_weights) {
+
+	ImportState state;
+	state.path = p_path;
+	state.animation_player = NULL;
+
+	// create new root node for scene
+	Spatial *scene_root = memnew(Spatial);
+	state.root = memnew(Spatial);
+	state.root_owner = scene_root; // the real scene root... sorry compatibility code is painful...
+
+	state.root->set_name("RootNode");
+	scene_root->add_child(state.root);
+	state.root->set_owner(scene_root);
+
+	state.fbx_root_node.instance();
+	state.fbx_root_node->godot_node = state.root;
+
+	// Size relative to cm.
+	const real_t fbx_unit_scale = p_document->GlobalSettingsPtr()->UnitScaleFactor();
+
+	// Set FBX file scale is relative to CM must be converted to M
+	state.scale = fbx_unit_scale / 100.0;
+	print_verbose("FBX unit scale is: " + rtos(state.scale));
+
+	// Enabled by default.
+	state.enable_material_import = true;
+	// Enabled by default.
+	state.enable_animation_import = true;
+	Ref<FBXNode> root_node;
+	root_node.instance();
+	root_node->node_name = "root node";
+	root_node->current_node_id = 0;
+	root_node->godot_node = state.root;
+
+	// cache this node onto the fbx_target map.
+	state.fbx_target_map.insert(0, root_node);
+
+	// cache basic node information from FBX document
+	// grabs all FBX bones
+	BuildDocumentBones(Ref<FBXBone>(), state, p_document, 0L);
+	BuildDocumentNodes(nullptr, state, p_document, 0L, nullptr);
+
+	// Build document skinning information
+	for (uint64_t skin_id : p_document->GetSkinIDs()) {
+		// Validate the parser
+		FBXDocParser::LazyObject *lazy_skin = p_document->GetObject(skin_id);
+		ERR_CONTINUE_MSG(lazy_skin == nullptr, "invalid lazy object [serious parser bug]");
+
+		// Validate the parser
+		const FBXDocParser::Skin *skin = lazy_skin->Get<FBXDocParser::Skin>();
+		ERR_CONTINUE_MSG(skin == nullptr, "invalid skin added to skin list [parser bug]");
+
+		const std::vector<const FBXDocParser::Connection *> source_to_destination = p_document->GetConnectionsBySourceSequenced(skin_id);
+		const std::vector<const FBXDocParser::Connection *> destination_to_source = p_document->GetConnectionsByDestinationSequenced(skin_id);
+		FBXDocParser::MeshGeometry *mesh = nullptr;
+		uint64_t mesh_id = 0;
+
+		// Most likely only contains the mesh link for the skin
+		// The mesh geometry.
+		for (const FBXDocParser::Connection *con : source_to_destination) {
+			// do something
+			print_verbose("src: " + itos(con->src));
+			FBXDocParser::Object *ob = con->DestinationObject();
+			mesh = dynamic_cast<FBXDocParser::MeshGeometry *>(ob);
+
+			if (mesh) {
+				mesh_id = mesh->ID();
+				break;
+			}
+		}
+
+		// Validate the mesh exists and was retrieved
+		ERR_CONTINUE_MSG(mesh_id == 0, "mesh id is invalid");
+
+		// NOTE: this will ONLY work on skinned bones (it is by design.)
+		// A cluster is a skinned bone so SKINS won't contain unskinned bones so we need to pre-add all bones and parent them in a step beforehand.
+		for (const FBXDocParser::Connection *con : destination_to_source) {
+			FBXDocParser::Object *ob = con->SourceObject();
+
+			//
+			// Read the FBX Document bone information
+			//
+
+			// Get bone weight data
+			const FBXDocParser::Cluster *deformer = dynamic_cast<const FBXDocParser::Cluster *>(ob);
+			ERR_CONTINUE_MSG(deformer == nullptr, "invalid bone cluster");
+
+			const uint64_t deformer_id = deformer->ID();
+			std::vector<const FBXDocParser::Connection *> connections = p_document->GetConnectionsByDestinationSequenced(deformer_id);
+
+			// Weight data always has a node in the scene lets grab the limb's node in the scene :) (reverse set to true since it's the opposite way around)
+			const FBXDocParser::ModelLimbNode *limb_node = ProcessDOMConnection<FBXDocParser::ModelLimbNode>(p_document, deformer_id, true);
+
+			ERR_CONTINUE_MSG(limb_node == nullptr, "unable to resolve model for skinned bone");
+
+			const uint64_t model_id = limb_node->ID();
+
+			// This will never happen, so if it does you know you fucked up.
+			ERR_CONTINUE_MSG(!state.fbx_bone_map.has(model_id), "missing LimbNode detected");
+
+			// new bone instance
+			Ref<FBXBone> bone_element = state.fbx_bone_map[model_id];
+
+			//
+			// Bone Weight Information Configuration
+			//
+
+			// Cache Weight Information into bone for later usage if you want the raw data.
+			const std::vector<unsigned int> &indexes = deformer->GetIndices();
+			const std::vector<float> &weights = deformer->GetWeights();
+			Ref<FBXMeshData> mesh_vertex_data;
+
+			// this data will pre-exist if vertex weight information is found
+			if (state.renderer_mesh_data.has(mesh_id)) {
+				mesh_vertex_data = state.renderer_mesh_data[mesh_id];
+			} else {
+				mesh_vertex_data.instance();
+				state.renderer_mesh_data.insert(mesh_id, mesh_vertex_data);
+			}
+
+			mesh_vertex_data->armature_id = bone_element->armature_id;
+			mesh_vertex_data->valid_armature_id = true;
+
+			//print_verbose("storing mesh vertex data for mesh to use later");
+			ERR_CONTINUE_MSG(indexes.size() != weights.size(), "[doc] error mismatch between weight info");
+
+			for (size_t idx = 0; idx < indexes.size(); idx++) {
+				const size_t vertex_index = indexes[idx];
+				const real_t influence_weight = weights[idx];
+
+				VertexWeightMapping &vm = mesh_vertex_data->vertex_weights[vertex_index];
+				vm.weights.push_back(influence_weight);
+				vm.bones.push_back(0);
+				vm.bones_ref.push_back(bone_element);
+			}
+
+			for (const int *vertex_index = mesh_vertex_data->vertex_weights.next(nullptr);
+					vertex_index != nullptr;
+					vertex_index = mesh_vertex_data->vertex_weights.next(vertex_index)) {
+				VertexWeightMapping *vm = mesh_vertex_data->vertex_weights.getptr(*vertex_index);
+				const int influence_count = vm->weights.size();
+				if (influence_count > mesh_vertex_data->max_weight_count) {
+					mesh_vertex_data->max_weight_count = influence_count;
+					mesh_vertex_data->valid_weight_count = true;
+				}
+			}
+
+			if (mesh_vertex_data->max_weight_count > 4) {
+				if (mesh_vertex_data->max_weight_count > 8) {
+					ERR_PRINT("[doc] Serious: maximum bone influences is 8 in this branch.");
+				}
+				// Clamp to 8 bone vertex influences.
+				mesh_vertex_data->max_weight_count = 8;
+				print_verbose("[doc] Using 8 vertex bone influences configuration.");
+			} else {
+				mesh_vertex_data->max_weight_count = 4;
+				print_verbose("[doc] Using 4 vertex bone influences configuration.");
+			}
+		}
+	}
+
+	// do we globally allow for import of materials
+	// (prevents overwrite of materials; so you can handle them explicitly)
+	if (state.enable_material_import) {
+		const std::vector<uint64_t> &materials = p_document->GetMaterialIDs();
+
+		for (uint64_t material_id : materials) {
+
+			FBXDocParser::LazyObject *lazy_material = p_document->GetObject(material_id);
+			const FBXDocParser::Material *mat = lazy_material->Get<FBXDocParser::Material>();
+			ERR_CONTINUE_MSG(!mat, "Could not convert fbx material by id: " + itos(material_id));
+
+			Ref<FBXMaterial> material;
+			material.instance();
+			material->set_imported_material(mat);
+
+			Ref<SpatialMaterial> godot_material = material->import_material(state);
+
+			state.cached_materials.insert(material_id, godot_material);
+		}
+	}
+
+	// build skin and skeleton information
+	print_verbose("[doc] Skeleton Bone count: " + itos(state.fbx_bone_map.size()));
+
+	// Importing bones using document based method from FBX directly
+	// We do not use the assimp bone format to determine this information anymore.
+	if (state.fbx_bone_map.size() > 0) {
+		// We are using a single skeleton only method here
+		// this is because we really have no concept of skeletons in FBX
+		// their are bones in a scene but they have no specific armature
+		// we can detect armatures but the issue lies in the complexity
+		// we opted to merge the entire scene onto one skeleton for now
+		// if we need to change this we have an archive of the old code.
+
+		const std::vector<uint64_t> &bind_pose_ids = p_document->GetBindPoseIDs();
+
+		for (uint64_t skin_id : bind_pose_ids) {
+
+			FBXDocParser::LazyObject *lazy_skin = p_document->GetObject(skin_id);
+			const FBXDocParser::FbxPose *active_skin = lazy_skin->Get<FBXDocParser::FbxPose>();
+
+			if (active_skin) {
+				const std::vector<FBXDocParser::FbxPoseNode *> &bind_poses = active_skin->GetBindPoses();
+
+				for (FBXDocParser::FbxPoseNode *pose_node : bind_poses) {
+					Transform t = pose_node->GetBindPose();
+					uint64_t fbx_node_id = pose_node->GetNodeID();
+					if (state.fbx_bone_map.has(fbx_node_id)) {
+						Ref<FBXBone> bone = state.fbx_bone_map[fbx_node_id];
+						if (bone.is_valid()) {
+							print_verbose("assigned skin pose from the file for bone " + bone->bone_name + ", transform: " + t);
+							bone->pose_node = t;
+							bone->assigned_pose_node = true;
+						}
+					}
+				}
+			}
+		}
+
+		// bind pose normally only has 1 per mesh but can have more than one
+		// this is the point of skins
+		// in FBX first bind pose is the master for the first skin
+
+		// In order to handle the FBX skeleton we must also inverse any parent transforms on the bones
+		// just to rule out any parent node transforms in the bone data
+		// this is trivial to do and allows us to use the single skeleton method and merge them
+		// this means that the nodes from maya kLocators will be preserved as bones
+		// in the same rig without having to match this across skeletons and merge by detection
+		// we can just merge and undo any parent transforms
+		for (Map<uint64_t, Ref<FBXBone> >::Element *bone_element = state.fbx_bone_map.front(); bone_element; bone_element = bone_element->next()) {
+			Ref<FBXBone> bone = bone_element->value();
+			Ref<FBXSkeleton> fbx_skeleton_inst;
+
+			uint64_t armature_id = bone->armature_id;
+			if (state.skeleton_map.has(armature_id)) {
+				fbx_skeleton_inst = state.skeleton_map[armature_id];
+			} else {
+				fbx_skeleton_inst.instance();
+				state.skeleton_map.insert(armature_id, fbx_skeleton_inst);
+			}
+
+			print_verbose("populating skeleton with bone: " + bone->bone_name);
+
+			//			// populate bone skeleton - since fbx has no DOM for the skeleton just a node.
+			//			bone->bone_skeleton = fbx_skeleton_inst;
+
+			// now populate bone on the armature node list
+			fbx_skeleton_inst->skeleton_bones.push_back(bone);
+
+			// we need to have a valid armature id and the model configured for the bone to be assigned fully.
+			// happens once per skeleton
+			if (state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->has_model()) {
+				Ref<FBXNode> node = state.fbx_target_map[armature_id];
+
+				fbx_skeleton_inst->set_model(node->get_model());
+				fbx_skeleton_inst->fbx_node = node;
+				print_verbose("allocated fbx skeleton primary / armature node for the level: " + node->node_name);
+			} else if (!state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->has_model()) {
+				print_error("bones are not mapped to an armature node for armature id: " + itos(armature_id) + " bone: " + bone->bone_name);
+				// this means bone will be removed and not used, which is safe actually and no skeleton will be created.
+			}
+		}
+
+		// setup skeleton instances if required :)
+		for (Map<uint64_t, Ref<FBXSkeleton> >::Element *skeleton_node = state.skeleton_map.front(); skeleton_node; skeleton_node = skeleton_node->next()) {
+			skeleton_node->value()->init_skeleton(state);
+		}
+	}
+
+	// build godot node tree
+	if (state.fbx_node_list.size() > 0) {
+		for (List<Ref<FBXNode> >::Element *node_element = state.fbx_node_list.front();
+				node_element;
+				node_element = node_element->next()) {
+			Ref<FBXNode> fbx_node = node_element->get();
+			MeshInstance *mesh_node = nullptr;
+			Ref<FBXMeshData> mesh_data_precached;
+
+			// check for valid geometry
+			if (fbx_node->fbx_model == nullptr) {
+				print_error("[doc] fundamental flaw, submit bug immediately with full import log with verbose logging on");
+			} else {
+				const std::vector<const FBXDocParser::Geometry *> &geometry = fbx_node->fbx_model->GetGeometry();
+				for (const FBXDocParser::Geometry *mesh : geometry) {
+					print_verbose("[doc] [" + itos(mesh->ID()) + "] mesh: " + fbx_node->node_name);
+
+					if (mesh == nullptr)
+						continue;
+
+					const FBXDocParser::MeshGeometry *mesh_geometry = dynamic_cast<const FBXDocParser::MeshGeometry *>(mesh);
+					if (mesh_geometry) {
+						uint64_t mesh_id = mesh_geometry->ID();
+
+						// this data will pre-exist if vertex weight information is found
+						if (state.renderer_mesh_data.has(mesh_id)) {
+							mesh_data_precached = state.renderer_mesh_data[mesh_id];
+						} else {
+							mesh_data_precached.instance();
+							state.renderer_mesh_data.insert(mesh_id, mesh_data_precached);
+						}
+
+						// mesh node, mesh id
+						mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model);
+						if (!state.MeshNodes.has(mesh_id)) {
+							state.MeshNodes.insert(mesh_id, fbx_node);
+						}
+					}
+
+					const FBXDocParser::ShapeGeometry *shape_geometry = dynamic_cast<const FBXDocParser::ShapeGeometry *>(mesh);
+					if (shape_geometry != nullptr) {
+						print_verbose("[doc] valid shape geometry converted");
+					}
+				}
+			}
+
+			Ref<FBXSkeleton> node_skeleton = fbx_node->skeleton_node;
+
+			if (node_skeleton.is_valid()) {
+				Skeleton *skel = node_skeleton->skeleton;
+				fbx_node->godot_node = skel;
+			} else if (mesh_node == nullptr) {
+				fbx_node->godot_node = memnew(Spatial);
+			} else {
+				fbx_node->godot_node = mesh_node;
+			}
+
+			fbx_node->godot_node->set_name(fbx_node->node_name);
+
+			// assign parent if valid
+			if (fbx_node->fbx_parent.is_valid()) {
+				fbx_node->fbx_parent->godot_node->add_child(fbx_node->godot_node);
+				fbx_node->godot_node->set_owner(state.root_owner);
+			}
+
+			// Node Transform debug, set local xform data.
+			fbx_node->godot_node->set_transform(get_unscaled_transform(fbx_node->pivot_transform->LocalTransform, state.scale));
+
+			// populate our mesh node reference
+			if (mesh_node != nullptr && mesh_data_precached.is_valid()) {
+				mesh_data_precached->godot_mesh_instance = mesh_node;
+			}
+		}
+	}
+
+	for (Map<uint64_t, Ref<FBXNode> >::Element *skin_mesh = state.MeshNodes.front(); skin_mesh; skin_mesh = skin_mesh->next()) {
+		const uint64_t mesh_id = skin_mesh->key();
+		Ref<FBXNode> fbx_node = skin_mesh->value();
+
+		ERR_CONTINUE_MSG(state.MeshSkins.has(skin_mesh->key()), "invalid skin already exists for this mesh?");
+		print_verbose("[doc] caching skin for " + itos(mesh_id) + ", mesh node name: " + fbx_node->node_name);
+		Ref<Skin> skin;
+		skin.instance();
+
+		for (Map<uint64_t, Ref<FBXBone> >::Element *elem = state.fbx_bone_map.front(); elem; elem = elem->next()) {
+			Ref<FBXBone> bone = elem->value();
+			Transform ignore_t;
+			Ref<FBXSkeleton> skeleton = bone->fbx_skeleton;
+
+			if (!bone->cluster) {
+				continue; // some bones have no skin this is OK.
+			}
+
+			Ref<FBXNode> bone_link = bone->get_link(state);
+			ERR_CONTINUE_MSG(bone_link.is_null(), "invalid skin pose bone link");
+
+			bool valid_bind = false;
+
+			Transform bind = bone->get_vertex_skin_xform(state, fbx_node->pivot_transform->GlobalTransform, valid_bind);
+			ERR_CONTINUE_MSG(!valid_bind, "invalid bind");
+
+			if (bind.basis.determinant() == 0) {
+				bind = Transform(Basis(), bind.origin);
+			}
+
+			skin->add_named_bind(bone->bone_name, get_unscaled_transform(bind, state.scale));
+		}
+
+		state.MeshSkins.insert(mesh_id, skin);
+	}
+
+	// mesh data iteration for populating skeleton mapping
+	for (Map<uint64_t, Ref<FBXMeshData> >::Element *mesh_data = state.renderer_mesh_data.front(); mesh_data; mesh_data = mesh_data->next()) {
+		Ref<FBXMeshData> mesh = mesh_data->value();
+		const uint64_t mesh_id = mesh_data->key();
+		MeshInstance *mesh_instance = mesh->godot_mesh_instance;
+		const int mesh_weights = mesh->max_weight_count;
+		Ref<FBXSkeleton> skeleton;
+		const bool valid_armature = mesh->valid_armature_id;
+		const uint64_t armature = mesh->armature_id;
+
+		if (mesh_weights > 0) {
+			// this is a bug, it means the weights were found but the skeleton wasn't
+			ERR_CONTINUE_MSG(!valid_armature, "[doc] fbx armature is missing");
+		} else {
+			continue; // safe to continue not a bug just a normal mesh
+		}
+
+		if (state.skeleton_map.has(armature)) {
+			skeleton = state.skeleton_map[armature];
+			print_verbose("[doc] armature mesh to skeleton mapping has been allocated");
+		} else {
+			print_error("[doc] unable to find armature mapping");
+		}
+
+		ERR_CONTINUE_MSG(!mesh_instance, "[doc] invalid mesh mapping for skeleton assignment");
+		ERR_CONTINUE_MSG(skeleton.is_null(), "[doc] unable to resolve the correct skeleton but we have weights!");
+
+		mesh_instance->set_skeleton_path(mesh_instance->get_path_to(skeleton->skeleton));
+		print_verbose("[doc] allocated skeleton to mesh " + mesh_instance->get_name());
+
+		// do we have a mesh skin for this mesh
+		ERR_CONTINUE_MSG(!state.MeshSkins.has(mesh_id), "no skin found for mesh");
+
+		Ref<Skin> mesh_skin = state.MeshSkins[mesh_id];
+
+		ERR_CONTINUE_MSG(mesh_skin.is_null(), "invalid skin stored in map");
+		print_verbose("[doc] allocated skin to mesh " + mesh_instance->get_name());
+		mesh_instance->set_skin(mesh_skin);
+	}
+
+	// build skin and skeleton information
+	print_verbose("[doc] Skeleton Bone count: " + itos(state.fbx_bone_map.size()));
+	const FBXDocParser::FileGlobalSettings *FBXSettings = p_document->GlobalSettingsPtr();
+
+	// Configure constraints
+	// NOTE: constraints won't be added quite yet, we don't have a real need for them *yet*. (they can be supported later on)
+	// const std::vector<uint64_t> fbx_constraints = p_document->GetConstraintStackIDs();
+
+	// get the animation FPS
+	float fps_setting = ImportUtils::get_fbx_fps(FBXSettings);
+
+	// enable animation import, only if local animation is enabled
+	if (state.enable_animation_import && (p_flags & IMPORT_ANIMATION)) {
+		// document animation stack list - get by ID so we can unload any non used animation stack
+		const std::vector<uint64_t> animation_stack = p_document->GetAnimationStackIDs();
+
+		for (uint64_t anim_id : animation_stack) {
+			FBXDocParser::LazyObject *lazyObject = p_document->GetObject(anim_id);
+			const FBXDocParser::AnimationStack *stack = lazyObject->Get<FBXDocParser::AnimationStack>();
+
+			if (stack != nullptr) {
+				String animation_name = ImportUtils::FBXNodeToName(stack->Name());
+				print_verbose("Valid animation stack has been found: " + animation_name);
+				// ReferenceTime is the same for some animations?
+				// LocalStop time is the start and end time
+				float r_start = CONVERT_FBX_TIME(stack->ReferenceStart());
+				float r_stop = CONVERT_FBX_TIME(stack->ReferenceStop());
+				float start_time = CONVERT_FBX_TIME(stack->LocalStart());
+				float end_time = CONVERT_FBX_TIME(stack->LocalStop());
+				float duration = end_time - start_time;
+
+				print_verbose("r_start " + rtos(r_start) + ", r_stop " + rtos(r_stop));
+				print_verbose("start_time" + rtos(start_time) + " end_time " + rtos(end_time));
+				print_verbose("anim duration : " + rtos(duration));
+
+				// we can safely create the animation player
+				if (state.animation_player == nullptr) {
+					print_verbose("Creating animation player");
+					state.animation_player = memnew(AnimationPlayer);
+					state.root->add_child(state.animation_player);
+					state.animation_player->set_owner(state.root_owner);
+				}
+
+				Ref<Animation> animation;
+				animation.instance();
+				animation->set_name(animation_name);
+				animation->set_length(duration);
+
+				print_verbose("Animation length: " + rtos(animation->get_length()) + " seconds");
+
+				// i think assimp was duplicating things, this lets me know to just reference or ignore this to prevent duplicate information in tracks
+				// this would mean that we would be doing three times as much work per track if my theory is correct.
+				// this was not the case but this is a good sanity check for the animation handler from the document.
+				// it also lets us know if the FBX specification massively changes the animation system, in theory such a change would make this show
+				// an fbx specification error, so best keep it in
+				// the overhead is tiny.
+				Map<uint64_t, const FBXDocParser::AnimationCurve *> CheckForDuplication;
+
+				const std::vector<const FBXDocParser::AnimationLayer *> &layers = stack->Layers();
+				print_verbose("FBX Animation layers: " + itos(layers.size()));
+				for (const FBXDocParser::AnimationLayer *layer : layers) {
+					std::vector<const FBXDocParser::AnimationCurveNode *> node_list = layer->Nodes();
+					print_verbose("Layer: " + ImportUtils::FBXNodeToName(layer->Name()) + ", " + " AnimCurveNode count " + itos(node_list.size()));
+
+					// first thing to do here is that i need to first get the animcurvenode to a Vector3
+					// we now need to put this into the track information for godot.
+					// to do this we need to know which track is what?
+
+					// target id, [ track name, [time index, vector] ]
+					// new map needs to be [ track name, keyframe_data ]
+					Map<uint64_t, Map<StringName, FBXTrack> > AnimCurveNodes;
+
+					// struct AnimTrack {
+					// 	// Animation track can be
+					// 	// visible, T, R, S
+					// 	Map<StringName, Map<uint64_t, Vector3> > animation_track;
+					// };
+
+					// Map<uint64_t, AnimTrack> AnimCurveNodes;
+
+					// so really, what does this mean to make an animtion track.
+					// we need to know what object the curves are for.
+					// we need the target ID and the target name for the track reduction.
+
+					FBXDocParser::Model::RotOrder quat_rotation_order = FBXDocParser::Model::RotOrder_EulerXYZ;
+
+					// T:: R:: S:: Visible:: Custom::
+					for (const FBXDocParser::AnimationCurveNode *curve_node : node_list) {
+						// when Curves() is called the curves are actually read, we could replace this with our own ProcessDomConnection code here if required.
+						// We may need to do this but ideally we use Curves
+						// note: when you call this there might be a delay in opening it
+						// uses mutable type to 'cache' the response until the AnimationCurveNode is cleaned up.
+						std::map<std::string, const FBXDocParser::AnimationCurve *> curves = curve_node->Curves();
+						const FBXDocParser::Object *object = curve_node->Target();
+						const FBXDocParser::Model *target = curve_node->TargetAsModel();
+						if (target == nullptr) {
+							if (object != nullptr) {
+								print_error("[doc] warning failed to find a target Model for curve: " + String(object->Name().c_str()));
+							} else {
+								//print_error("[doc] failed to resolve object");
+								continue;
+							}
+
+							continue;
+						} else {
+							//print_verbose("[doc] applied rotation order: " + itos(target->RotationOrder()));
+							quat_rotation_order = target->RotationOrder();
+						}
+
+						uint64_t target_id = target->ID();
+						String target_name = ImportUtils::FBXNodeToName(target->Name());
+
+						const FBXDocParser::PropertyTable *properties = curve_node->Props();
+						bool got_x = false, got_y = false, got_z = false;
+						float offset_x = FBXDocParser::PropertyGet<float>(properties, "d|X", got_x);
+						float offset_y = FBXDocParser::PropertyGet<float>(properties, "d|Y", got_y);
+						float offset_z = FBXDocParser::PropertyGet<float>(properties, "d|Z", got_z);
+
+						String curve_node_name = ImportUtils::FBXNodeToName(curve_node->Name());
+
+						// Reduce all curves for this node into a single container
+						// T, R, S is what we expect, although other tracks are possible
+						// like for example visibility tracks.
+
+						// We are not ordered here, we don't care about ordering, this happens automagically by godot when we insert with the
+						// key time :), so order is unimportant because the insertion will happen at a time index
+						// good to know: we do not need a list of these in another format :)
+						//Map<String, Vector<const Assimp::FBX::AnimationCurve *> > unordered_track;
+
+						// T
+						// R
+						// S
+						// Map[String, List<VECTOR>]
+
+						// So this is a reduction of the animation curve nodes
+						// We build this as a lookup, this is essentially our 'animation track'
+						//AnimCurveNodes.insert(curve_node_name, Map<uint64_t, Vector3>());
+
+						// create the animation curve information with the target id
+						// so the point of this makes a track with the name "T" for example
+						// the target ID is also set here, this means we don't need to do anything extra when we are in the 'create all animation tracks' step
+						FBXTrack &keyframe_map = AnimCurveNodes[target_id][StringName(curve_node_name)];
+
+						if (got_x && got_y && got_z) {
+							Vector3 default_value = Vector3(offset_x, offset_y, offset_z);
+							keyframe_map.default_value = default_value;
+							keyframe_map.has_default = true;
+							//print_verbose("track name: " + curve_node_name);
+							//print_verbose("xyz default: " + default_value);
+						}
+						// target id, [ track name, [time index, vector] ]
+						// Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes;
+
+						// we probably need the target id here.
+						// so map[uint64_t map]...
+						// Map<uint64_t, Vector3D> translation_keys, rotation_keys, scale_keys;
+
+						// extra const required by C++11 colon/Range operator
+						// note: do not use C++17 syntax here for dicts.
+						// this is banned in Godot.
+						for (std::pair<const std::string, const FBXDocParser::AnimationCurve *> &kvp : curves) {
+							String curve_element = ImportUtils::FBXNodeToName(kvp.first);
+							const FBXDocParser::AnimationCurve *curve = kvp.second;
+							String curve_name = ImportUtils::FBXNodeToName(curve->Name());
+							uint64_t curve_id = curve->ID();
+
+							if (CheckForDuplication.has(curve_id)) {
+								print_error("(FBX spec changed?) We found a duplicate curve being used for an alternative node - report to godot issue tracker");
+							} else {
+								CheckForDuplication.insert(curve_id, curve);
+							}
+
+							// FBX has no name for AnimCurveNode::, most of the time, not seen any with valid name here.
+							const std::map<int64_t, float> track_time = curve->GetValueTimeTrack();
+
+							if (track_time.size() > 0) {
+								for (std::pair<int64_t, float> keyframe : track_time) {
+									if (curve_element == "d|X") {
+										keyframe_map.keyframes[keyframe.first].x = keyframe.second;
+									} else if (curve_element == "d|Y") {
+										keyframe_map.keyframes[keyframe.first].y = keyframe.second;
+									} else if (curve_element == "d|Z") {
+										keyframe_map.keyframes[keyframe.first].z = keyframe.second;
+									} else {
+										//print_error("FBX Unsupported element: " + curve_element);
+									}
+
+									//print_verbose("[" + itos(target_id) + "] Keyframe added:  " + itos(keyframe_map.size()));
+
+									//print_verbose("Keyframe t:" + rtos(animation_track_time) + " v: " + rtos(keyframe.second));
+								}
+							}
+						}
+					}
+
+					// Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes;
+					// add this animation track here
+
+					// target id, [ track name, [time index, vector] ]
+					//std::map<uint64_t, std::map<StringName, FBXTrack > > AnimCurveNodes;
+					for (Map<uint64_t, Map<StringName, FBXTrack> >::Element *track = AnimCurveNodes.front(); track; track = track->next()) {
+
+						// 5 tracks
+						// current track index
+						// track count is 5
+						// track count is 5.
+						// next track id is 5.
+						const uint64_t target_id = track->key();
+						int track_idx = animation->add_track(Animation::TYPE_TRANSFORM);
+
+						// animation->track_set_path(track_idx, node_path);
+						// animation->track_set_path(track_idx, node_path);
+						Ref<FBXBone> bone;
+
+						// note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad.
+						// remember that state.fbx_bone_map[target_id] will create a new entry EVEN if you only read.
+						// this would break node animation targets, so if you change this be warned. :)
+						if (state.fbx_bone_map.has(target_id)) {
+							bone = state.fbx_bone_map[target_id];
+						}
+
+						Transform target_transform;
+
+						if (state.fbx_target_map.has(target_id)) {
+							Ref<FBXNode> node_ref = state.fbx_target_map[target_id];
+							target_transform = node_ref->pivot_transform->GlobalTransform;
+							//print_verbose("[doc] allocated animation node transform");
+						}
+
+						//int size_targets = state.fbx_target_map.size();
+						//print_verbose("Target ID map: " + itos(size_targets));
+						//print_verbose("[doc] debug bone map size: " + itos(state.fbx_bone_map.size()));
+
+						// if this is a skeleton mapped track we can just set the path for the track.
+						// todo: implement node paths here at some
+						if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) {
+
+							if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
+								Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
+								String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
+								bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
+								print_verbose("[doc] track bone path: " + bone_path);
+								NodePath path = bone_path;
+								animation->track_set_path(track_idx, path);
+							}
+						} else if (state.fbx_target_map.has(target_id)) {
+							//print_verbose("[doc] we have a valid target for a node animation");
+							Ref<FBXNode> target_node = state.fbx_target_map[target_id];
+							if (target_node.is_valid() && target_node->godot_node != nullptr) {
+								String node_path = state.root->get_path_to(target_node->godot_node);
+								NodePath path = node_path;
+								animation->track_set_path(track_idx, path);
+								//print_verbose("[doc] node animation path: " + node_path);
+							}
+						} else {
+							// note: this could actually be unsafe this means we should be careful about continuing here, if we see bizzare effects later we should disable this.
+							// I am not sure if this is unsafe or not, testing will tell us this.
+							print_error("[doc] invalid fbx target detected for this track");
+							continue;
+						}
+
+						// everything in FBX and Maya is a node therefore if this happens something is seriously broken.
+						if (!state.fbx_target_map.has(target_id)) {
+							print_error("unable to resolve this to an FBX object.");
+							continue;
+						}
+
+						Ref<FBXNode> target_node = state.fbx_target_map[target_id];
+						const FBXDocParser::Model *model = target_node->fbx_model;
+						const FBXDocParser::PropertyTable *props = model->Props();
+
+						Map<StringName, FBXTrack> &track_data = track->value();
+						FBXTrack &translation_keys = track_data[StringName("T")];
+						FBXTrack &rotation_keys = track_data[StringName("R")];
+						FBXTrack &scale_keys = track_data[StringName("S")];
+
+						double increment = 1.0f / fps_setting;
+						double time = 0.0f;
+
+						bool last = false;
+
+						Vector<Vector3> pos_values;
+						Vector<float> pos_times;
+						Vector<Vector3> scale_values;
+						Vector<float> scale_times;
+						Vector<Quat> rot_values;
+						Vector<float> rot_times;
+
+						double max_duration = 0;
+						double anim_length = animation->get_length();
+
+						for (std::pair<int64_t, Vector3> position_key : translation_keys.keyframes) {
+							pos_values.push_back(position_key.second * state.scale);
+							double animation_track_time = CONVERT_FBX_TIME(position_key.first);
+
+							if (animation_track_time > max_duration) {
+								max_duration = animation_track_time;
+							}
+
+							//print_verbose("pos keyframe: t:" + rtos(animation_track_time) + " value " + position_key.second);
+							pos_times.push_back(animation_track_time);
+						}
+
+						for (std::pair<int64_t, Vector3> scale_key : scale_keys.keyframes) {
+							scale_values.push_back(scale_key.second);
+							double animation_track_time = CONVERT_FBX_TIME(scale_key.first);
+
+							if (animation_track_time > max_duration) {
+								max_duration = animation_track_time;
+							}
+							//print_verbose("scale keyframe t:" + rtos(animation_track_time));
+							scale_times.push_back(animation_track_time);
+						}
+
+						//
+						// Pre and Post keyframe rotation handler
+						// -- Required because Maya and Autodesk <3 the pain when it comes to implementing animation code! enjoy <3
+
+						bool got_pre = false;
+						bool got_post = false;
+
+						Quat post_rotation;
+						Quat pre_rotation;
+
+						// Rotation matrix
+						const Vector3 &PreRotation = FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", got_pre);
+						const Vector3 &PostRotation = FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", got_post);
+
+						FBXDocParser::Model::RotOrder rot_order = model->RotationOrder();
+						if (got_pre) {
+							pre_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PreRotation));
+						}
+						if (got_post) {
+							post_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PostRotation));
+						}
+
+						Quat lastQuat = Quat();
+
+						for (std::pair<int64_t, Vector3> rotation_key : rotation_keys.keyframes) {
+							double animation_track_time = CONVERT_FBX_TIME(rotation_key.first);
+
+							//print_verbose("euler rotation key: " + rotation_key.second);
+							Quat rot_key_value = ImportUtils::EulerToQuaternion(quat_rotation_order, ImportUtils::deg2rad(rotation_key.second));
+
+							if (lastQuat != Quat() && rot_key_value.dot(lastQuat) < 0) {
+								rot_key_value.x = -rot_key_value.x;
+								rot_key_value.y = -rot_key_value.y;
+								rot_key_value.z = -rot_key_value.z;
+								rot_key_value.w = -rot_key_value.w;
+							}
+							// pre_post rotation possibly could fix orientation
+							Quat final_rotation = pre_rotation * rot_key_value * post_rotation;
+
+							lastQuat = final_rotation;
+
+							if (animation_track_time > max_duration) {
+								max_duration = animation_track_time;
+							}
+
+							rot_values.push_back(final_rotation);
+							rot_times.push_back(animation_track_time);
+						}
+
+						bool valid_rest = false;
+						Transform bone_rest;
+						int skeleton_bone = -1;
+						if (state.fbx_bone_map.has(target_id)) {
+							if (bone.is_valid() && bone->fbx_skeleton.is_valid()) {
+								skeleton_bone = bone->godot_bone_id;
+								if (skeleton_bone >= 0) {
+									bone_rest = bone->fbx_skeleton->skeleton->get_bone_rest(skeleton_bone);
+									valid_rest = true;
+								}
+							}
+
+							if (!valid_rest) {
+								print_verbose("invalid rest!");
+							}
+						}
+
+						const Vector3 def_pos = translation_keys.has_default ? (translation_keys.default_value * state.scale) : bone_rest.origin;
+						const Quat def_rot = rotation_keys.has_default ? ImportUtils::EulerToQuaternion(quat_rotation_order, ImportUtils::deg2rad(rotation_keys.default_value)) : bone_rest.basis.get_rotation_quat();
+						const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale();
+						print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")");
+
+						while (true) {
+							Vector3 pos = def_pos;
+							Quat rot = def_rot;
+							Vector3 scale = def_scale;
+
+							if (pos_values.size()) {
+								pos = _interpolate_track<Vector3>(pos_times, pos_values, time,
+										AssetImportAnimation::INTERP_LINEAR);
+							}
+
+							if (rot_values.size()) {
+								rot = _interpolate_track<Quat>(rot_times, rot_values, time,
+										AssetImportAnimation::INTERP_LINEAR);
+							}
+
+							if (scale_values.size()) {
+								scale = _interpolate_track<Vector3>(scale_times, scale_values, time,
+										AssetImportAnimation::INTERP_LINEAR);
+							}
+
+							// node animations must also include pivots
+							if (skeleton_bone >= 0) {
+								Transform xform = Transform();
+								xform.basis.set_quat_scale(rot, scale);
+								xform.origin = pos;
+								const Transform t = bone_rest.affine_inverse() * xform;
+
+								// populate	this again
+								rot = t.basis.get_rotation_quat();
+								rot.normalize();
+								scale = t.basis.get_scale();
+								pos = t.origin;
+							}
+
+							animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
+
+							if (last) {
+								break;
+							}
+
+							time += increment;
+							if (time > anim_length) {
+								last = true;
+								time = anim_length;
+								break;
+							}
+						}
+					}
+				}
+				state.animation_player->add_animation(animation_name, animation);
+			}
+		}
+
+		// AnimStack elements contain start stop time and name of animation
+		// AnimLayer is the current active layer of the animation (multiple layers can be active we only support 1)
+		// AnimCurveNode has a OP link back to the model which is the real node.
+		// AnimCurveNode has a direct link to AnimationCurve (of which it may have more than one)
+
+		// Store animation stack in list
+		// iterate over all AnimStacks like the cache node algorithm recursively
+		// this can then be used with ProcessDomConnection<> to link from
+		// AnimStack:: <-- (OO) --> AnimLayer:: <-- (OO) --> AnimCurveNode:: (which can OP resolve) to Model::
+	}
+
+	//
+	// Cleanup operations - explicit to prevent errors on shutdown - found that ref to ref does behave badly sometimes.
+	//
+
+	state.renderer_mesh_data.clear();
+	state.MeshSkins.clear();
+	state.fbx_target_map.clear();
+	state.fbx_node_list.clear();
+
+	for (Map<uint64_t, Ref<FBXBone> >::Element *element = state.fbx_bone_map.front(); element; element = element->next()) {
+		Ref<FBXBone> bone = element->value();
+		bone->parent_bone.unref();
+		bone->pivot_xform.unref();
+		bone->fbx_skeleton.unref();
+	}
+
+	for (Map<uint64_t, Ref<FBXSkeleton> >::Element *element = state.skeleton_map.front(); element; element = element->next()) {
+		Ref<FBXSkeleton> skel = element->value();
+		skel->fbx_node.unref();
+		skel->skeleton_bones.clear();
+	}
+
+	state.fbx_bone_map.clear();
+	state.skeleton_map.clear();
+	state.fbx_root_node.unref();
+
+	return scene_root;
+}
+
+void EditorSceneImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone,
+		ImportState &state, const FBXDocParser::Document *p_doc,
+		uint64_t p_id) {
+	const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(p_id, "Model");
+	// FBX can do an join like this
+	// Model -> SubDeformer (bone) -> Deformer (skin pose)
+	// This is important because we need to somehow link skin back to bone id in skeleton :)
+	// The rules are:
+	// A subdeformer will exist if 'limbnode' class tag present
+	// The subdeformer will not necessarily have a deformer as joints do not have one
+	for (const FBXDocParser::Connection *con : conns) {
+		// goto: bone creation
+		//print_verbose("con: " + String(con->PropertyName().c_str()));
+
+		// ignore object-property links we want the object to object links nothing else
+		if (con->PropertyName().length()) {
+			continue;
+		}
+
+		// convert connection source object into Object base class
+		const FBXDocParser::Object *const object = con->SourceObject();
+
+		if (nullptr == object) {
+			print_verbose("failed to convert source object for Model link");
+			continue;
+		}
+
+		// FBX Model::Cube, Model::Bone001, etc elements
+		// This detects if we can cast the object into this model structure.
+		const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(object);
+
+		// declare our bone element reference (invalid, unless we create a bone in this step)
+		// this lets us pass valid armature information into children objects and this is why we moved this up here
+		// previously this was created .instanced() on the same line.
+		Ref<FBXBone> bone_element;
+
+		if (model != nullptr) {
+			// model marked with limb node / casted.
+			const FBXDocParser::ModelLimbNode *const limb_node = dynamic_cast<const FBXDocParser::ModelLimbNode *>(model);
+			if (limb_node != nullptr) {
+				// Write bone into bone list for FBX
+
+				ERR_FAIL_COND_MSG(state.fbx_bone_map.has(limb_node->ID()), "[serious] duplicate LimbNode detected");
+
+				bool parent_is_bone = state.fbx_bone_map.find(p_id);
+				bone_element.instance();
+
+				// used to build the bone hierarchy in the skeleton
+				bone_element->parent_bone_id = parent_is_bone ? p_id : 0;
+				bone_element->valid_parent = parent_is_bone;
+				bone_element->limb_node = limb_node;
+
+				// parent is a node and this is the first bone
+				if (!parent_is_bone) {
+					uint64_t armature_id = p_id;
+					bone_element->valid_armature_id = true;
+					bone_element->armature_id = armature_id;
+					print_verbose("[doc] valid armature has been configured for first child: " + itos(armature_id));
+				} else if (p_parent_bone.is_valid()) {
+					if (p_parent_bone->valid_armature_id) {
+						bone_element->valid_armature_id = true;
+						bone_element->armature_id = p_parent_bone->armature_id;
+						print_verbose("[doc] bone has valid armature id:" + itos(bone_element->armature_id));
+					} else {
+						print_error("[doc] unassigned armature id: " + String(limb_node->Name().c_str()));
+					}
+				} else {
+					print_error("[doc] error is this a bone? " + String(limb_node->Name().c_str()));
+				}
+
+				if (!parent_is_bone) {
+					print_verbose("[doc] Root bone: " + bone_element->bone_name);
+				}
+
+				uint64_t limb_id = limb_node->ID();
+				const FBXDocParser::Cluster *deformer = ProcessDOMConnection<FBXDocParser::Cluster>(p_doc, limb_id);
+
+				bone_element->bone_name = ImportUtils::FBXNodeToName(model->Name());
+				bone_element->parent_bone = p_parent_bone;
+
+				if (deformer != nullptr) {
+
+					print_verbose("[doc] Mesh Cluster: " + String(deformer->Name().c_str()) + ", " + deformer->TransformLink());
+					print_verbose("fbx node: debug name: " + String(model->Name().c_str()) + "bone name: " + String(deformer->Name().c_str()));
+
+					// assign FBX animation bind pose compensation data;
+					bone_element->transform_link = deformer->TransformLink();
+					bone_element->transform_matrix = deformer->GetTransform();
+					bone_element->cluster = deformer;
+
+					// skin configures target node ID.
+					bone_element->target_node_id = deformer->TargetNode()->ID();
+					bone_element->valid_target = true;
+					bone_element->bone_id = limb_id;
+				}
+
+				// insert limb by ID into list.
+				state.fbx_bone_map.insert(limb_node->ID(), bone_element);
+			}
+
+			// recursion call - child nodes
+			BuildDocumentBones(bone_element, state, p_doc, model->ID());
+		}
+	}
+}
+
+void EditorSceneImporterFBX::BuildDocumentNodes(
+		Ref<PivotTransform> parent_transform,
+		ImportState &state,
+		const FBXDocParser::Document *p_doc,
+		uint64_t id,
+		Ref<FBXNode> parent_node) {
+
+	// tree
+	// here we get the node 0 on the root by default
+	const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(id, "Model");
+
+	// branch
+	for (const FBXDocParser::Connection *con : conns) {
+
+		// ignore object-property links
+		if (con->PropertyName().length()) {
+			// really important we document why this is ignored.
+			print_verbose("ignoring property link - no docs on why this is ignored");
+			continue;
+		}
+
+		// convert connection source object into Object base class
+		// Source objects can exist with 'null connections' this means that we only for sure know the source exists.
+		const FBXDocParser::Object *const source_object = con->SourceObject();
+
+		if (nullptr == source_object) {
+			print_verbose("failed to convert source object for Model link");
+			continue;
+		}
+
+		// FBX Model::Cube, Model::Bone001, etc elements
+		// This detects if we can cast the object into this model structure.
+		const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(source_object);
+		// model is the current node
+		if (nullptr != model) {
+			uint64_t current_node_id = model->ID();
+
+			Ref<FBXNode> new_node;
+			new_node.instance();
+			new_node->current_node_id = current_node_id;
+			new_node->node_name = ImportUtils::FBXNodeToName(model->Name());
+
+			Ref<PivotTransform> fbx_transform;
+			fbx_transform.instance();
+			fbx_transform->set_parent(parent_transform);
+			fbx_transform->set_model(model);
+			fbx_transform->debug_pivot_xform("name: " + new_node->node_name);
+			fbx_transform->Execute();
+
+			new_node->set_pivot_transform(fbx_transform);
+
+			// check if this node is a bone
+			if (state.fbx_bone_map.has(current_node_id)) {
+				Ref<FBXBone> bone = state.fbx_bone_map[current_node_id];
+				if (bone.is_valid()) {
+					bone->set_pivot_xform(fbx_transform);
+					print_verbose("allocated bone data: " + bone->bone_name);
+				}
+			}
+
+			// set the model, we can't just assign this safely
+			new_node->set_model(model);
+
+			if (parent_node.is_valid()) {
+				new_node->set_parent(parent_node);
+			} else {
+				new_node->set_parent(state.fbx_root_node);
+			}
+
+			// populate lookup tables with references
+			// [fbx_node_id, fbx_node]
+
+			state.fbx_node_list.push_back(new_node);
+			if (!state.fbx_target_map.has(new_node->current_node_id)) {
+				state.fbx_target_map[new_node->current_node_id] = new_node;
+			}
+
+			// print node name
+			print_verbose("[doc] new node " + new_node->node_name);
+
+			// sub branches
+			BuildDocumentNodes(new_node->pivot_transform, state, p_doc, current_node_id, new_node);
+		}
+	}
+}

+ 64 - 81
modules/assimp/editor_scene_importer_assimp.h → modules/fbx/editor_scene_importer_fbx.h

@@ -1,5 +1,5 @@
 /*************************************************************************/
 /*************************************************************************/
-/*  editor_scene_importer_assimp.h                                       */
+/*  editor_scene_importer_fbx.h                                          */
 /*************************************************************************/
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
 /*                           GODOT ENGINE                                */
@@ -28,10 +28,14 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#ifndef EDITOR_SCENE_IMPORTER_ASSIMP_H
-#define EDITOR_SCENE_IMPORTER_ASSIMP_H
+#ifndef EDITOR_SCENE_IMPORTER_FBX_H
+#define EDITOR_SCENE_IMPORTER_FBX_H
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
+
+#include "data/import_state.h"
+#include "tools/import_utils.h"
+
 #include "core/bind/core_bind.h"
 #include "core/bind/core_bind.h"
 #include "core/io/resource_importer.h"
 #include "core/io/resource_importer.h"
 #include "core/vector.h"
 #include "core/vector.h"
@@ -44,35 +48,16 @@
 #include "scene/resources/animation.h"
 #include "scene/resources/animation.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/resources/surface_tool.h"
 
 
-#include <assimp/matrix4x4.h>
-#include <assimp/scene.h>
-#include <assimp/types.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/LogStream.hpp>
-#include <assimp/Logger.hpp>
-#include <map>
-
-#include "import_state.h"
-#include "import_utils.h"
+#include "fbx_parser/FBXDocument.h"
+#include "fbx_parser/FBXImportSettings.h"
+#include "fbx_parser/FBXMeshGeometry.h"
+#include "fbx_parser/FBXUtil.h"
 
 
-using namespace AssimpImporter;
-
-class AssimpStream : public Assimp::LogStream {
-public:
-	// Constructor
-	AssimpStream() {}
-
-	// Destructor
-	~AssimpStream() {}
-	// Write something using your own functionality
-	void write(const char *message) {
-		print_verbose(String("Open Asset Import: ") + String(message).strip_edges());
-	}
-};
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
 
 
-class EditorSceneImporterAssimp : public EditorSceneImporter {
+class EditorSceneImporterFBX : public EditorSceneImporter {
 private:
 private:
-	GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
+	GDCLASS(EditorSceneImporterFBX, EditorSceneImporter);
 
 
 	struct AssetImportAnimation {
 	struct AssetImportAnimation {
 		enum Interpolation {
 		enum Interpolation {
@@ -83,67 +68,65 @@ private:
 		};
 		};
 	};
 	};
 
 
-	struct BoneInfo {
-		uint32_t bone;
-		float weight;
-	};
+	// ------------------------------------------------------------------------------------------------
+	template <typename T>
+	const T *ProcessDOMConnection(
+			const FBXDocParser::Document *doc,
+			uint64_t current_element,
+			bool reverse_lookup = false) {
+
+		const std::vector<const FBXDocParser::Connection *> &conns = reverse_lookup ? doc->GetConnectionsByDestinationSequenced(current_element) : doc->GetConnectionsBySourceSequenced(current_element);
+		//print_verbose("[doc] looking for " + String(element_to_find));
+		// using the temp pattern here so we can debug before it returns
+		// in some cases we return too early, with 'deformer object base class' in wrong place
+		// in assimp this means we can accidentally return too early...
+		const T *return_obj = nullptr;
+
+		for (const FBXDocParser::Connection *con : conns) {
+			const FBXDocParser::Object *source_object = con->SourceObject();
+			const FBXDocParser::Object *dest_object = con->DestinationObject();
+			if (source_object && dest_object != nullptr) {
+				//print_verbose("[doc] connection name: " + String(source_object->Name().c_str()) + ", dest: " + String(dest_object->Name().c_str()));
+				const T *temp = dynamic_cast<const T *>(reverse_lookup ? source_object : dest_object);
+				if (temp) {
+					return_obj = temp;
+				}
+			}
+		}
+
+		if (return_obj != nullptr) {
+			//print_verbose("[doc] returned valid element");
+			//print_verbose("Found object for bone");
+			return return_obj;
+		}
+
+		// safe to return nothing, need to use nullptr here as nullptr is used internally for FBX document.
+		return nullptr;
+	}
+
+	void BuildDocumentBones(Ref<FBXBone> p_parent_bone,
+			ImportState &state, const FBXDocParser::Document *p_doc,
+			uint64_t p_id);
 
 
-	Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
-			const aiNode *assimp_node, Ref<Skin> &skin,
-			Skeleton *&skeleton_assigned);
-
-	// simple object creation functions
-	Spatial *create_light(ImportState &state,
-			const String &node_name,
-			Transform &look_at_transform);
-	Spatial *create_camera(
-			ImportState &state,
-			const String &node_name,
-			Transform &look_at_transform);
-	// non recursive - linear so must not use recursive arguments
-	MeshInstance *create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform);
-	// recursive node generator
-	void _generate_node(ImportState &state, const aiNode *assimp_node);
-	void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
-			int anim_fps, Ref<Animation> animation, float ticks_per_second,
-			Skeleton *skeleton, const NodePath &node_path,
-			const String &node_name, aiBone *track_bone);
-
-	void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
-	Node *get_node_by_name(ImportState &state, String name);
-	aiBone *get_bone_from_stack(ImportState &state, aiString name);
-	Spatial *_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
+	void BuildDocumentNodes(Ref<PivotTransform> parent_transform, ImportState &state, const FBXDocParser::Document *doc, uint64_t id, Ref<FBXNode> fbx_parent);
+
+	Spatial *_generate_scene(const String &p_path, const FBXDocParser::Document *p_document,
+			const uint32_t p_flags,
+			int p_bake_fps, const int32_t p_max_bone_weights);
 
 
 	template <class T>
 	template <class T>
 	T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
 	T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
 	void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const;
 	void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const;
 
 
-	struct ImportFormat {
-		Vector<String> extensions;
-		bool is_default;
-	};
-
-protected:
-	static void _bind_methods();
-
 public:
 public:
-	EditorSceneImporterAssimp() {
-		Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE);
-		unsigned int severity = Assimp::Logger::Info | Assimp::Logger::Err | Assimp::Logger::Warn;
-		Assimp::DefaultLogger::get()->attachStream(new AssimpStream(), severity);
-	}
-	~EditorSceneImporterAssimp() {
-		Assimp::DefaultLogger::kill();
-	}
+	EditorSceneImporterFBX() {}
+	~EditorSceneImporterFBX() {}
 
 
 	virtual void get_extensions(List<String> *r_extensions) const;
 	virtual void get_extensions(List<String> *r_extensions) const;
 	virtual uint32_t get_import_flags() const;
 	virtual uint32_t get_import_flags() const;
 	virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL);
 	virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL);
-	Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path);
-
-	static void RegenerateBoneStack(ImportState &state);
-
-	void RegenerateBoneStack(ImportState &state, aiMesh *mesh);
+	void create_mesh_data_skin(ImportState &state, const Ref<FBXNode> &fbx_node, uint64_t mesh_id);
 };
 };
-#endif
-#endif
+
+#endif // TOOLS_ENABLED
+#endif // EDITOR_SCENE_IMPORTER_FBX_H

+ 283 - 0
modules/fbx/fbx_parser/ByteSwapper.h

@@ -0,0 +1,283 @@
+/*************************************************************************/
+/*  ByteSwapper.h                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Helper class tp perform various byte oder swappings
+   (e.g. little to big endian) */
+#ifndef BYTE_SWAPPER_H
+#define BYTE_SWAPPER_H
+
+#include <stdint.h>
+#include <algorithm>
+#include <locale>
+
+namespace FBXDocParser {
+// --------------------------------------------------------------------------------------
+/** Defines some useful byte order swap routines.
+ *
+ * This is required to read big-endian model formats on little-endian machines,
+ * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */
+// --------------------------------------------------------------------------------------
+class ByteSwap {
+	ByteSwap() {}
+
+public:
+	// ----------------------------------------------------------------------
+	/** Swap two bytes of data
+	 *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
+	static inline void Swap2(void *_szOut) {
+		uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+		std::swap(szOut[0], szOut[1]);
+	}
+
+	// ----------------------------------------------------------------------
+	/** Swap four bytes of data
+	 *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
+	static inline void Swap4(void *_szOut) {
+		uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+		std::swap(szOut[0], szOut[3]);
+		std::swap(szOut[1], szOut[2]);
+	}
+
+	// ----------------------------------------------------------------------
+	/** Swap eight bytes of data
+	 *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
+	static inline void Swap8(void *_szOut) {
+		uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+		std::swap(szOut[0], szOut[7]);
+		std::swap(szOut[1], szOut[6]);
+		std::swap(szOut[2], szOut[5]);
+		std::swap(szOut[3], szOut[4]);
+	}
+
+	// ----------------------------------------------------------------------
+	/** ByteSwap a float. Not a joke.
+	 *  @param[inout] fOut ehm. .. */
+	static inline void Swap(float *fOut) {
+		Swap4(fOut);
+	}
+
+	// ----------------------------------------------------------------------
+	/** ByteSwap a double. Not a joke.
+	 *  @param[inout] fOut ehm. .. */
+	static inline void Swap(double *fOut) {
+		Swap8(fOut);
+	}
+
+	// ----------------------------------------------------------------------
+	/** ByteSwap an int16t. Not a joke.
+	 *  @param[inout] fOut ehm. .. */
+	static inline void Swap(int16_t *fOut) {
+		Swap2(fOut);
+	}
+
+	static inline void Swap(uint16_t *fOut) {
+		Swap2(fOut);
+	}
+
+	// ----------------------------------------------------------------------
+	/** ByteSwap an int32t. Not a joke.
+	 *  @param[inout] fOut ehm. .. */
+	static inline void Swap(int32_t *fOut) {
+		Swap4(fOut);
+	}
+
+	static inline void Swap(uint32_t *fOut) {
+		Swap4(fOut);
+	}
+
+	// ----------------------------------------------------------------------
+	/** ByteSwap an int64t. Not a joke.
+	 *  @param[inout] fOut ehm. .. */
+	static inline void Swap(int64_t *fOut) {
+		Swap8(fOut);
+	}
+
+	static inline void Swap(uint64_t *fOut) {
+		Swap8(fOut);
+	}
+
+	// ----------------------------------------------------------------------
+	//! Templatized ByteSwap
+	//! \returns param tOut as swapped
+	template <typename Type>
+	static inline Type Swapped(Type tOut) {
+		return _swapper<Type, sizeof(Type)>()(tOut);
+	}
+
+private:
+	template <typename T, size_t size>
+	struct _swapper;
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 2> {
+	T operator()(T tOut) {
+		Swap2(&tOut);
+		return tOut;
+	}
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 4> {
+	T operator()(T tOut) {
+		Swap4(&tOut);
+		return tOut;
+	}
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 8> {
+	T operator()(T tOut) {
+		Swap8(&tOut);
+		return tOut;
+	}
+};
+
+// --------------------------------------------------------------------------------------
+// ByteSwap macros for BigEndian/LittleEndian support
+// --------------------------------------------------------------------------------------
+#if (defined AI_BUILD_BIG_ENDIAN)
+#define AI_LE(t) (t)
+#define AI_BE(t) ByteSwap::Swapped(t)
+#define AI_LSWAP2(p)
+#define AI_LSWAP4(p)
+#define AI_LSWAP8(p)
+#define AI_LSWAP2P(p)
+#define AI_LSWAP4P(p)
+#define AI_LSWAP8P(p)
+#define LE_NCONST const
+#define AI_SWAP2(p) ByteSwap::Swap2(&(p))
+#define AI_SWAP4(p) ByteSwap::Swap4(&(p))
+#define AI_SWAP8(p) ByteSwap::Swap8(&(p))
+#define AI_SWAP2P(p) ByteSwap::Swap2((p))
+#define AI_SWAP4P(p) ByteSwap::Swap4((p))
+#define AI_SWAP8P(p) ByteSwap::Swap8((p))
+#define BE_NCONST
+#else
+#define AI_BE(t) (t)
+#define AI_LE(t) ByteSwap::Swapped(t)
+#define AI_SWAP2(p)
+#define AI_SWAP4(p)
+#define AI_SWAP8(p)
+#define AI_SWAP2P(p)
+#define AI_SWAP4P(p)
+#define AI_SWAP8P(p)
+#define BE_NCONST const
+#define AI_LSWAP2(p) ByteSwap::Swap2(&(p))
+#define AI_LSWAP4(p) ByteSwap::Swap4(&(p))
+#define AI_LSWAP8(p) ByteSwap::Swap8(&(p))
+#define AI_LSWAP2P(p) ByteSwap::Swap2((p))
+#define AI_LSWAP4P(p) ByteSwap::Swap4((p))
+#define AI_LSWAP8P(p) ByteSwap::Swap8((p))
+#define LE_NCONST
+#endif
+
+namespace Intern {
+
+// --------------------------------------------------------------------------------------------
+template <typename T, bool doit>
+struct ByteSwapper {
+	void operator()(T *inout) {
+		ByteSwap::Swap(inout);
+	}
+};
+
+template <typename T>
+struct ByteSwapper<T, false> {
+	void operator()(T *) {
+	}
+};
+
+// --------------------------------------------------------------------------------------------
+template <bool SwapEndianess, typename T, bool RuntimeSwitch>
+struct Getter {
+	void operator()(T *inout, bool le) {
+		le = !le;
+		if (le) {
+			ByteSwapper<T, (sizeof(T) > 1 ? true : false)>()(inout);
+		} else
+			ByteSwapper<T, false>()(inout);
+	}
+};
+
+template <bool SwapEndianess, typename T>
+struct Getter<SwapEndianess, T, false> {
+
+	void operator()(T *inout, bool /*le*/) {
+		// static branch
+		ByteSwapper<T, (SwapEndianess && sizeof(T) > 1)>()(inout);
+	}
+};
+} // namespace Intern
+} // namespace FBXDocParser
+
+#endif // BYTE_SWAPPER_H

+ 2 - 2
thirdparty/assimp/CREDITS → modules/fbx/fbx_parser/CREDITS

@@ -60,7 +60,7 @@ The GUY who performed some of the CSM mocaps.
 Contributed fixes for the documentation and the doxygen markup
 Contributed fixes for the documentation and the doxygen markup
 
 
 - Zhao Lei
 - Zhao Lei
-Contributed several bugfixes fixing memory leaks and improving float parsing 
+Contributed several bugfixes fixing memory leaks and improving float parsing
 
 
 - sueastside
 - sueastside
 Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
 Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
@@ -129,7 +129,7 @@ Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
 Contributed the Debian build fixes ( architecture macro ).
 Contributed the Debian build fixes ( architecture macro ).
 
 
 - gellule
 - gellule
-Several LWO and LWS fixes (pivoting). 
+Several LWO and LWS fixes (pivoting).
 
 
 - Marcel Metz
 - Marcel Metz
 GCC/Linux fixes for the SimpleOpenGL sample.
 GCC/Linux fixes for the SimpleOpenGL sample.

+ 294 - 0
modules/fbx/fbx_parser/FBXAnimation.cpp

@@ -0,0 +1,294 @@
+/*************************************************************************/
+/*  FBXAnimation.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXAnimation.cpp
+ *  @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode,
+ *         Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack
+ */
+
+#include "FBXCommon.h"
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document & /*doc*/) :
+		Object(id, element, name) {
+	const ScopePtr sc = GetRequiredScope(element);
+	const ElementPtr KeyTime = GetRequiredElement(sc, "KeyTime");
+	const ElementPtr KeyValueFloat = GetRequiredElement(sc, "KeyValueFloat");
+
+	// note preserved keys and values for legacy FBXConverter.cpp
+	// we can remove this once the animation system is written
+	// and clean up this code so we do not have copies everywhere.
+	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);
+	}
+
+	// put the two lists into the map, underlying container is really just a dictionary
+	// these will always match, if not an error will throw and the file will not import
+	// this is useful because we then can report something and fix this later if it becomes an issue
+	// at this point we do not need a different count of these elements so this makes the
+	// most sense to do.
+	for (size_t x = 0; x < keys.size(); x++) {
+		keyvalues[keys[x]] = values[x];
+	}
+
+	const ElementPtr KeyAttrDataFloat = sc->GetElement("KeyAttrDataFloat");
+	if (KeyAttrDataFloat) {
+		ParseVectorDataArray(attributes, KeyAttrDataFloat);
+	}
+
+	const ElementPtr KeyAttrFlags = sc->GetElement("KeyAttrFlags");
+	if (KeyAttrFlags) {
+		ParseVectorDataArray(flags, KeyAttrFlags);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::~AnimationCurve() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr 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 ScopePtr sc = GetRequiredScope(element);
+
+	// find target node
+	const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
+	const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
+
+	for (const Connection *con : conns) {
+
+		// link should go for a property
+		if (!con->PropertyName().length()) {
+			continue;
+		}
+
+		Object *object = con->DestinationObject();
+
+		if (!object) {
+			DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring", element);
+			continue;
+		}
+
+		target = object;
+		prop = con->PropertyName();
+		break;
+	}
+
+	props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::~AnimationCurveNode() {
+	curves.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+const AnimationMap &AnimationCurveNode::Curves() const {
+	/* Lazy loaded animation curves, will only load if required */
+	if (curves.empty()) {
+		// resolve attached animation curves
+		const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
+
+		for (const Connection *con : conns) {
+			// So the advantage of having this STL boilerplate is that it's dead simple once you get it.
+			// The other advantage is casting is guaranteed to be safe and nullptr will be returned in the last step if it fails.
+			Object *ob = con->SourceObject();
+			AnimationCurve *anim_curve = dynamic_cast<AnimationCurve *>(ob);
+			ERR_CONTINUE_MSG(!anim_curve, "Failed to convert animation curve from object");
+
+			curves.insert(std::make_pair(con->PropertyName(), anim_curve));
+		}
+	}
+
+	return curves;
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+		Object(id, element, name), doc(doc) {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	// note: the props table here bears little importance and is usually absent
+	props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::~AnimationLayer() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist,
+		size_t whitelist_size /*= 0*/) const {
+	AnimationCurveNodeList nodes;
+
+	// resolve attached animation nodes
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurveNode");
+	nodes.reserve(conns.size());
+
+	for (const Connection *con : conns) {
+
+		// link should not go to a property
+		if (con->PropertyName().length()) {
+			continue;
+		}
+
+		Object *ob = con->SourceObject();
+
+		if (!ob) {
+			DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring", element);
+			continue;
+		}
+
+		const AnimationCurveNode *anim = dynamic_cast<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();
+			bool ok = false;
+			for (size_t i = 0; i < whitelist_size; ++i) {
+				if (!strcmp(s, target_prop_whitelist[i])) {
+					ok = true;
+					break;
+				}
+			}
+			if (!ok) {
+				continue;
+			}
+		}
+		nodes.push_back(anim);
+	}
+
+	return nodes;
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+		Object(id, element, name) {
+	const ScopePtr 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);
+
+	// resolve attached animation layers
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer");
+	layers.reserve(conns.size());
+
+	for (const Connection *con : conns) {
+
+		// link should not go to a property
+		if (con->PropertyName().length()) {
+			continue;
+		}
+
+		Object *ob = con->SourceObject();
+		if (!ob) {
+			DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring", element);
+			continue;
+		}
+
+		const AnimationLayer *anim = dynamic_cast<const AnimationLayer *>(ob);
+
+		if (!anim) {
+			DOMWarning("source object for ->AnimationStack link is not an AnimationLayer", element);
+			continue;
+		}
+
+		layers.push_back(anim);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::~AnimationStack() {
+	if (props != nullptr) {
+		delete props;
+		props = nullptr;
+	}
+}
+
+} // namespace FBXDocParser

+ 470 - 0
modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp

@@ -0,0 +1,470 @@
+/*************************************************************************/
+/*  FBXBinaryTokenizer.cpp                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXBinaryTokenizer.cpp
+ *  @brief Implementation of a fake lexer for binary fbx files -
+ *    we emit tokens so the parser needs almost no special handling
+ *    for binary files.
+ */
+
+#include "ByteSwapper.h"
+#include "FBXTokenizer.h"
+#include "core/print_string.h"
+
+#include <stdint.h>
+
+namespace FBXDocParser {
+//enum Flag
+//{
+//   e_unknown_0 = 1 << 0,
+//   e_unknown_1 = 1 << 1,
+//   e_unknown_2 = 1 << 2,
+//   e_unknown_3 = 1 << 3,
+//   e_unknown_4 = 1 << 4,
+//   e_unknown_5 = 1 << 5,
+//   e_unknown_6 = 1 << 6,
+//   e_unknown_7 = 1 << 7,
+//   e_unknown_8 = 1 << 8,
+//   e_unknown_9 = 1 << 9,
+//   e_unknown_10 = 1 << 10,
+//   e_unknown_11 = 1 << 11,
+//   e_unknown_12 = 1 << 12,
+//   e_unknown_13 = 1 << 13,
+//   e_unknown_14 = 1 << 14,
+//   e_unknown_15 = 1 << 15,
+//   e_unknown_16 = 1 << 16,
+//   e_unknown_17 = 1 << 17,
+//   e_unknown_18 = 1 << 18,
+//   e_unknown_19 = 1 << 19,
+//   e_unknown_20 = 1 << 20,
+//   e_unknown_21 = 1 << 21,
+//   e_unknown_22 = 1 << 22,
+//   e_unknown_23 = 1 << 23,
+//   e_flag_field_size_64_bit = 1 << 24, // Not sure what is
+//   e_unknown_25 = 1 << 25,
+//   e_unknown_26 = 1 << 26,
+//   e_unknown_27 = 1 << 27,
+//   e_unknown_28 = 1 << 28,
+//   e_unknown_29 = 1 << 29,
+//   e_unknown_30 = 1 << 30,
+//   e_unknown_31 = 1 << 31
+//};
+//
+//bool check_flag(uint32_t flags, Flag to_check)
+//{
+//	return (flags & to_check) != 0;
+//}
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) :
+		sbegin(sbegin),
+		send(send),
+		type(type),
+		line(offset),
+		column(BINARY_MARKER) {
+#ifdef DEBUG_ENABLED
+	contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
+#endif
+	// calc length
+	// measure from sBegin to sEnd and validate?
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// signal tokenization error
+void TokenizeError(const std::string &message, size_t offset) {
+	print_error("[FBX-Tokenize] " + String(message.c_str()) + ", offset " + itos(offset));
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t Offset(const char *begin, const char *cursor) {
+	//ai_assert(begin <= cursor);
+
+	return cursor - begin;
+}
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string &message, const char *begin, const char *cursor) {
+	TokenizeError(message, Offset(begin, cursor));
+}
+
+// ------------------------------------------------------------------------------------------------
+uint32_t ReadWord(const char *input, const char *&cursor, const char *end) {
+	const size_t k_to_read = sizeof(uint32_t);
+	if (Offset(cursor, end) < k_to_read) {
+		TokenizeError("cannot ReadWord, out of bounds", input, cursor);
+	}
+
+	uint32_t word;
+	::memcpy(&word, cursor, 4);
+	AI_SWAP4(word);
+
+	cursor += k_to_read;
+
+	return word;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint64_t ReadDoubleWord(const char *input, const char *&cursor, const char *end) {
+	const size_t k_to_read = sizeof(uint64_t);
+	if (Offset(cursor, end) < k_to_read) {
+		TokenizeError("cannot ReadDoubleWord, out of bounds", input, cursor);
+	}
+
+	uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/;
+	::memcpy(&dword, cursor, sizeof(uint64_t));
+	AI_SWAP8(dword);
+
+	cursor += k_to_read;
+
+	return dword;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint8_t ReadByte(const char *input, const char *&cursor, const char *end) {
+	if (Offset(cursor, end) < sizeof(uint8_t)) {
+		TokenizeError("cannot ReadByte, out of bounds", input, cursor);
+	}
+
+	uint8_t word; /* = *reinterpret_cast< const uint8_t* >( cursor )*/
+	::memcpy(&word, cursor, sizeof(uint8_t));
+	++cursor;
+
+	return word;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const char *input,
+		const char *&cursor, const char *end, bool long_length = false, bool allow_null = false) {
+	const uint32_t len_len = long_length ? 4 : 1;
+	if (Offset(cursor, end) < len_len) {
+		TokenizeError("cannot ReadString, out of bounds reading length", input, cursor);
+	}
+
+	const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end);
+
+	if (Offset(cursor, end) < length) {
+		TokenizeError("cannot ReadString, length is out of bounds", input, cursor);
+	}
+
+	sbegin_out = cursor;
+	cursor += length;
+
+	send_out = cursor;
+
+	if (!allow_null) {
+		for (unsigned int i = 0; i < length; ++i) {
+			if (sbegin_out[i] == '\0') {
+				TokenizeError("failed ReadString, unexpected NUL character in string", input, cursor);
+			}
+		}
+	}
+
+	return length;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end) {
+	if (Offset(cursor, end) < 1) {
+		TokenizeError("cannot ReadData, out of bounds reading length", input, cursor);
+	}
+
+	const char type = *cursor;
+	sbegin_out = cursor++;
+
+	switch (type) {
+		// 16 bit int
+		case 'Y':
+			cursor += 2;
+			break;
+
+			// 1 bit bool flag (yes/no)
+		case 'C':
+			cursor += 1;
+			break;
+
+			// 32 bit int
+		case 'I':
+			// <- fall through
+
+			// float
+		case 'F':
+			cursor += 4;
+			break;
+
+			// double
+		case 'D':
+			cursor += 8;
+			break;
+
+			// 64 bit int
+		case 'L':
+			cursor += 8;
+			break;
+
+			// note: do not write cursor += ReadWord(...cursor) as this would be UB
+
+			// raw binary data
+		case 'R': {
+			const uint32_t length = ReadWord(input, cursor, end);
+			cursor += length;
+			break;
+		}
+
+		case 'b':
+			// TODO: what is the 'b' type code? Right now we just skip over it /
+			// take the full range we could get
+			cursor = end;
+			break;
+
+			// array of *
+		case 'f':
+		case 'd':
+		case 'l':
+		case 'i':
+		case 'c': {
+			const uint32_t length = ReadWord(input, cursor, end);
+			const uint32_t encoding = ReadWord(input, cursor, end);
+
+			const uint32_t comp_len = ReadWord(input, cursor, end);
+
+			// compute length based on type and check against the stored value
+			if (encoding == 0) {
+				uint32_t stride = 0;
+				switch (type) {
+					case 'f':
+					case 'i':
+						stride = 4;
+						break;
+
+					case 'd':
+					case 'l':
+						stride = 8;
+						break;
+
+					case 'c':
+						stride = 1;
+						break;
+
+					default:
+						break;
+				};
+				//ai_assert(stride > 0);
+				if (length * stride != comp_len) {
+					TokenizeError("cannot ReadData, calculated data stride differs from what the file claims", input, cursor);
+				}
+			}
+			// zip/deflate algorithm (encoding==1)? take given length. anything else? die
+			else if (encoding != 1) {
+				TokenizeError("cannot ReadData, unknown encoding", input, cursor);
+			}
+			cursor += comp_len;
+			break;
+		}
+
+			// string
+		case 'S': {
+			const char *sb, *se;
+			// 0 characters can legally happen in such strings
+			ReadString(sb, se, input, cursor, end, true, true);
+			break;
+		}
+		default:
+			TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1), input, cursor);
+	}
+
+	if (cursor > end) {
+		TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1), input, cursor);
+	}
+
+	// the type code is contained in the returned range
+	send_out = cursor;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits) {
+	// the first word contains the offset at which this block ends
+	const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+	// we may get 0 if reading reached the end of the file -
+	// fbx files have a mysterious extra footer which I don't know
+	// how to extract any information from, but at least it always
+	// starts with a 0.
+	if (!end_offset) {
+		return false;
+	}
+
+	if (end_offset > Offset(input, end)) {
+		TokenizeError("block offset is out of range", input, cursor);
+	} else if (end_offset < Offset(input, cursor)) {
+		TokenizeError("block offset is negative out of range", input, cursor);
+	}
+
+	// the second data word contains the number of properties in the scope
+	const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+	// the third data word contains the length of the property list
+	const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+	// now comes the name of the scope/key
+	const char *sbeg, *send;
+	ReadString(sbeg, send, input, cursor, end);
+
+	output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor)));
+
+	// now come the individual properties
+	const char *begin_cursor = cursor;
+	for (unsigned int i = 0; i < prop_count; ++i) {
+		ReadData(sbeg, send, input, cursor, begin_cursor + prop_length);
+
+		output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor)));
+
+		if (i != prop_count - 1) {
+			output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor)));
+		}
+	}
+
+	if (Offset(begin_cursor, cursor) != prop_length) {
+		TokenizeError("property length not reached, something is wrong", input, cursor);
+	}
+
+	// at the end of each nested block, there is a NUL record to indicate
+	// that the sub-scope exists (i.e. to distinguish between P: and P : {})
+	// this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit.
+	const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t) * 3 + 1) : (sizeof(uint32_t) * 3 + 1);
+
+	if (Offset(input, cursor) < end_offset) {
+		if (end_offset - Offset(input, cursor) < sentinel_block_length) {
+			TokenizeError("insufficient padding bytes at block end", input, cursor);
+		}
+
+		output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor)));
+
+		// XXX this is vulnerable to stack overflowing ..
+		while (Offset(input, cursor) < end_offset - sentinel_block_length) {
+			ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
+		}
+		output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor)));
+
+		for (unsigned int i = 0; i < sentinel_block_length; ++i) {
+			if (cursor[i] != '\0') {
+				TokenizeError("failed to read nested block sentinel, expected all bytes to be 0", input, cursor);
+			}
+		}
+		cursor += sentinel_block_length;
+	}
+
+	if (Offset(input, cursor) != end_offset) {
+		TokenizeError("scope length not reached, something is wrong", input, cursor);
+	}
+
+	return true;
+}
+
+} // anonymous namespace
+
+// ------------------------------------------------------------------------------------------------
+// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length) {
+
+	if (length < 0x1b) {
+		//TokenizeError("file is too short",0);
+	}
+
+	//uint32_t offset = 0x15;
+	/*    const char* cursor = input + 0x15;
+    const uint32_t flags = ReadWord(input, cursor, input + length);
+    const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused
+    const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/
+
+	if (strncmp(input, "Kaydara FBX Binary", 18)) {
+		TokenizeError("magic bytes not found", 0);
+	}
+
+	const char *cursor = input + 18;
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	/*Result ignored*/ ReadByte(input, cursor, input + length);
+	const uint32_t version = ReadWord(input, cursor, input + length);
+	print_verbose("FBX Version: " + itos(version));
+	//ASSIMP_LOG_DEBUG_F("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;
+		}
+	}
+}
+
+} // namespace FBXDocParser

+ 110 - 0
modules/fbx/fbx_parser/FBXCommon.h

@@ -0,0 +1,110 @@
+/*************************************************************************/
+/*  FBXCommon.h                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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 FBXCommon.h
+* Some useful constants and enums for dealing with FBX files.
+*/
+#ifndef FBX_COMMON_H
+#define FBX_COMMON_H
+
+#include <string>
+
+namespace FBXDocParser {
+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
+};
+
+enum TransformInheritance {
+	Transform_RrSs = 0,
+	Transform_RSrs = 1,
+	Transform_Rrs = 2,
+	TransformInheritance_MAX // end-of-enum sentinel
+};
+} // namespace FBXDocParser
+
+#endif // FBX_COMMON_H

+ 279 - 0
modules/fbx/fbx_parser/FBXDeformer.cpp

@@ -0,0 +1,279 @@
+/*************************************************************************/
+/*  FBXDeformer.cpp                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXNoteAttribute.cpp
+ *  @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "core/math/math_funcs.h"
+#include "core/math/transform.h"
+
+#include <iostream>
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name) {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+	props = GetPropertyTable(doc, "Deformer.Fbx" + classname, element, sc, true);
+}
+
+// ------------------------------------------------------------------------------------------------
+Deformer::~Deformer() {
+}
+
+Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name) {
+	const ScopePtr sc = GetRequiredScope(element);
+	const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+	// used something.fbx as this is a cache name.
+	props = GetPropertyTable(doc, "Something.Fbx" + classname, element, sc, true);
+}
+
+Constraint::~Constraint() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Deformer(id, element, doc, name), valid_transformAssociateModel(false) {
+	const ScopePtr sc = GetRequiredScope(element);
+	//    for( auto element : sc.Elements())
+	//    {
+	//        std::cout << "cluster element: " << element.first << std::endl;
+	//    }
+	//
+	//    element: Indexes
+	//    element: Transform
+	//    element: TransformAssociateModel
+	//    element: TransformLink
+	//    element: UserData
+	//    element: Version
+	//    element: Weights
+
+	const ElementPtr Indexes = sc->GetElement("Indexes");
+	const ElementPtr Weights = sc->GetElement("Weights");
+
+	const ElementPtr TransformAssociateModel = sc->GetElement("TransformAssociateModel");
+	if (TransformAssociateModel != nullptr) {
+		//Transform t = ReadMatrix(*TransformAssociateModel);
+		link_mode = SkinLinkMode_Additive;
+		valid_transformAssociateModel = true;
+	} else {
+		link_mode = SkinLinkMode_Normalized;
+		valid_transformAssociateModel = false;
+	}
+
+	const ElementPtr Transform = GetRequiredElement(sc, "Transform", element);
+	const ElementPtr TransformLink = GetRequiredElement(sc, "TransformLink", element);
+
+	// todo: check if we need this
+	//const Element& TransformAssociateModel = GetRequiredElement(sc, "TransformAssociateModel", &element);
+
+	transform = ReadMatrix(Transform);
+	transformLink = ReadMatrix(TransformLink);
+
+	// it is actually possible that there be Deformer's with no weights
+	if (!!Indexes != !!Weights) {
+		DOMError("either Indexes or Weights are missing from Cluster", element);
+	}
+
+	if (Indexes) {
+		ParseVectorDataArray(indices, Indexes);
+		ParseVectorDataArray(weights, Weights);
+	}
+
+	if (indices.size() != weights.size()) {
+		DOMError("sizes of index and weight array don't match up", element);
+	}
+
+	// read assigned node
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Model");
+	for (const Connection *con : conns) {
+		const Model *mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
+		if (mod) {
+			node = mod;
+			break;
+		}
+	}
+
+	if (!node) {
+		DOMError("failed to read target Node for Cluster", element);
+		node = nullptr;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Cluster::~Cluster() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Deformer(id, element, doc, name), accuracy(0.0f) {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	// keep this it is used for debugging and any FBX format changes
+	// for (auto element : sc.Elements()) {
+	// 	std::cout << "skin element: " << element.first << std::endl;
+	// }
+
+	const ElementPtr Link_DeformAcuracy = sc->GetElement("Link_DeformAcuracy");
+	if (Link_DeformAcuracy) {
+		accuracy = ParseTokenAsFloat(GetRequiredToken(Link_DeformAcuracy, 0));
+	}
+
+	const ElementPtr SkinType = sc->GetElement("SkinningType");
+
+	if (SkinType) {
+		std::string skin_type = ParseTokenAsString(GetRequiredToken(SkinType, 0));
+
+		if (skin_type == "Linear") {
+			skinType = Skin_Linear;
+		} else if (skin_type == "Rigid") {
+			skinType = Skin_Rigid;
+		} else if (skin_type == "DualQuaternion") {
+			skinType = Skin_DualQuaternion;
+		} else if (skin_type == "Blend") {
+			skinType = Skin_Blend;
+		} else {
+			print_error("[doc:skin] could not find valid skin type: " + String(skin_type.c_str()));
+		}
+	}
+
+	// resolve assigned clusters
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+
+	//
+
+	clusters.reserve(conns.size());
+	for (const Connection *con : conns) {
+		const Cluster *cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element);
+		if (cluster) {
+			clusters.push_back(cluster);
+			continue;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Skin::~Skin() {
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Deformer(id, element, doc, name) {
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+	blendShapeChannels.reserve(conns.size());
+	for (const Connection *con : conns) {
+		const BlendShapeChannel *bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
+		if (bspc) {
+			blendShapeChannels.push_back(bspc);
+			continue;
+		}
+	}
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::~BlendShape() {
+}
+// ------------------------------------------------------------------------------------------------
+BlendShapeChannel::BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Deformer(id, element, doc, name) {
+	const ScopePtr sc = GetRequiredScope(element);
+	const ElementPtr DeformPercent = sc->GetElement("DeformPercent");
+	if (DeformPercent) {
+		percent = ParseTokenAsFloat(GetRequiredToken(DeformPercent, 0));
+	}
+	const ElementPtr FullWeights = sc->GetElement("FullWeights");
+	if (FullWeights) {
+		ParseVectorDataArray(fullWeights, FullWeights);
+	}
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry");
+	shapeGeometries.reserve(conns.size());
+	for (const Connection *con : conns) {
+		const ShapeGeometry *const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
+		if (sg) {
+			shapeGeometries.push_back(sg);
+			continue;
+		}
+	}
+}
+// ------------------------------------------------------------------------------------------------
+BlendShapeChannel::~BlendShapeChannel() {
+}
+// ------------------------------------------------------------------------------------------------
+} // namespace FBXDocParser

+ 699 - 0
modules/fbx/fbx_parser/FBXDocument.cpp

@@ -0,0 +1,699 @@
+/*************************************************************************/
+/*  FBXDocument.cpp                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXDocument.cpp
+ *  @brief Implementation of the FBX DOM classes
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "FBXUtil.h"
+
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::LazyObject(uint64_t id, const ElementPtr element, const Document &doc) :
+		doc(doc), element(element), id(id), flags() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::~LazyObject() {
+	object.reset();
+}
+
+ObjectPtr LazyObject::LoadObject() {
+	if (IsBeingConstructed() || FailedToConstruct()) {
+		return nullptr;
+	}
+
+	if (object) {
+		return object.get();
+	}
+
+	TokenPtr key = element->KeyToken();
+	ERR_FAIL_COND_V(!key, nullptr);
+	const TokenList &tokens = element->Tokens();
+
+	if (tokens.size() < 3) {
+		//DOMError("expected at least 3 tokens: id, name and class tag",&element);
+		return nullptr;
+	}
+
+	const char *err = nullptr;
+	std::string name = ParseTokenAsString(tokens[1], err);
+	if (err) {
+		DOMError(err, element);
+	}
+
+	// small fix for binary reading: binary fbx files don't use
+	// prefixes such as Model:: in front of their names. The
+	// loading code expects this at many places, though!
+	// so convert the binary representation (a 0x0001) to the
+	// double colon notation.
+	if (tokens[1]->IsBinary()) {
+		for (size_t i = 0; i < name.length(); ++i) {
+			if (name[i] == 0x0 && name[i + 1] == 0x1) {
+				name = name.substr(i + 2) + "::" + name.substr(0, i);
+			}
+		}
+	}
+
+	const std::string classtag = ParseTokenAsString(tokens[2], err);
+	if (err) {
+		DOMError(err, element);
+	}
+
+	// prevent recursive calls
+	flags |= BEING_CONSTRUCTED;
+
+	// this needs to be relatively fast since it happens a lot,
+	// so avoid constructing strings all the time.
+	const char *obtype = key->begin();
+	const size_t length = static_cast<size_t>(key->end() - key->begin());
+
+	if (!strncmp(obtype, "Pose", length)) {
+		object.reset(new FbxPose(id, element, doc, name));
+	} else if (!strncmp(obtype, "Geometry", length)) {
+		if (!strcmp(classtag.c_str(), "Mesh")) {
+			object.reset(new MeshGeometry(id, element, name, doc));
+		}
+		if (!strcmp(classtag.c_str(), "Shape")) {
+			object.reset(new ShapeGeometry(id, element, name, doc));
+		}
+		if (!strcmp(classtag.c_str(), "Line")) {
+			object.reset(new LineGeometry(id, element, name, doc));
+		}
+	} else if (!strncmp(obtype, "NodeAttribute", length)) {
+		if (!strcmp(classtag.c_str(), "Camera")) {
+			object.reset(new Camera(id, element, doc, name));
+		} else if (!strcmp(classtag.c_str(), "CameraSwitcher")) {
+			object.reset(new CameraSwitcher(id, element, doc, name));
+		} else if (!strcmp(classtag.c_str(), "Light")) {
+			object.reset(new Light(id, element, doc, name));
+		} else if (!strcmp(classtag.c_str(), "Null")) {
+			object.reset(new Null(id, element, doc, name));
+		} else if (!strcmp(classtag.c_str(), "LimbNode")) {
+			// This is an older format for bones
+			// this is what blender uses I believe
+			object.reset(new LimbNode(id, element, doc, name));
+		}
+	} else if (!strncmp(obtype, "Constraint", length)) {
+		object.reset(new Constraint(id, element, doc, name));
+	} else if (!strncmp(obtype, "Deformer", length)) {
+		if (!strcmp(classtag.c_str(), "Cluster")) {
+			object.reset(new Cluster(id, element, doc, name));
+		} else if (!strcmp(classtag.c_str(), "Skin")) {
+
+			object.reset(new Skin(id, element, doc, name));
+		} else if (!strcmp(classtag.c_str(), "BlendShape")) {
+			object.reset(new BlendShape(id, element, doc, name));
+		} else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) {
+			object.reset(new BlendShapeChannel(id, element, doc, name));
+		}
+	} else if (!strncmp(obtype, "Model", length)) {
+		// Model is normal node
+
+		// LimbNode model is a 'bone' node.
+		if (!strcmp(classtag.c_str(), "LimbNode")) {
+			object.reset(new ModelLimbNode(id, element, doc, name));
+
+		} else if (strcmp(classtag.c_str(), "IKEffector") && strcmp(classtag.c_str(), "FKEffector")) {
+			// FK and IK effectors are not supporte
+			object.reset(new Model(id, element, doc, name));
+		}
+	} else if (!strncmp(obtype, "Material", length)) {
+		object.reset(new Material(id, element, doc, name));
+	} else if (!strncmp(obtype, "Texture", length)) {
+		object.reset(new Texture(id, element, doc, name));
+	} else if (!strncmp(obtype, "LayeredTexture", length)) {
+		object.reset(new LayeredTexture(id, element, doc, name));
+	} else if (!strncmp(obtype, "Video", length)) {
+		object.reset(new Video(id, element, doc, name));
+	} else if (!strncmp(obtype, "AnimationStack", length)) {
+		object.reset(new AnimationStack(id, element, name, doc));
+	} else if (!strncmp(obtype, "AnimationLayer", length)) {
+		object.reset(new AnimationLayer(id, element, name, doc));
+	} else if (!strncmp(obtype, "AnimationCurve", length)) {
+		object.reset(new AnimationCurve(id, element, name, doc));
+	} else if (!strncmp(obtype, "AnimationCurveNode", length)) {
+		object.reset(new AnimationCurveNode(id, element, name, doc));
+	} else {
+		ERR_FAIL_V_MSG(nullptr, "FBX contains unsupported object: " + String(obtype));
+	}
+
+	flags &= ~BEING_CONSTRUCTED;
+
+	return object.get();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::Object(uint64_t id, const ElementPtr element, const std::string &name) :
+		element(element), name(name), id(id) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::~Object() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::FileGlobalSettings(const Document &doc, const PropertyTable *props) :
+		props(props), doc(doc) {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::~FileGlobalSettings() {
+	if (props != nullptr) {
+		delete props;
+		props = nullptr;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Document::Document(const Parser &parser, const ImportSettings &settings) :
+		settings(settings), parser(parser), SafeToImport(false) {
+	// Cannot use array default initialization syntax because vc8 fails on it
+	for (unsigned int &timeStamp : creationTimeStamp) {
+		timeStamp = 0;
+	}
+
+	// we must check if we can read the header version safely, if its outdated then drop it.
+	if (ReadHeader()) {
+		SafeToImport = true;
+		ReadPropertyTemplates();
+
+		ReadGlobalSettings();
+
+		// This order is important, connections need parsed objects to check
+		// whether connections are ok or not. Objects may not be evaluated yet,
+		// though, since this may require valid connections.
+		ReadObjects();
+		ReadConnections();
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Document::~Document() {
+	for (PropertyTemplateMap::value_type v : templates) {
+		delete v.second;
+	}
+
+	for (ObjectMap::value_type &v : objects) {
+		delete v.second;
+	}
+
+	for (ConnectionMap::value_type &v : src_connections) {
+		delete v.second;
+	}
+
+	// clear globals import pointer
+	globals.reset();
+}
+
+// ------------------------------------------------------------------------------------------------
+static const unsigned int LowerSupportedVersion = 7100;
+static const unsigned int UpperSupportedVersion = 7700;
+
+bool Document::ReadHeader() {
+	// Read ID objects from "Objects" section
+	const ScopePtr sc = parser.GetRootScope();
+	const ElementPtr ehead = sc->GetElement("FBXHeaderExtension");
+	if (!ehead || !ehead->Compound()) {
+		DOMError("no FBXHeaderExtension dictionary found");
+	}
+
+	const ScopePtr shead = ehead->Compound();
+	fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead, "FBXVersion", ehead), 0));
+
+	// While we may have some success with newer files, we don't support
+	// the older 6.n fbx format
+	if (fbxVersion < LowerSupportedVersion) {
+		DOMWarning("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013, you can re-export using Maya, 3DS, blender or Autodesk FBX tool");
+		return false;
+	}
+	if (fbxVersion > UpperSupportedVersion) {
+		DOMWarning("unsupported, newer format version, supported are only FBX 2011, up to FBX 2020"
+				   " trying to read it nevertheless");
+	}
+
+	const ElementPtr ecreator = shead->GetElement("Creator");
+	if (ecreator) {
+		creator = ParseTokenAsString(GetRequiredToken(ecreator, 0));
+	}
+
+	const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp");
+	if (etimestamp && etimestamp->Compound()) {
+		const ScopePtr stimestamp = etimestamp->Compound();
+		creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Year"), 0));
+		creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Month"), 0));
+		creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Day"), 0));
+		creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Hour"), 0));
+		creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Minute"), 0));
+		creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Second"), 0));
+		creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Millisecond"), 0));
+	}
+
+	return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadGlobalSettings() {
+	ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported");
+
+	const ScopePtr sc = parser.GetRootScope();
+	const ElementPtr ehead = sc->GetElement("GlobalSettings");
+	if (nullptr == ehead || !ehead->Compound()) {
+		DOMWarning("no GlobalSettings dictionary found");
+		globals = std::make_shared<FileGlobalSettings>(*this, new PropertyTable());
+		return;
+	}
+
+	const PropertyTable *props = GetPropertyTable(*this, "", ehead, ehead->Compound(), true);
+
+	//double v = PropertyGet<float>( *props, std::string("UnitScaleFactor"), 1.0 );
+
+	if (!props) {
+		DOMError("GlobalSettings dictionary contains no property table");
+	}
+
+	globals = std::make_shared<FileGlobalSettings>(*this, props);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadObjects() {
+	// read ID objects from "Objects" section
+	const ScopePtr sc = parser.GetRootScope();
+	const ElementPtr eobjects = sc->GetElement("Objects");
+	if (!eobjects || !eobjects->Compound()) {
+		DOMError("no Objects dictionary found");
+	}
+
+	// add a dummy entry to represent the Model::RootNode object (id 0),
+	// which is only indirectly defined in the input file
+	objects[0] = new LazyObject(0L, eobjects, *this);
+
+	const ScopePtr sobjects = eobjects->Compound();
+	for (const ElementMap::value_type &iter : sobjects->Elements()) {
+
+		// extract ID
+		const TokenList &tok = iter.second->Tokens();
+
+		if (tok.empty()) {
+			DOMError("expected ID after object key", iter.second);
+		}
+
+		const char *err;
+		const uint64_t id = ParseTokenAsID(tok[0], err);
+		if (err) {
+			DOMError(err, iter.second);
+		}
+
+		// id=0 is normally implicit
+		if (id == 0L) {
+			DOMError("encountered object with implicitly defined id 0", iter.second);
+		}
+
+		if (objects.find(id) != objects.end()) {
+			DOMWarning("encountered duplicate object id, ignoring first occurrence", iter.second);
+		}
+
+		objects[id] = new LazyObject(id, iter.second, *this);
+
+		// grab all animation stacks upfront since there is no listing of them
+		if (!strcmp(iter.first.c_str(), "AnimationStack")) {
+			animationStacks.push_back(id);
+		} else if (!strcmp(iter.first.c_str(), "Constraint")) {
+			constraints.push_back(id);
+		} else if (!strcmp(iter.first.c_str(), "Pose")) {
+			bind_poses.push_back(id);
+		} else if (!strcmp(iter.first.c_str(), "Material")) {
+			materials.push_back(id);
+		} else if (!strcmp(iter.first.c_str(), "Deformer")) {
+			TokenPtr key = iter.second->KeyToken();
+			ERR_CONTINUE_MSG(!key, "[parser bug] invalid token key for deformer");
+			const TokenList &tokens = iter.second->Tokens();
+			const std::string class_tag = ParseTokenAsString(tokens[2], err);
+
+			if (err) {
+				DOMError(err, iter.second);
+			}
+
+			if (class_tag == "Skin") {
+				//print_verbose("registered skin:" + itos(id));
+				skins.push_back(id);
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadPropertyTemplates() {
+	const ScopePtr sc = parser.GetRootScope();
+	// read property templates from "Definitions" section
+	const ElementPtr edefs = sc->GetElement("Definitions");
+	if (!edefs || !edefs->Compound()) {
+		DOMWarning("no Definitions dictionary found");
+		return;
+	}
+
+	const ScopePtr sdefs = edefs->Compound();
+	const ElementCollection otypes = sdefs->GetCollection("ObjectType");
+	for (ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
+		const ElementPtr el = (*it).second;
+		const ScopePtr sc_2 = el->Compound();
+		if (!sc_2) {
+			DOMWarning("expected nested scope in ObjectType, ignoring", el);
+			continue;
+		}
+
+		const TokenList &tok = el->Tokens();
+		if (tok.empty()) {
+			DOMWarning("expected name for ObjectType element, ignoring", el);
+			continue;
+		}
+
+		const std::string &oname = ParseTokenAsString(tok[0]);
+
+		const ElementCollection templs = sc_2->GetCollection("PropertyTemplate");
+		for (ElementMap::const_iterator iter = templs.first; iter != templs.second; ++iter) {
+			const ElementPtr el_2 = (*iter).second;
+			const ScopePtr sc_3 = el_2->Compound();
+			if (!sc_3) {
+				DOMWarning("expected nested scope in PropertyTemplate, ignoring", el);
+				continue;
+			}
+
+			const TokenList &tok_2 = el_2->Tokens();
+			if (tok_2.empty()) {
+				DOMWarning("expected name for PropertyTemplate element, ignoring", el);
+				continue;
+			}
+
+			const std::string &pname = ParseTokenAsString(tok_2[0]);
+
+			const ElementPtr Properties70 = sc_3->GetElement("Properties70");
+			if (Properties70) {
+				// PropertyTable(const ElementPtr element, const PropertyTable* templateProps);
+				const PropertyTable *props = new PropertyTable(Properties70, nullptr);
+
+				templates[oname + "." + pname] = props;
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadConnections() {
+	const ScopePtr sc = parser.GetRootScope();
+
+	// read property templates from "Definitions" section
+	const ElementPtr econns = sc->GetElement("Connections");
+	if (!econns || !econns->Compound()) {
+		DOMError("no Connections dictionary found");
+	}
+
+	uint64_t insertionOrder = 0l;
+	const ScopePtr sconns = econns->Compound();
+	const ElementCollection conns = sconns->GetCollection("C");
+	for (ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
+		const ElementPtr el = (*it).second;
+		const std::string &type = ParseTokenAsString(GetRequiredToken(el, 0));
+
+		// PP = property-property connection, ignored for now
+		// (tokens: "PP", ID1, "Property1", ID2, "Property2")
+		if (type == "PP") {
+			continue;
+		}
+
+		const uint64_t src = ParseTokenAsID(GetRequiredToken(el, 1));
+		const uint64_t dest = ParseTokenAsID(GetRequiredToken(el, 2));
+
+		// OO = object-object connection
+		// OP = object-property connection, in which case the destination property follows the object ID
+		const std::string &prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el, 3)) : "");
+
+		if (objects.find(src) == objects.end()) {
+			DOMWarning("source object for connection does not exist", el);
+			continue;
+		}
+
+		// dest may be 0 (root node) but we added a dummy object before
+		if (objects.find(dest) == objects.end()) {
+			DOMWarning("destination object for connection does not exist", el);
+			continue;
+		}
+
+		// add new connection
+		const Connection *const c = new Connection(insertionOrder++, src, dest, prop, *this);
+		src_connections.insert(ConnectionMap::value_type(src, c));
+		dest_connections.insert(ConnectionMap::value_type(dest, c));
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<const AnimationStack *> &Document::AnimationStacks() const {
+	if (!animationStacksResolved.empty() || animationStacks.empty()) {
+		return animationStacksResolved;
+	}
+
+	animationStacksResolved.reserve(animationStacks.size());
+	for (uint64_t id : animationStacks) {
+		LazyObject *lazy = GetObject(id);
+
+		// Two things happen here:
+		// We cast internally an Object PTR to an Animation Stack PTR
+		// We return invalid weak_ptrs for objects which are invalid
+
+		const AnimationStack *stack = lazy->Get<AnimationStack>();
+		ERR_CONTINUE_MSG(!stack, "invalid ptr to AnimationStack - conversion failure");
+
+		// We push back the weak reference :) to keep things simple, as ownership is on the parser side so it wont be cleaned up.
+		animationStacksResolved.push_back(stack);
+	}
+
+	return animationStacksResolved;
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Document::GetObject(uint64_t id) const {
+	ObjectMap::const_iterator it = objects.find(id);
+	return it == objects.end() ? nullptr : (*it).second;
+}
+
+#define MAX_CLASSNAMES 6
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap &conns) const {
+	std::vector<const Connection *> temp;
+
+	const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range =
+			conns.equal_range(id);
+
+	temp.reserve(std::distance(range.first, range.second));
+	for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
+		temp.push_back((*it).second);
+	}
+
+	std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
+
+	return temp; // NRVO should handle this
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
+		const ConnectionMap &conns,
+		const char *const *classnames,
+		size_t count) const
+
+{
+	size_t lengths[MAX_CLASSNAMES];
+
+	const size_t c = count;
+	for (size_t i = 0; i < c; ++i) {
+		lengths[i] = strlen(classnames[i]);
+	}
+
+	std::vector<const Connection *> temp;
+	const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range =
+			conns.equal_range(id);
+
+	temp.reserve(std::distance(range.first, range.second));
+	for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
+		TokenPtr key = (is_src ? (*it).second->LazyDestinationObject() : (*it).second->LazySourceObject())->GetElement()->KeyToken();
+
+		const char *obtype = key->begin();
+
+		for (size_t i = 0; i < c; ++i) {
+			//ai_assert(classnames[i]);
+			if (static_cast<size_t>(std::distance(key->begin(), key->end())) == lengths[i] && !strncmp(classnames[i], obtype, lengths[i])) {
+				obtype = nullptr;
+				break;
+			}
+		}
+
+		if (obtype) {
+			continue;
+		}
+
+		temp.push_back((*it).second);
+	}
+
+	std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
+	return temp; // NRVO should handle this
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source) const {
+	return GetConnectionsSequenced(source, ConnectionsBySource());
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t src, const char *classname) const {
+	const char *arr[] = { classname };
+	return GetConnectionsBySourceSequenced(src, arr, 1);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source,
+		const char *const *classnames, size_t count) const {
+	return GetConnectionsSequenced(source, true, ConnectionsBySource(), classnames, count);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
+		const char *classname) const {
+	const char *arr[] = { classname };
+	return GetConnectionsByDestinationSequenced(dest, arr, 1);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const {
+	return GetConnectionsSequenced(dest, ConnectionsByDestination());
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
+		const char *const *classnames, size_t count) const {
+	return GetConnectionsSequenced(dest, false, ConnectionsByDestination(), classnames, count);
+}
+
+// ------------------------------------------------------------------------------------------------
+Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop,
+		const Document &doc) :
+		insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Connection::~Connection() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Connection::LazySourceObject() const {
+	LazyObject *const lazy = doc.GetObject(src);
+	return lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Connection::LazyDestinationObject() const {
+	LazyObject *const lazy = doc.GetObject(dest);
+	return lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+Object *Connection::SourceObject() const {
+	LazyObject *lazy = doc.GetObject(src);
+	//ai_assert(lazy);
+	return lazy->LoadObject();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object *Connection::DestinationObject() const {
+	LazyObject *lazy = doc.GetObject(dest);
+	//ai_assert(lazy);
+	return lazy->LoadObject();
+}
+
+} // namespace FBXDocParser

+ 1311 - 0
modules/fbx/fbx_parser/FBXDocument.h

@@ -0,0 +1,1311 @@
+/*************************************************************************/
+/*  FBXDocument.h                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/** @file  FBXDocument.h
+ *  @brief FBX DOM
+ */
+#ifndef FBX_DOCUMENT_H
+#define FBX_DOCUMENT_H
+
+#include "FBXCommon.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "core/math/transform.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+#include "core/print_string.h"
+#include <stdint.h>
+#include <numeric>
+
+#define _AI_CONCAT(a, b) a##b
+#define AI_CONCAT(a, b) _AI_CONCAT(a, b)
+
+namespace FBXDocParser {
+
+class Parser;
+class Object;
+struct ImportSettings;
+class Connection;
+
+class PropertyTable;
+class Document;
+class Material;
+class ShapeGeometry;
+class LineGeometry;
+class Geometry;
+
+class Video;
+
+class AnimationCurve;
+class AnimationCurveNode;
+class AnimationLayer;
+class AnimationStack;
+
+class BlendShapeChannel;
+class BlendShape;
+class Skin;
+class Cluster;
+
+typedef Object *ObjectPtr;
+#define new_Object new Object
+
+/** Represents a delay-parsed FBX objects. Many objects in the scene
+ *  are not needed by assimp, so it makes no sense to parse them
+ *  upfront. */
+class LazyObject {
+public:
+	LazyObject(uint64_t id, const ElementPtr element, const Document &doc);
+	~LazyObject();
+
+	ObjectPtr LoadObject();
+
+	/* Casting weak pointers to their templated type safely and preserving ref counting and safety
+	 * with lock() keyword to prevent leaking memory
+	 */
+	template <typename T>
+	const T *Get() {
+		ObjectPtr ob = LoadObject();
+		return dynamic_cast<const T *>(ob);
+	}
+
+	uint64_t ID() const {
+		return id;
+	}
+
+	bool IsBeingConstructed() const {
+		return (flags & BEING_CONSTRUCTED) != 0;
+	}
+
+	bool FailedToConstruct() const {
+		return (flags & FAILED_TO_CONSTRUCT) != 0;
+	}
+
+	const ElementPtr GetElement() const {
+		return element;
+	}
+
+	const Document &GetDocument() const {
+		return doc;
+	}
+
+private:
+	const Document &doc;
+	ElementPtr element = nullptr;
+	std::shared_ptr<Object> object = nullptr;
+	const uint64_t id = 0;
+
+	enum Flags {
+		BEING_CONSTRUCTED = 0x1,
+		FAILED_TO_CONSTRUCT = 0x2
+	};
+
+	unsigned int flags = 0;
+};
+
+/** Base class for in-memory (DOM) representations of FBX objects */
+class Object {
+public:
+	Object(uint64_t id, const ElementPtr element, const std::string &name);
+
+	virtual ~Object();
+
+	const ElementPtr SourceElement() const {
+		return element;
+	}
+
+	const std::string &Name() const {
+		return name;
+	}
+
+	uint64_t ID() const {
+		return id;
+	}
+
+protected:
+	const ElementPtr element;
+	const std::string name;
+	const uint64_t id = 0;
+};
+
+/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table,
+ *  fixed members are added by deriving classes. */
+class NodeAttribute : public Object {
+public:
+	NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~NodeAttribute();
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+private:
+	const PropertyTable *props;
+};
+
+/** DOM base class for FBX camera settings attached to a node */
+class CameraSwitcher : public NodeAttribute {
+public:
+	CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~CameraSwitcher();
+
+	int CameraID() const {
+		return cameraId;
+	}
+
+	const std::string &CameraName() const {
+		return cameraName;
+	}
+
+	const std::string &CameraIndexName() const {
+		return cameraIndexName;
+	}
+
+private:
+	int cameraId;
+	std::string cameraName;
+	std::string cameraIndexName;
+};
+
+#define fbx_stringize(a) #a
+
+#define fbx_simple_property(name, type, default_value)                           \
+	type name() const {                                                          \
+		return PropertyGet<type>(Props(), fbx_stringize(name), (default_value)); \
+	}
+
+// XXX improve logging
+#define fbx_simple_enum_property(name, type, default_value)                                               \
+	type name() const {                                                                                   \
+		const int ival = PropertyGet<int>(Props(), fbx_stringize(name), static_cast<int>(default_value)); \
+		if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) {                                                  \
+			return static_cast<type>(default_value);                                                      \
+		}                                                                                                 \
+		return static_cast<type>(ival);                                                                   \
+	}
+
+class FbxPoseNode;
+class FbxPose : public Object {
+public:
+	FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	const std::vector<FbxPoseNode *> &GetBindPoses() const {
+		return pose_nodes;
+	}
+
+	virtual ~FbxPose();
+
+private:
+	std::vector<FbxPoseNode *> pose_nodes;
+};
+
+class FbxPoseNode {
+public:
+	FbxPoseNode(const ElementPtr element, const Document &doc, const std::string &name) {
+		const ScopePtr sc = GetRequiredScope(element);
+
+		// get pose node transform
+		const ElementPtr Transform = GetRequiredElement(sc, "Matrix", element);
+		transform = ReadMatrix(Transform);
+
+		// get node id this pose node is for
+		const ElementPtr NodeId = sc->GetElement("Node");
+		if (NodeId) {
+			target_id = ParseTokenAsInt64(GetRequiredToken(NodeId, 0));
+		}
+
+		print_verbose("added posenode " + itos(target_id) + " transform: " + transform);
+	}
+	virtual ~FbxPoseNode() {
+	}
+
+	uint64_t GetNodeID() const {
+		return target_id;
+	}
+
+	Transform GetBindPose() const {
+		return transform;
+	}
+
+private:
+	uint64_t target_id;
+	Transform transform;
+};
+
+/** DOM base class for FBX cameras attached to a node */
+class Camera : public NodeAttribute {
+public:
+	Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~Camera();
+
+	fbx_simple_property(Position, Vector3, Vector3(0, 0, 0));
+	fbx_simple_property(UpVector, Vector3, Vector3(0, 1, 0));
+	fbx_simple_property(InterestPosition, Vector3, Vector3(0, 0, 0));
+
+	fbx_simple_property(AspectWidth, float, 1.0f);
+	fbx_simple_property(AspectHeight, float, 1.0f);
+	fbx_simple_property(FilmWidth, float, 1.0f);
+	fbx_simple_property(FilmHeight, float, 1.0f);
+
+	fbx_simple_property(NearPlane, float, 0.1f);
+	fbx_simple_property(FarPlane, float, 100.0f);
+
+	fbx_simple_property(FilmAspectRatio, float, 1.0f);
+	fbx_simple_property(ApertureMode, int, 0);
+
+	fbx_simple_property(FieldOfView, float, 1.0f);
+	fbx_simple_property(FocalLength, float, 1.0f);
+};
+
+/** DOM base class for FBX null markers attached to a node */
+class Null : public NodeAttribute {
+public:
+	Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+	virtual ~Null();
+};
+
+/** DOM base class for FBX limb node markers attached to a node */
+class LimbNode : public NodeAttribute {
+public:
+	LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+	virtual ~LimbNode();
+};
+
+/** DOM base class for FBX lights attached to a node */
+class Light : public NodeAttribute {
+public:
+	Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+	virtual ~Light();
+
+	enum Type {
+		Type_Point,
+		Type_Directional,
+		Type_Spot,
+		Type_Area,
+		Type_Volume,
+
+		Type_MAX // end-of-enum sentinel
+	};
+
+	enum Decay {
+		Decay_None,
+		Decay_Linear,
+		Decay_Quadratic,
+		Decay_Cubic,
+
+		Decay_MAX // end-of-enum sentinel
+	};
+
+	fbx_simple_property(Color, Vector3, Vector3(1, 1, 1));
+	fbx_simple_enum_property(LightType, Type, 0);
+	fbx_simple_property(CastLightOnObject, bool, false);
+	fbx_simple_property(DrawVolumetricLight, bool, true);
+	fbx_simple_property(DrawGroundProjection, bool, true);
+	fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false);
+	fbx_simple_property(Intensity, float, 100.0f);
+	fbx_simple_property(InnerAngle, float, 0.0f);
+	fbx_simple_property(OuterAngle, float, 45.0f);
+	fbx_simple_property(Fog, int, 50);
+	fbx_simple_enum_property(DecayType, Decay, 2);
+	fbx_simple_property(DecayStart, float, 1.0f);
+	fbx_simple_property(FileName, std::string, "");
+
+	fbx_simple_property(EnableNearAttenuation, bool, false);
+	fbx_simple_property(NearAttenuationStart, float, 0.0f);
+	fbx_simple_property(NearAttenuationEnd, float, 0.0f);
+	fbx_simple_property(EnableFarAttenuation, bool, false);
+	fbx_simple_property(FarAttenuationStart, float, 0.0f);
+	fbx_simple_property(FarAttenuationEnd, float, 0.0f);
+
+	fbx_simple_property(CastShadows, bool, true);
+	fbx_simple_property(ShadowColor, Vector3, Vector3(0, 0, 0));
+
+	fbx_simple_property(AreaLightShape, int, 0);
+
+	fbx_simple_property(LeftBarnDoor, float, 20.0f);
+	fbx_simple_property(RightBarnDoor, float, 20.0f);
+	fbx_simple_property(TopBarnDoor, float, 20.0f);
+	fbx_simple_property(BottomBarnDoor, float, 20.0f);
+	fbx_simple_property(EnableBarnDoor, bool, true);
+};
+
+class Model;
+
+typedef Model *ModelPtr;
+#define new_Model new Model
+
+/** DOM base class for FBX models (even though its semantics are more "node" than "model" */
+class Model : public Object {
+public:
+	enum RotOrder {
+		RotOrder_EulerXYZ = 0,
+		RotOrder_EulerXZY,
+		RotOrder_EulerYZX,
+		RotOrder_EulerYXZ,
+		RotOrder_EulerZXY,
+		RotOrder_EulerZYX,
+
+		RotOrder_SphericXYZ,
+
+		RotOrder_MAX // end-of-enum sentinel
+	};
+
+	Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~Model();
+
+	fbx_simple_property(QuaternionInterpolate, int, 0);
+
+	fbx_simple_property(RotationOffset, Vector3, Vector3());
+	fbx_simple_property(RotationPivot, Vector3, Vector3());
+	fbx_simple_property(ScalingOffset, Vector3, Vector3());
+	fbx_simple_property(ScalingPivot, Vector3, Vector3());
+	fbx_simple_property(TranslationActive, bool, false);
+	fbx_simple_property(TranslationMin, Vector3, Vector3());
+	fbx_simple_property(TranslationMax, Vector3, Vector3());
+
+	fbx_simple_property(TranslationMinX, bool, false);
+	fbx_simple_property(TranslationMaxX, bool, false);
+	fbx_simple_property(TranslationMinY, bool, false);
+	fbx_simple_property(TranslationMaxY, bool, false);
+	fbx_simple_property(TranslationMinZ, bool, false);
+	fbx_simple_property(TranslationMaxZ, bool, false);
+
+	fbx_simple_enum_property(RotationOrder, RotOrder, 0);
+	fbx_simple_property(RotationSpaceForLimitOnly, bool, false);
+	fbx_simple_property(RotationStiffnessX, float, 0.0f);
+	fbx_simple_property(RotationStiffnessY, float, 0.0f);
+	fbx_simple_property(RotationStiffnessZ, float, 0.0f);
+	fbx_simple_property(AxisLen, float, 0.0f);
+
+	fbx_simple_property(PreRotation, Vector3, Vector3());
+	fbx_simple_property(PostRotation, Vector3, Vector3());
+	fbx_simple_property(RotationActive, bool, false);
+
+	fbx_simple_property(RotationMin, Vector3, Vector3());
+	fbx_simple_property(RotationMax, Vector3, Vector3());
+
+	fbx_simple_property(RotationMinX, bool, false);
+	fbx_simple_property(RotationMaxX, bool, false);
+	fbx_simple_property(RotationMinY, bool, false);
+	fbx_simple_property(RotationMaxY, bool, false);
+	fbx_simple_property(RotationMinZ, bool, false);
+	fbx_simple_property(RotationMaxZ, bool, false);
+	fbx_simple_enum_property(InheritType, TransformInheritance, 0);
+
+	fbx_simple_property(ScalingActive, bool, false);
+	fbx_simple_property(ScalingMin, Vector3, Vector3());
+	fbx_simple_property(ScalingMax, Vector3, Vector3(1, 1, 1));
+	fbx_simple_property(ScalingMinX, bool, false);
+	fbx_simple_property(ScalingMaxX, bool, false);
+	fbx_simple_property(ScalingMinY, bool, false);
+	fbx_simple_property(ScalingMaxY, bool, false);
+	fbx_simple_property(ScalingMinZ, bool, false);
+	fbx_simple_property(ScalingMaxZ, bool, false);
+
+	fbx_simple_property(GeometricTranslation, Vector3, Vector3());
+	fbx_simple_property(GeometricRotation, Vector3, Vector3());
+	fbx_simple_property(GeometricScaling, Vector3, Vector3(1, 1, 1));
+
+	fbx_simple_property(MinDampRangeX, float, 0.0f);
+	fbx_simple_property(MinDampRangeY, float, 0.0f);
+	fbx_simple_property(MinDampRangeZ, float, 0.0f);
+	fbx_simple_property(MaxDampRangeX, float, 0.0f);
+	fbx_simple_property(MaxDampRangeY, float, 0.0f);
+	fbx_simple_property(MaxDampRangeZ, float, 0.0f);
+
+	fbx_simple_property(MinDampStrengthX, float, 0.0f);
+	fbx_simple_property(MinDampStrengthY, float, 0.0f);
+	fbx_simple_property(MinDampStrengthZ, float, 0.0f);
+	fbx_simple_property(MaxDampStrengthX, float, 0.0f);
+	fbx_simple_property(MaxDampStrengthY, float, 0.0f);
+	fbx_simple_property(MaxDampStrengthZ, float, 0.0f);
+
+	fbx_simple_property(PreferredAngleX, float, 0.0f);
+	fbx_simple_property(PreferredAngleY, float, 0.0f);
+	fbx_simple_property(PreferredAngleZ, float, 0.0f);
+
+	fbx_simple_property(Show, bool, true);
+	fbx_simple_property(LODBox, bool, false);
+	fbx_simple_property(Freeze, bool, false);
+
+	const std::string &Shading() const {
+		return shading;
+	}
+
+	const std::string &Culling() const {
+		return culling;
+	}
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+	/** Get material links */
+	const std::vector<const Material *> &GetMaterials() const {
+		return materials;
+	}
+
+	/** Get geometry links */
+	const std::vector<const Geometry *> &GetGeometry() const {
+		return geometry;
+	}
+
+	/** Get node attachments */
+	const std::vector<const NodeAttribute *> &GetAttributes() const {
+		return attributes;
+	}
+
+	/** convenience method to check if the node has a Null node marker */
+	bool IsNull() const;
+
+private:
+	void ResolveLinks(const ElementPtr element, const Document &doc);
+
+private:
+	std::vector<const Material *> materials;
+	std::vector<const Geometry *> geometry;
+	std::vector<const NodeAttribute *> attributes;
+
+	std::string shading;
+	std::string culling;
+	const PropertyTable *props = nullptr;
+};
+
+class ModelLimbNode : public Model {
+public:
+	ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~ModelLimbNode();
+};
+
+/** DOM class for generic FBX textures */
+class Texture : public Object {
+public:
+	Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~Texture();
+
+	const std::string &Type() const {
+		return type;
+	}
+
+	const std::string &FileName() const {
+		return fileName;
+	}
+
+	const std::string &RelativeFilename() const {
+		return relativeFileName;
+	}
+
+	const std::string &AlphaSource() const {
+		return alphaSource;
+	}
+
+	const Vector2 &UVTranslation() const {
+		return uvTrans;
+	}
+
+	const Vector2 &UVScaling() const {
+		return uvScaling;
+	}
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+	// return a 4-tuple
+	const unsigned int *Crop() const {
+		return crop;
+	}
+
+	const Video *Media() const {
+		return media;
+	}
+
+private:
+	Vector2 uvTrans;
+	Vector2 uvScaling;
+
+	std::string type;
+	std::string relativeFileName;
+	std::string fileName;
+	std::string alphaSource;
+	const PropertyTable *props = nullptr;
+
+	unsigned int crop[4] = { 0 };
+
+	const Video *media = nullptr;
+};
+
+/** DOM class for layered FBX textures */
+class LayeredTexture : public Object {
+public:
+	LayeredTexture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+	virtual ~LayeredTexture();
+
+	// Can only be called after construction of the layered texture object due to construction flag.
+	void fillTexture(const Document &doc);
+
+	enum BlendMode {
+		BlendMode_Translucent,
+		BlendMode_Additive,
+		BlendMode_Modulate,
+		BlendMode_Modulate2,
+		BlendMode_Over,
+		BlendMode_Normal,
+		BlendMode_Dissolve,
+		BlendMode_Darken,
+		BlendMode_ColorBurn,
+		BlendMode_LinearBurn,
+		BlendMode_DarkerColor,
+		BlendMode_Lighten,
+		BlendMode_Screen,
+		BlendMode_ColorDodge,
+		BlendMode_LinearDodge,
+		BlendMode_LighterColor,
+		BlendMode_SoftLight,
+		BlendMode_HardLight,
+		BlendMode_VividLight,
+		BlendMode_LinearLight,
+		BlendMode_PinLight,
+		BlendMode_HardMix,
+		BlendMode_Difference,
+		BlendMode_Exclusion,
+		BlendMode_Subtract,
+		BlendMode_Divide,
+		BlendMode_Hue,
+		BlendMode_Saturation,
+		BlendMode_Color,
+		BlendMode_Luminosity,
+		BlendMode_Overlay,
+		BlendMode_BlendModeCount
+	};
+
+	const Texture *getTexture(int index = 0) const {
+		return textures[index];
+	}
+	int textureCount() const {
+		return static_cast<int>(textures.size());
+	}
+	BlendMode GetBlendMode() const {
+		return blendMode;
+	}
+	float Alpha() {
+		return alpha;
+	}
+
+private:
+	std::vector<const Texture *> textures;
+	BlendMode blendMode;
+	float alpha;
+};
+
+typedef std::map<std::string, const Texture *> TextureMap;
+typedef std::map<std::string, const LayeredTexture *> LayeredTextureMap;
+
+/** DOM class for generic FBX videos */
+class Video : public Object {
+public:
+	Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~Video();
+
+	const std::string &Type() const {
+		return type;
+	}
+
+	const std::string &FileName() const {
+		return fileName;
+	}
+
+	const std::string &RelativeFilename() const {
+		return relativeFileName;
+	}
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+	const uint8_t *Content() const {
+		return content;
+	}
+
+	uint64_t ContentLength() const {
+		return contentLength;
+	}
+
+	uint8_t *RelinquishContent() {
+		uint8_t *ptr = content;
+		content = 0;
+		return ptr;
+	}
+
+	bool operator==(const Video &other) const {
+		return (
+				type == other.type && relativeFileName == other.relativeFileName && fileName == other.fileName);
+	}
+
+	bool operator<(const Video &other) const {
+		return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName);
+	}
+
+private:
+	std::string type;
+	std::string relativeFileName;
+	std::string fileName;
+	const PropertyTable *props = nullptr;
+
+	uint64_t contentLength;
+	uint8_t *content;
+};
+
+/** DOM class for generic FBX materials */
+class Material : public Object {
+public:
+	Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~Material();
+
+	const std::string &GetShadingModel() const {
+		return shading;
+	}
+
+	bool IsMultilayer() const {
+		return multilayer;
+	}
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+	const TextureMap &Textures() const {
+		return textures;
+	}
+
+	const LayeredTextureMap &LayeredTextures() const {
+		return layeredTextures;
+	}
+
+private:
+	std::string shading;
+	bool multilayer;
+	const PropertyTable *props;
+
+	TextureMap textures;
+	LayeredTextureMap layeredTextures;
+};
+
+// signed int keys (this can happen!)
+typedef std::vector<int64_t> KeyTimeList;
+typedef std::vector<float> KeyValueList;
+
+/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */
+class AnimationCurve : public Object {
+public:
+	AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+	virtual ~AnimationCurve();
+
+	/** get list of keyframe positions (time).
+     *  Invariant: |GetKeys()| > 0 */
+	const KeyTimeList &GetKeys() const {
+		return keys;
+	}
+
+	/** get list of keyframe values.
+      * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
+	const KeyValueList &GetValues() const {
+		return values;
+	}
+
+	const std::map<int64_t, float> &GetValueTimeTrack() const {
+		return keyvalues;
+	}
+
+	const std::vector<float> &GetAttributes() const {
+		return attributes;
+	}
+
+	const std::vector<unsigned int> &GetFlags() const {
+		return flags;
+	}
+
+private:
+	KeyTimeList keys;
+	KeyValueList values;
+	std::vector<float> attributes;
+	std::map<int64_t, float> keyvalues;
+	std::vector<unsigned int> flags;
+};
+
+/* Typedef for pointers for the animation handler */
+typedef std::shared_ptr<AnimationCurve> AnimationCurvePtr;
+typedef std::weak_ptr<AnimationCurve> AnimationCurveWeakPtr;
+typedef std::map<std::string, const AnimationCurve *> AnimationMap;
+
+/* Animation Curve node ptr */
+typedef std::shared_ptr<AnimationCurveNode> AnimationCurveNodePtr;
+typedef std::weak_ptr<AnimationCurveNode> AnimationCurveNodeWeakPtr;
+
+/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
+class AnimationCurveNode : public Object {
+public:
+	/* the optional white list specifies a list of property names for which the caller
+    wants animations for. If the curve node does not match one of these, std::range_error
+    will be thrown. */
+	AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc,
+			const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0);
+
+	virtual ~AnimationCurveNode();
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+	const AnimationMap &Curves() const;
+
+	/** Object the curve is assigned to, this can be NULL if the
+     *  target object has no DOM representation or could not
+     *  be read for other reasons.*/
+	Object *Target() const {
+		return target;
+	}
+
+	Model *TargetAsModel() const {
+		return dynamic_cast<Model *>(target);
+	}
+
+	NodeAttribute *TargetAsNodeAttribute() const {
+		return dynamic_cast<NodeAttribute *>(target);
+	}
+
+	/** Property of Target() that is being animated*/
+	const std::string &TargetProperty() const {
+		return prop;
+	}
+
+private:
+	Object *target = nullptr;
+	const PropertyTable *props;
+	mutable AnimationMap curves;
+	std::string prop;
+	const Document &doc;
+};
+
+typedef std::vector<const AnimationCurveNode *> AnimationCurveNodeList;
+
+typedef std::shared_ptr<AnimationLayer> AnimationLayerPtr;
+typedef std::weak_ptr<AnimationLayer> AnimationLayerWeakPtr;
+typedef std::vector<const AnimationLayer *> AnimationLayerList;
+
+/** Represents a FBX animation layer (i.e. a list of node animations) */
+class AnimationLayer : public Object {
+public:
+	AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+	virtual ~AnimationLayer();
+
+	const PropertyTable *Props() const {
+		//ai_assert(props.get());
+		return props;
+	}
+
+	/* the optional white list specifies a list of property names for which the caller
+    wants animations for. Curves not matching this list will not be added to the
+    animation layer. */
+	const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
+
+private:
+	const PropertyTable *props;
+	const Document &doc;
+};
+
+/** Represents a FBX animation stack (i.e. a list of animation layers) */
+class AnimationStack : public Object {
+public:
+	AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+	virtual ~AnimationStack();
+
+	fbx_simple_property(LocalStart, int64_t, 0L);
+	fbx_simple_property(LocalStop, int64_t, 0L);
+	fbx_simple_property(ReferenceStart, int64_t, 0L);
+	fbx_simple_property(ReferenceStop, int64_t, 0L);
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+	const AnimationLayerList &Layers() const {
+		return layers;
+	}
+
+private:
+	const PropertyTable *props = nullptr;
+	AnimationLayerList layers;
+};
+
+/** DOM class for deformers */
+class Deformer : public Object {
+public:
+	Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+	virtual ~Deformer();
+
+	const PropertyTable *Props() const {
+		//ai_assert(props.get());
+		return props;
+	}
+
+private:
+	const PropertyTable *props;
+};
+
+/** Constraints are from Maya they can help us with BoneAttachments :) **/
+class Constraint : public Object {
+public:
+	Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+	virtual ~Constraint();
+
+private:
+	const PropertyTable *props;
+};
+
+typedef std::vector<float> WeightArray;
+typedef std::vector<unsigned int> WeightIndexArray;
+
+/** DOM class for BlendShapeChannel deformers */
+class BlendShapeChannel : public Deformer {
+public:
+	BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~BlendShapeChannel();
+
+	float DeformPercent() const {
+		return percent;
+	}
+
+	const WeightArray &GetFullWeights() const {
+		return fullWeights;
+	}
+
+	const std::vector<const ShapeGeometry *> &GetShapeGeometries() const {
+		return shapeGeometries;
+	}
+
+private:
+	float percent;
+	WeightArray fullWeights;
+	std::vector<const ShapeGeometry *> shapeGeometries;
+};
+
+/** DOM class for BlendShape deformers */
+class BlendShape : public Deformer {
+public:
+	BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~BlendShape();
+
+	const std::vector<const BlendShapeChannel *> &BlendShapeChannels() const {
+		return blendShapeChannels;
+	}
+
+private:
+	std::vector<const BlendShapeChannel *> blendShapeChannels;
+};
+
+/** DOM class for skin deformer clusters (aka sub-deformers) */
+class Cluster : public Deformer {
+public:
+	Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~Cluster();
+
+	/** get the list of deformer weights associated with this cluster.
+     *  Use #GetIndices() to get the associated vertices. Both arrays
+     *  have the same size (and may also be empty). */
+	const std::vector<float> &GetWeights() const {
+		return weights;
+	}
+
+	/** get indices into the vertex data of the geometry associated
+     *  with this cluster. Use #GetWeights() to get the associated weights.
+     *  Both arrays have the same size (and may also be empty). */
+	const std::vector<unsigned int> &GetIndices() const {
+		return indices;
+	}
+
+	/** */
+	const Transform &GetTransform() const {
+		return transform;
+	}
+
+	const Transform &TransformLink() const {
+		return transformLink;
+	}
+
+	const Model *TargetNode() const {
+		return node;
+	}
+
+	const Transform &TransformAssociateModel() const {
+		return transformAssociateModel;
+	}
+
+	bool TransformAssociateModelValid() const {
+		return valid_transformAssociateModel;
+	}
+
+	// property is not in the fbx file
+	// if the cluster has an associate model
+	// we then have an additive type
+	enum SkinLinkMode {
+		SkinLinkMode_Normalized = 0,
+		SkinLinkMode_Additive = 1
+	};
+
+	SkinLinkMode GetLinkMode() {
+		return link_mode;
+	}
+
+private:
+	std::vector<float> weights;
+	std::vector<unsigned int> indices;
+
+	Transform transform;
+	Transform transformLink;
+	Transform transformAssociateModel;
+	SkinLinkMode link_mode;
+	bool valid_transformAssociateModel;
+	const Model *node = nullptr;
+};
+
+/** DOM class for skin deformers */
+class Skin : public Deformer {
+public:
+	Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+	virtual ~Skin();
+
+	float DeformAccuracy() const {
+		return accuracy;
+	}
+
+	const std::vector<const Cluster *> &Clusters() const {
+		return clusters;
+	}
+
+	enum SkinType {
+		Skin_Rigid = 0,
+		Skin_Linear,
+		Skin_DualQuaternion,
+		Skin_Blend
+	};
+
+	const SkinType &GetSkinType() const {
+		return skinType;
+	}
+
+private:
+	float accuracy;
+	SkinType skinType;
+	std::vector<const Cluster *> clusters;
+};
+
+/** Represents a link between two FBX objects. */
+class Connection {
+public:
+	Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop, const Document &doc);
+	~Connection();
+
+	// note: a connection ensures that the source and dest objects exist, but
+	// not that they have DOM representations, so the return value of one of
+	// these functions can still be NULL.
+	Object *SourceObject() const;
+	Object *DestinationObject() const;
+
+	// these, however, are always guaranteed to be valid
+	LazyObject *LazySourceObject() const;
+	LazyObject *LazyDestinationObject() const;
+
+	/** return the name of the property the connection is attached to.
+      * this is an empty string for object to object (OO) connections. */
+	const std::string &PropertyName() const {
+		return prop;
+	}
+
+	uint64_t InsertionOrder() const {
+		return insertionOrder;
+	}
+
+	int CompareTo(const Connection *c) const {
+		//ai_assert(nullptr != c);
+
+		// note: can't subtract because this would overflow uint64_t
+		if (InsertionOrder() > c->InsertionOrder()) {
+			return 1;
+		} else if (InsertionOrder() < c->InsertionOrder()) {
+			return -1;
+		}
+		return 0;
+	}
+
+	bool Compare(const Connection *c) const {
+		//ai_assert(nullptr != c);
+
+		return InsertionOrder() < c->InsertionOrder();
+	}
+
+public:
+	uint64_t insertionOrder;
+	const std::string prop;
+
+	uint64_t src, dest;
+	const Document &doc;
+};
+
+// XXX again, unique_ptr would be useful. shared_ptr is too
+// bloated since the objects have a well-defined single owner
+// during their entire lifetime (Document). FBX files have
+// up to many thousands of objects (most of which we never use),
+// so the memory overhead for them should be kept at a minimum.
+typedef std::map<uint64_t, LazyObject *> ObjectMap;
+typedef std::map<std::string, const PropertyTable *> PropertyTemplateMap;
+typedef std::multimap<uint64_t, const Connection *> ConnectionMap;
+
+/** DOM class for global document settings, a single instance per document can
+ *  be accessed via Document.Globals(). */
+class FileGlobalSettings {
+public:
+	FileGlobalSettings(const Document &doc, const PropertyTable *props);
+
+	~FileGlobalSettings();
+
+	const PropertyTable *Props() const {
+		return props;
+	}
+
+	const Document &GetDocument() const {
+		return doc;
+	}
+
+	fbx_simple_property(UpAxis, int, 1);
+	fbx_simple_property(UpAxisSign, int, 1);
+	fbx_simple_property(FrontAxis, int, 2);
+	fbx_simple_property(FrontAxisSign, int, 1);
+	fbx_simple_property(CoordAxis, int, 0);
+	fbx_simple_property(CoordAxisSign, int, 1);
+	fbx_simple_property(OriginalUpAxis, int, 0);
+	fbx_simple_property(OriginalUpAxisSign, int, 1);
+	fbx_simple_property(UnitScaleFactor, float, 1);
+	fbx_simple_property(OriginalUnitScaleFactor, float, 1);
+	fbx_simple_property(AmbientColor, Vector3, Vector3(0, 0, 0));
+	fbx_simple_property(DefaultCamera, std::string, "");
+
+	enum FrameRate {
+		FrameRate_DEFAULT = 0,
+		FrameRate_120 = 1,
+		FrameRate_100 = 2,
+		FrameRate_60 = 3,
+		FrameRate_50 = 4,
+		FrameRate_48 = 5,
+		FrameRate_30 = 6,
+		FrameRate_30_DROP = 7,
+		FrameRate_NTSC_DROP_FRAME = 8,
+		FrameRate_NTSC_FULL_FRAME = 9,
+		FrameRate_PAL = 10,
+		FrameRate_CINEMA = 11,
+		FrameRate_1000 = 12,
+		FrameRate_CINEMA_ND = 13,
+		FrameRate_CUSTOM = 14,
+
+		FrameRate_MAX // end-of-enum sentinel
+	};
+
+	fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT);
+	fbx_simple_property(TimeSpanStart, uint64_t, 0L);
+	fbx_simple_property(TimeSpanStop, uint64_t, 0L);
+	fbx_simple_property(CustomFrameRate, float, -1.0f);
+
+private:
+	const PropertyTable *props = nullptr;
+	const Document &doc;
+};
+
+/** DOM root for a FBX file */
+class Document {
+public:
+	Document(const Parser &parser, const ImportSettings &settings);
+
+	~Document();
+
+	LazyObject *GetObject(uint64_t id) const;
+
+	bool IsSafeToImport() const {
+		return SafeToImport;
+	}
+
+	bool IsBinary() const {
+		return parser.IsBinary();
+	}
+
+	unsigned int FBXVersion() const {
+		return fbxVersion;
+	}
+
+	const std::string &Creator() const {
+		return creator;
+	}
+
+	// elements (in this order): Year, Month, Day, Hour, Second, Millisecond
+	const unsigned int *CreationTimeStamp() const {
+		return creationTimeStamp;
+	}
+
+	const FileGlobalSettings *GlobalSettingsPtr() const {
+		return globals.get();
+	}
+
+	const PropertyTemplateMap &Templates() const {
+		return templates;
+	}
+
+	const ObjectMap &Objects() const {
+		return objects;
+	}
+
+	const ImportSettings &Settings() const {
+		return settings;
+	}
+
+	const ConnectionMap &ConnectionsBySource() const {
+		return src_connections;
+	}
+
+	const ConnectionMap &ConnectionsByDestination() const {
+		return dest_connections;
+	}
+
+	// note: the implicit rule in all DOM classes is to always resolve
+	// from destination to source (since the FBX object hierarchy is,
+	// with very few exceptions, a DAG, this avoids cycles). In all
+	// cases that may involve back-facing edges in the object graph,
+	// use LazyObject::IsBeingConstructed() to check.
+
+	std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source) const;
+	std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest) const;
+
+	std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source, const char *classname) const;
+	std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest, const char *classname) const;
+
+	std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source,
+			const char *const *classnames, size_t count) const;
+	std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest,
+			const char *const *classnames,
+			size_t count) const;
+
+	const std::vector<const AnimationStack *> &AnimationStacks() const;
+	const std::vector<uint64_t> &GetAnimationStackIDs() const {
+		return animationStacks;
+	}
+
+	const std::vector<uint64_t> &GetConstraintStackIDs() const {
+		return constraints;
+	}
+
+	const std::vector<uint64_t> &GetBindPoseIDs() const {
+		return bind_poses;
+	};
+
+	const std::vector<uint64_t> &GetMaterialIDs() const {
+		return materials;
+	};
+
+	const std::vector<uint64_t> &GetSkinIDs() const {
+		return skins;
+	}
+
+private:
+	std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, const ConnectionMap &) const;
+	std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, bool is_src,
+			const ConnectionMap &,
+			const char *const *classnames,
+			size_t count) const;
+	bool ReadHeader();
+	void ReadObjects();
+	void ReadPropertyTemplates();
+	void ReadConnections();
+	void ReadGlobalSettings();
+
+private:
+	const ImportSettings &settings;
+
+	ObjectMap objects;
+	const Parser &parser;
+	bool SafeToImport = false;
+
+	PropertyTemplateMap templates;
+	ConnectionMap src_connections;
+	ConnectionMap dest_connections;
+
+	unsigned int fbxVersion = 0;
+	std::string creator;
+	unsigned int creationTimeStamp[7] = { 0 };
+
+	std::vector<uint64_t> animationStacks;
+	std::vector<uint64_t> bind_poses;
+	// constraints aren't in the tree / at least they are not easy to access.
+	std::vector<uint64_t> constraints;
+	std::vector<uint64_t> materials;
+	std::vector<uint64_t> skins;
+	mutable std::vector<const AnimationStack *> animationStacksResolved;
+	std::shared_ptr<FileGlobalSettings> globals = nullptr;
+};
+
+} // namespace FBXDocParser
+
+namespace std {
+template <>
+struct hash<const FBXDocParser::Video> {
+	std::size_t operator()(const FBXDocParser::Video &video) const {
+		using std::hash;
+		using std::size_t;
+		using std::string;
+
+		size_t res = 17;
+		res = res * 31 + hash<string>()(video.Name());
+		res = res * 31 + hash<string>()(video.RelativeFilename());
+		res = res * 31 + hash<string>()(video.Type());
+
+		return res;
+	}
+};
+} // namespace std
+
+#endif // FBX_DOCUMENT_H

+ 172 - 0
modules/fbx/fbx_parser/FBXDocumentUtil.cpp

@@ -0,0 +1,172 @@
+/*************************************************************************/
+/*  FBXDocumentUtil.cpp                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXDocumentUtil.cpp
+ *  @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h
+ */
+
+#include "FBXDocumentUtil.h"
+#include "FBXDocument.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "FBXUtil.h"
+#include "core/print_string.h"
+
+namespace FBXDocParser {
+namespace Util {
+
+void DOMError(const std::string &message) {
+	print_error("[FBX-DOM]" + String(message.c_str()));
+}
+
+void DOMError(const std::string &message, const Token *token) {
+	print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMError(const std::string &message, const std::shared_ptr<Token> token) {
+	print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMError(const std::string &message, const Element *element /*= NULL*/) {
+	if (element) {
+		DOMError(message, element->KeyToken());
+	}
+	print_error("[FBX-DOM] " + String(message.c_str()));
+}
+
+void DOMError(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) {
+	if (element) {
+		DOMError(message, element->KeyToken());
+	}
+	print_error("[FBX-DOM] " + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message) {
+	print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message, const Token *token) {
+	print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMWarning(const std::string &message, const Element *element /*= NULL*/) {
+	if (element) {
+		DOMWarning(message, element->KeyToken());
+		return;
+	}
+	print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message, const std::shared_ptr<Token> token) {
+	print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMWarning(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) {
+	if (element) {
+		DOMWarning(message, element->KeyToken());
+		return;
+	}
+	print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+// ------------------------------------------------------------------------------------------------
+// fetch a property table and the corresponding property template
+const PropertyTable *GetPropertyTable(const Document &doc,
+		const std::string &templateName,
+		const ElementPtr element,
+		const ScopePtr sc,
+		bool no_warn /*= false*/) {
+	// todo: make this an abstraction
+	const ElementPtr Properties70 = sc->GetElement("Properties70");
+	const PropertyTable *templateProps = static_cast<const PropertyTable *>(nullptr);
+
+	if (templateName.length()) {
+		PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
+		if (it != doc.Templates().end()) {
+			templateProps = (*it).second;
+		}
+	}
+
+	if (!Properties70 || !Properties70->Compound()) {
+		if (!no_warn) {
+			DOMWarning("property table (Properties70) not found", element);
+		}
+		if (templateProps) {
+			return templateProps;
+		} else {
+			return new const PropertyTable();
+		}
+	}
+
+	return new PropertyTable(Properties70, templateProps);
+}
+} // namespace Util
+} // namespace FBXDocParser

+ 142 - 0
modules/fbx/fbx_parser/FBXDocumentUtil.h

@@ -0,0 +1,142 @@
+/*************************************************************************/
+/*  FBXDocumentUtil.h                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, 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  FBXDocumentUtil.h
+ *  @brief FBX internal utilities used by the DOM reading code
+ */
+#ifndef FBX_DOCUMENT_UTIL_H
+#define FBX_DOCUMENT_UTIL_H
+
+#include "FBXDocument.h"
+#include <memory>
+#include <string>
+
+struct Token;
+struct Element;
+
+namespace FBXDocParser {
+namespace Util {
+
+// Parser errors
+void DOMError(const std::string &message);
+void DOMError(const std::string &message, const Token *token);
+void DOMError(const std::string &message, const Element *element);
+void DOMError(const std::string &message, const std::shared_ptr<Element> element);
+void DOMError(const std::string &message, const std::shared_ptr<Token> token);
+
+// Parser warnings
+void DOMWarning(const std::string &message);
+void DOMWarning(const std::string &message, const Token *token);
+void DOMWarning(const std::string &message, const Element *element);
+void DOMWarning(const std::string &message, const std::shared_ptr<Token> token);
+void DOMWarning(const std::string &message, const std::shared_ptr<Element> element);
+
+// fetch a property table and the corresponding property template
+const PropertyTable *GetPropertyTable(const Document &doc,
+		const std::string &templateName,
+		const ElementPtr element,
+		const ScopePtr sc,
+		bool no_warn = false);
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+const T *ProcessSimpleConnection(const Connection &con,
+		bool is_object_property_conn,
+		const char *name,
+		const ElementPtr element,
+		const char **propNameOut = nullptr) {
+	if (is_object_property_conn && !con.PropertyName().length()) {
+		DOMWarning("expected incoming " + std::string(name) +
+						   " link to be an object-object connection, ignoring",
+				element);
+		return nullptr;
+	} else if (!is_object_property_conn && con.PropertyName().length()) {
+		DOMWarning("expected incoming " + std::string(name) +
+						   " link to be an object-property connection, ignoring",
+				element);
+		return nullptr;
+	}
+
+	if (is_object_property_conn && propNameOut) {
+		// note: this is ok, the return value of PropertyValue() is guaranteed to
+		// remain valid and unchanged as long as the document exists.
+		*propNameOut = con.PropertyName().c_str();
+	}
+
+	// Cast Object to AnimationPlayer for example using safe functions, which return nullptr etc
+	Object *ob = con.SourceObject();
+	ERR_FAIL_COND_V_MSG(!ob, nullptr, "Failed to load object from SourceObject ptr");
+	return dynamic_cast<const T *>(ob);
+}
+
+} // namespace Util
+} // namespace FBXDocParser
+
+#endif // FBX_DOCUMENT_UTIL_H

+ 174 - 0
modules/fbx/fbx_parser/FBXImportSettings.h

@@ -0,0 +1,174 @@
+/*************************************************************************/
+/*  FBXImportSettings.h                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXImportSettings.h
+ *  @brief FBX importer runtime configuration
+ */
+#ifndef FBX_IMPORT_SETTINGS_H
+#define FBX_IMPORT_SETTINGS_H
+
+namespace FBXDocParser {
+
+/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */
+struct ImportSettings {
+	ImportSettings() :
+			strictMode(true), readAllLayers(true), readAllMaterials(true), readMaterials(true), readTextures(true), readCameras(true), readLights(true), readAnimations(true), readWeights(true), preservePivots(true), optimizeEmptyAnimationCurves(true), useLegacyEmbeddedTextureNaming(false), removeEmptyBones(true), convertToMeters(false) {
+		// empty
+	}
+
+	/** enable strict mode:
+     *   - only accept fbx 2012, 2013 files
+     *   - on the slightest error, give up.
+     *
+     *  Basically, strict mode means that the fbx file will actually
+     *  be validated. Strict mode is off by default. */
+	bool strictMode;
+
+	/** specifies whether all geometry layers are read and scanned for
+      * usable data channels. The FBX spec indicates that many readers
+      * will only read the first channel and that this is in some way
+      * the recommended way- in reality, however, it happens a lot that
+      * vertex data is spread among multiple layers. The default
+      * value for this option is true.*/
+	bool readAllLayers;
+
+	/** specifies whether all materials are read, or only those that
+     *  are referenced by at least one mesh. Reading all materials
+     *  may make FBX reading a lot slower since all objects
+     *  need to be processed .
+     *  This bit is ignored unless readMaterials=true*/
+	bool readAllMaterials;
+
+	/** import materials (true) or skip them and assign a default
+     *  material. The default value is true.*/
+	bool readMaterials;
+
+	/** import embedded textures? Default value is true.*/
+	bool readTextures;
+
+	/** import cameras? Default value is true.*/
+	bool readCameras;
+
+	/** import light sources? Default value is true.*/
+	bool readLights;
+
+	/** import animations (i.e. animation curves, the node
+     *  skeleton is always imported). Default value is true. */
+	bool readAnimations;
+
+	/** read bones (vertex weights and deform info).
+     *  Default value is true. */
+	bool readWeights;
+
+	/** preserve transformation pivots and offsets. Since these can
+     *  not directly be represented in assimp, additional dummy
+     *  nodes will be generated. Note that settings this to false
+     *  can make animation import a lot slower. The default value
+     *  is true.
+     *
+     *  The naming scheme for the generated nodes is:
+     *    <OriginalName>_$AssimpFbx$_<TransformName>
+     *
+     *  where <TransformName> is one of
+     *    RotationPivot
+     *    RotationOffset
+     *    PreRotation
+     *    PostRotation
+     *    ScalingPivot
+     *    ScalingOffset
+     *    Translation
+     *    Scaling
+     *    Rotation
+     **/
+	bool preservePivots;
+
+	/** do not import animation curves that specify a constant
+     *  values matching the corresponding node transformation.
+     *  The default value is true. */
+	bool optimizeEmptyAnimationCurves;
+
+	/** use legacy naming for embedded textures eg: (*0, *1, *2)
+    */
+	bool useLegacyEmbeddedTextureNaming;
+
+	/** Empty bones shall be removed
+    */
+	bool removeEmptyBones;
+
+	/** Set to true to perform a conversion from cm to meter after the import
+    */
+	bool convertToMeters;
+};
+
+} // namespace FBXDocParser
+
+#endif // FBX_IMPORT_SETTINGS_H

+ 406 - 0
modules/fbx/fbx_parser/FBXMaterial.cpp

@@ -0,0 +1,406 @@
+/*************************************************************************/
+/*  FBXMaterial.cpp                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXMaterial.cpp
+ *  @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation
+ */
+
+#include "ByteSwapper.h"
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+
+#include "FBXUtil.h"
+#include <algorithm> // std::transform
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Material::Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name) {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	const ElementPtr ShadingModel = sc->GetElement("ShadingModel");
+	const ElementPtr MultiLayer = sc->GetElement("MultiLayer");
+
+	if (MultiLayer) {
+		multilayer = !!ParseTokenAsInt(GetRequiredToken(MultiLayer, 0));
+	}
+
+	if (ShadingModel) {
+		shading = ParseTokenAsString(GetRequiredToken(ShadingModel, 0));
+	} else {
+		DOMWarning("shading mode not specified, assuming phong", element);
+		shading = "phong";
+	}
+
+	std::string templateName;
+
+	if (shading == "phong") {
+		templateName = "Material.FbxSurfacePhong";
+	} else if (shading == "lambert") {
+		templateName = "Material.FbxSurfaceLambert";
+	} else {
+		DOMWarning("shading mode not recognized: " + shading, element);
+	}
+
+	props = GetPropertyTable(doc, templateName, element, sc);
+
+	// resolve texture links
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+	for (const Connection *con : conns) {
+
+		// texture link to properties, not objects
+		if (!con->PropertyName().length()) {
+			continue;
+		}
+
+		Object *ob = con->SourceObject();
+		if (!ob) {
+			DOMWarning("failed to read source object for texture link, ignoring", element);
+			continue;
+		}
+
+		const Texture *tex = dynamic_cast<const Texture *>(ob);
+		if (!tex) {
+			LayeredTexture *layeredTexture = dynamic_cast<LayeredTexture *>(ob);
+
+			if (!layeredTexture) {
+				DOMWarning("source object for texture link is not a texture or layered texture, ignoring", element);
+				continue;
+			}
+
+			const std::string &prop = con->PropertyName();
+			if (layeredTextures.find(prop) != layeredTextures.end()) {
+				DOMWarning("duplicate layered texture link: " + prop, element);
+			}
+
+			layeredTextures[prop] = layeredTexture;
+			layeredTexture->fillTexture(doc);
+		} else {
+			const std::string &prop = con->PropertyName();
+			if (textures.find(prop) != textures.end()) {
+				DOMWarning("duplicate texture link: " + prop, element);
+			}
+
+			textures[prop] = tex;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Material::~Material() {
+	if (props != nullptr) {
+		delete props;
+		props = nullptr;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name), uvScaling(1.0f, 1.0f), media(nullptr) {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	const ElementPtr Type = sc->GetElement("Type");
+	const ElementPtr FileName = sc->GetElement("FileName");
+	const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
+	const ElementPtr ModelUVTranslation = sc->GetElement("ModelUVTranslation");
+	const ElementPtr ModelUVScaling = sc->GetElement("ModelUVScaling");
+	const ElementPtr Texture_Alpha_Source = sc->GetElement("Texture_Alpha_Source");
+	const ElementPtr Cropping = sc->GetElement("Cropping");
+
+	if (Type) {
+		type = ParseTokenAsString(GetRequiredToken(Type, 0));
+	}
+
+	if (FileName) {
+		fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
+	}
+
+	if (RelativeFilename) {
+		relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
+	}
+
+	if (ModelUVTranslation) {
+		uvTrans = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 0)),
+				ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 1)));
+	}
+
+	if (ModelUVScaling) {
+		uvScaling = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 0)),
+				ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 1)));
+	}
+
+	if (Cropping) {
+		crop[0] = ParseTokenAsInt(GetRequiredToken(Cropping, 0));
+		crop[1] = ParseTokenAsInt(GetRequiredToken(Cropping, 1));
+		crop[2] = ParseTokenAsInt(GetRequiredToken(Cropping, 2));
+		crop[3] = ParseTokenAsInt(GetRequiredToken(Cropping, 3));
+	} else {
+		// vc8 doesn't support the crop() syntax in initialization lists
+		// (and vc9 WARNS about the new (i.e. compliant) behaviour).
+		crop[0] = crop[1] = crop[2] = crop[3] = 0;
+	}
+
+	if (Texture_Alpha_Source) {
+		alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0));
+	}
+
+	props = GetPropertyTable(doc, "Texture.FbxFileTexture", element, sc);
+
+	// 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available.
+	bool ok;
+	const Vector3 &scaling = PropertyGet<Vector3>(props, "Scaling", ok);
+	if (ok) {
+		uvScaling.x = scaling.x;
+		uvScaling.y = scaling.y;
+	}
+
+	const Vector3 &trans = PropertyGet<Vector3>(props, "Translation", ok);
+	if (ok) {
+		uvTrans.x = trans.x;
+		uvTrans.y = trans.y;
+	}
+
+	// resolve video links
+	if (doc.Settings().readTextures) {
+		const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+		for (const Connection *con : conns) {
+			const Object *const ob = con->SourceObject();
+			if (!ob) {
+				DOMWarning("failed to read source object for texture link, ignoring", element);
+				continue;
+			}
+
+			const Video *const video = dynamic_cast<const Video *>(ob);
+			if (video) {
+				media = video;
+			}
+		}
+	}
+}
+
+Texture::~Texture() {
+	if (props != nullptr) {
+		delete props;
+		props = nullptr;
+	}
+}
+
+LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) :
+		Object(id, element, name), blendMode(BlendMode_Modulate), alpha(1) {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	ElementPtr BlendModes = sc->GetElement("BlendModes");
+	ElementPtr Alphas = sc->GetElement("Alphas");
+
+	if (BlendModes != 0) {
+		blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(BlendModes, 0));
+	}
+	if (Alphas != 0) {
+		alpha = ParseTokenAsFloat(GetRequiredToken(Alphas, 0));
+	}
+}
+
+LayeredTexture::~LayeredTexture() {
+}
+
+void LayeredTexture::fillTexture(const Document &doc) {
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+	for (size_t i = 0; i < conns.size(); ++i) {
+		const Connection *con = conns.at(i);
+
+		const Object *const ob = con->SourceObject();
+		if (!ob) {
+			DOMWarning("failed to read source object for texture link, ignoring", element);
+			continue;
+		}
+
+		const Texture *const tex = dynamic_cast<const Texture *>(ob);
+
+		textures.push_back(tex);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name), contentLength(0), content(0) {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	const ElementPtr Type = sc->GetElement("Type");
+	// File Version 7500 Crashes if this is not checked fully.
+	// As of writing this comment 7700 exists, in August 2020
+	ElementPtr FileName = nullptr;
+	if (HasElement(sc, "Filename")) {
+		FileName = (ElementPtr)sc->GetElement("Filename");
+	} else if (HasElement(sc, "FileName")) {
+		FileName = (ElementPtr)sc->GetElement("FileName");
+	} else {
+		print_error("file has invalid video material returning...");
+		return;
+	}
+	const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
+	const ElementPtr Content = sc->GetElement("Content");
+
+	if (Type) {
+		type = ParseTokenAsString(GetRequiredToken(Type, 0));
+	}
+
+	if (FileName) {
+		fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
+	}
+
+	if (RelativeFilename) {
+		relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
+	}
+
+	if (Content && !Content->Tokens().empty()) {
+		//this field is omitted when the embedded texture is already loaded, let's ignore if it's not found
+		try {
+			const Token *token = GetRequiredToken(Content, 0);
+			const char *data = token->begin();
+			if (!token->IsBinary()) {
+				if (*data != '"') {
+					DOMError("embedded content is not surrounded by quotation marks", element);
+				} else {
+					size_t targetLength = 0;
+					auto numTokens = Content->Tokens().size();
+					// First time compute size (it could be large like 64Gb and it is good to allocate it once)
+					for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
+						const Token *dataToken = GetRequiredToken(Content, tokenIdx);
+						size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
+						const char *base64data = dataToken->begin() + 1;
+						const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength);
+						if (outLength == 0) {
+							DOMError("Corrupted embedded content found", element);
+						}
+						targetLength += outLength;
+					}
+					if (targetLength == 0) {
+						DOMError("Corrupted embedded content found", element);
+					}
+					content = new uint8_t[targetLength];
+					contentLength = static_cast<uint64_t>(targetLength);
+					size_t dst_offset = 0;
+					for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
+						const Token *dataToken = GetRequiredToken(Content, tokenIdx);
+						ERR_FAIL_COND(!dataToken);
+						size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
+						const char *base64data = dataToken->begin() + 1;
+						dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset);
+					}
+					if (targetLength != dst_offset) {
+						delete[] content;
+						contentLength = 0;
+						DOMError("Corrupted embedded content found", element);
+					}
+				}
+			} else if (static_cast<size_t>(token->end() - data) < 5) {
+				DOMError("binary data array is too short, need five (5) bytes for type signature and element count", element);
+			} else if (*data != 'R') {
+				DOMWarning("video content is not raw binary data, ignoring", element);
+			} else {
+				// read number of elements
+				uint32_t len = 0;
+				::memcpy(&len, data + 1, sizeof(len));
+				AI_SWAP4(len);
+
+				contentLength = len;
+
+				content = new uint8_t[len];
+				::memcpy(content, data + 5, len);
+			}
+		} catch (...) {
+			//			//we don't need the content data for contents that has already been loaded
+			//			ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ",
+			//									   runtimeError.what());
+		}
+	}
+
+	props = GetPropertyTable(doc, "Video.FbxVideo", element, sc);
+}
+
+Video::~Video() {
+	if (content) {
+		delete[] content;
+	}
+
+	if (props != nullptr) {
+		delete props;
+		props = nullptr;
+	}
+}
+
+} // namespace FBXDocParser

+ 451 - 0
modules/fbx/fbx_parser/FBXMeshGeometry.cpp

@@ -0,0 +1,451 @@
+/*************************************************************************/
+/*  FBXMeshGeometry.cpp                                                  */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXMeshGeometry.cpp
+ *  @brief Assimp::FBX::MeshGeometry implementation
+ */
+
+#include <functional>
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXMeshGeometry.h"
+#include "core/math/vector3.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Geometry::Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+		Object(id, element, name), skin() {
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+	for (const Connection *con : conns) {
+		const Skin *sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
+		if (sk) {
+			skin = sk;
+		}
+		const BlendShape *bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry",
+				element);
+		if (bsp) {
+			blendShapes.push_back(bsp);
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Geometry::~Geometry() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<const BlendShape *> &Geometry::get_blend_shapes() const {
+	return blendShapes;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Skin *Geometry::DeformerSkin() const {
+	return skin;
+}
+
+// ------------------------------------------------------------------------------------------------
+MeshGeometry::MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+		Geometry(id, element, name, doc) {
+	print_verbose("mesh name: " + String(name.c_str()));
+
+	ScopePtr sc = element->Compound();
+	ERR_FAIL_COND_MSG(sc == nullptr, "failed to read geometry, prevented crash");
+	ERR_FAIL_COND_MSG(!HasElement(sc, "Vertices"), "Detected mesh with no vertexes, didn't populate the mesh");
+
+	// must have Mesh elements:
+	const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
+	const ElementPtr PolygonVertexIndex = GetRequiredElement(sc, "PolygonVertexIndex", element);
+
+	if (HasElement(sc, "Edges")) {
+		const ElementPtr element_edges = GetRequiredElement(sc, "Edges", element);
+		ParseVectorDataArray(m_edges, element_edges);
+	}
+
+	// read mesh data into arrays
+	ParseVectorDataArray(m_vertices, Vertices);
+	ParseVectorDataArray(m_face_indices, PolygonVertexIndex);
+
+	ERR_FAIL_COND_MSG(m_vertices.empty(), "mesh with no vertexes in FBX file, did you mean to delete it?");
+	ERR_FAIL_COND_MSG(m_face_indices.empty(), "mesh has no faces, was this intended?");
+
+	// Retrieve layer elements, for all of the mesh
+	const ElementCollection &Layer = sc->GetCollection("Layer");
+
+	// Store all layers
+	std::vector<std::tuple<int, std::string> > valid_layers;
+
+	// now read the sub mesh information from the geometry (normals, uvs, etc)
+	for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
+		const ScopePtr layer = GetRequiredScope(it->second);
+		const ElementCollection &LayerElement = layer->GetCollection("LayerElement");
+		for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
+			std::string layer_name = eit->first;
+			ElementPtr element_layer = eit->second;
+			const ScopePtr layer_element = GetRequiredScope(element_layer);
+
+			// Actual usable 'type' LayerElementUV, LayerElementNormal, etc
+			const ElementPtr Type = GetRequiredElement(layer_element, "Type");
+			const ElementPtr TypedIndex = GetRequiredElement(layer_element, "TypedIndex");
+			const std::string &type = ParseTokenAsString(GetRequiredToken(Type, 0));
+			const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex, 0));
+
+			// we only need the layer name and the typed index.
+			valid_layers.push_back(std::tuple<int, std::string>(typedIndex, type));
+		}
+	}
+
+	// get object / mesh directly from the FBX by the element ID.
+	const ScopePtr top = GetRequiredScope(element);
+
+	// iterate over all layers for the mesh (uvs, normals, smoothing groups, colors, etc)
+	for (size_t x = 0; x < valid_layers.size(); x++) {
+		const int layer_id = std::get<0>(valid_layers[x]);
+		const std::string &layer_type_name = std::get<1>(valid_layers[x]);
+
+		// Get collection of elements from the XLayerMap (example: LayerElementUV)
+		// this must contain our proper elements.
+
+		// This is stupid, because it means we select them ALL not just the one we want.
+		// but it's fine we can match by id.
+		GetRequiredElement(top, layer_type_name);
+		const ElementCollection &candidates = top->GetCollection(layer_type_name);
+
+		ElementMap::const_iterator iter;
+		for (iter = candidates.first; iter != candidates.second; ++iter) {
+			const ScopePtr layer_scope = GetRequiredScope(iter->second);
+			TokenPtr layer_token = GetRequiredToken(iter->second, 0);
+			const int index = ParseTokenAsInt(layer_token);
+
+			ERR_FAIL_COND_MSG(layer_scope == nullptr, "prevented crash, layer scope is invalid");
+
+			if (index == layer_id) {
+				const std::string &MappingInformationType = ParseTokenAsString(GetRequiredToken(
+						GetRequiredElement(layer_scope, "MappingInformationType"), 0));
+
+				const std::string &ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
+						GetRequiredElement(layer_scope, "ReferenceInformationType"), 0));
+
+				if (layer_type_name == "LayerElementUV") {
+					if (index == 0) {
+						m_uv_0 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
+					} else if (index == 1) {
+						m_uv_1 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
+					}
+				} else if (layer_type_name == "LayerElementMaterial") {
+					m_material_allocation_ids = resolve_vertex_data_array<int>(layer_scope, MappingInformationType, ReferenceInformationType, "Materials");
+				} else if (layer_type_name == "LayerElementNormal") {
+					m_normals = resolve_vertex_data_array<Vector3>(layer_scope, MappingInformationType, ReferenceInformationType, "Normals");
+				} else if (layer_type_name == "LayerElementColor") {
+					m_colors = resolve_vertex_data_array<Color>(layer_scope, MappingInformationType, ReferenceInformationType, "Colors");
+				}
+			}
+		}
+	}
+
+	print_verbose("Mesh statistics \nuv_0: " + m_uv_0.debug_info() + "\nuv_1: " + m_uv_1.debug_info() + "\nvertices: " + itos(m_vertices.size()));
+
+	// Compose the edge of the mesh.
+	// You can see how the edges are stored into the FBX here: https://gist.github.com/AndreaCatania/da81840f5aa3b2feedf189e26c5a87e6
+	for (size_t i = 0; i < m_edges.size(); i += 1) {
+		ERR_FAIL_INDEX_MSG((size_t)m_edges[i], m_face_indices.size(), "The edge is pointing to a weird location in the face indices. The FBX is corrupted.");
+		int polygon_vertex_0 = m_face_indices[m_edges[i]];
+		int polygon_vertex_1;
+		if (polygon_vertex_0 < 0) {
+			// The polygon_vertex_0 points to the end of a polygon, so it's
+			// connected with the beginning of polygon in the edge list.
+
+			// Fist invert the vertex.
+			polygon_vertex_0 = ~polygon_vertex_0;
+
+			// Search the start vertex of the polygon.
+			// Iterate from the polygon_vertex_index backward till the start of
+			// the polygon is found.
+			ERR_FAIL_COND_MSG(m_edges[i] - 1 < 0, "The polygon is not yet started and we already need the final vertex. This FBX is corrupted.");
+			bool found_it = false;
+			for (int x = m_edges[i] - 1; x >= 0; x -= 1) {
+				if (x == 0) {
+					// This for sure is the start.
+					polygon_vertex_1 = m_face_indices[x];
+					found_it = true;
+					break;
+				} else if (m_face_indices[x] < 0) {
+					// This is the end of the previous polygon, so the next is
+					// the start of the polygon we need.
+					polygon_vertex_1 = m_face_indices[x + 1];
+					found_it = true;
+					break;
+				}
+			}
+			// As the algorithm above, this check is useless. Because the first
+			// ever vertex is always considered the begining of a polygon.
+			ERR_FAIL_COND_MSG(found_it == false, "Was not possible to find the first vertex of this polygon. FBX file is corrupted.");
+
+		} else {
+			ERR_FAIL_INDEX_MSG((size_t)(m_edges[i] + 1), m_face_indices.size(), "FBX The other FBX edge seems to point to an invalid vertices. This FBX file is corrupted.");
+			// Take the next vertex
+			polygon_vertex_1 = m_face_indices[m_edges[i] + 1];
+		}
+
+		if (polygon_vertex_1 < 0) {
+			// We don't care if the `polygon_vertex_1` is the end of the polygon,
+			//  for `polygon_vertex_1` so we can just invert it.
+			polygon_vertex_1 = ~polygon_vertex_1;
+		}
+
+		ERR_FAIL_COND_MSG(polygon_vertex_0 == polygon_vertex_1, "The vertices of this edge can't be the same, Is this a point???. This FBX file is corrupted.");
+
+		// Just create the edge.
+		edge_map.push_back({ polygon_vertex_0, polygon_vertex_1 });
+	}
+}
+
+MeshGeometry::~MeshGeometry() {
+	// empty
+}
+
+const std::vector<Vector3> &MeshGeometry::get_vertices() const {
+	return m_vertices;
+}
+
+const std::vector<MeshGeometry::Edge> &MeshGeometry::get_edge_map() const {
+	return edge_map;
+}
+
+const std::vector<int> &MeshGeometry::get_polygon_indices() const {
+	return m_face_indices;
+}
+
+const std::vector<int> &MeshGeometry::get_edges() const {
+	return m_edges;
+}
+
+const MeshGeometry::MappingData<Vector3> &MeshGeometry::get_normals() const {
+	return m_normals;
+}
+
+const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_0() const {
+	//print_verbose("get uv_0 " + m_uv_0.debug_info() );
+	return m_uv_0;
+}
+
+const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_1() const {
+	//print_verbose("get uv_1 " + m_uv_1.debug_info() );
+	return m_uv_1;
+}
+
+const MeshGeometry::MappingData<Color> &MeshGeometry::get_colors() const {
+	return m_colors;
+}
+
+const MeshGeometry::MappingData<int> &MeshGeometry::get_material_allocation_id() const {
+	return m_material_allocation_ids;
+}
+
+int MeshGeometry::get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b) {
+	for (size_t i = 0; i < p_map.size(); i += 1) {
+		if ((p_map[i].vertex_0 == p_vertex_a && p_map[i].vertex_1 == p_vertex_b) || (p_map[i].vertex_1 == p_vertex_a && p_map[i].vertex_0 == p_vertex_b)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+MeshGeometry::Edge MeshGeometry::get_edge(const std::vector<Edge> &p_map, int p_id) {
+	ERR_FAIL_INDEX_V_MSG((size_t)p_id, p_map.size(), Edge({ -1, -1 }), "ID not found.");
+	return p_map[p_id];
+}
+
+template <class T>
+MeshGeometry::MappingData<T> MeshGeometry::resolve_vertex_data_array(
+		const ScopePtr source,
+		const std::string &MappingInformationType,
+		const std::string &ReferenceInformationType,
+		const std::string &dataElementName) {
+
+	ERR_FAIL_COND_V_MSG(source == nullptr, MappingData<T>(), "Invalid scope operator preventing memory corruption");
+
+	// UVIndex, MaterialIndex, NormalIndex, etc..
+	std::string indexDataElementName = dataElementName + "Index";
+	// goal: expand everything to be per vertex
+
+	ReferenceType l_ref_type = ReferenceType::direct;
+
+	// Read the reference type into the enumeration
+	if (ReferenceInformationType == "IndexToDirect") {
+		l_ref_type = ReferenceType::index_to_direct;
+	} else if (ReferenceInformationType == "Index") {
+		// set non legacy index to direct mapping
+		l_ref_type = ReferenceType::index;
+	} else if (ReferenceInformationType == "Direct") {
+		l_ref_type = ReferenceType::direct;
+	} else {
+		ERR_FAIL_V_MSG(MappingData<T>(), "invalid reference type has the FBX format changed?");
+	}
+
+	MapType l_map_type = MapType::none;
+
+	if (MappingInformationType == "None") {
+		l_map_type = MapType::none;
+	} else if (MappingInformationType == "ByVertice") {
+		l_map_type = MapType::vertex;
+	} else if (MappingInformationType == "ByPolygonVertex") {
+		l_map_type = MapType::polygon_vertex;
+	} else if (MappingInformationType == "ByPolygon") {
+		l_map_type = MapType::polygon;
+	} else if (MappingInformationType == "ByEdge") {
+		l_map_type = MapType::edge;
+	} else if (MappingInformationType == "AllSame") {
+		l_map_type = MapType::all_the_same;
+	} else {
+		print_error("invalid mapping type: " + String(MappingInformationType.c_str()));
+	}
+
+	// create mapping data
+	MeshGeometry::MappingData<T> tempData;
+	tempData.map_type = l_map_type;
+	tempData.ref_type = l_ref_type;
+
+	// parse data into array
+	ParseVectorDataArray(tempData.data, GetRequiredElement(source, dataElementName));
+
+	// index array wont always exist
+	const ElementPtr element = GetOptionalElement(source, indexDataElementName);
+	if (element) {
+		ParseVectorDataArray(tempData.index, element);
+	}
+
+	return tempData;
+}
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+		Geometry(id, element, name, doc) {
+	const ScopePtr sc = element->Compound();
+	if (nullptr == sc) {
+		DOMError("failed to read Geometry object (class: Shape), no data scope found");
+	}
+	const ElementPtr Indexes = GetRequiredElement(sc, "Indexes", element);
+	const ElementPtr Normals = GetRequiredElement(sc, "Normals", element);
+	const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
+	ParseVectorDataArray(m_indices, Indexes);
+	ParseVectorDataArray(m_vertices, Vertices);
+	ParseVectorDataArray(m_normals, Normals);
+}
+
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::~ShapeGeometry() {
+	// empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &ShapeGeometry::GetVertices() const {
+	return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &ShapeGeometry::GetNormals() const {
+	return m_normals;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<unsigned int> &ShapeGeometry::GetIndices() const {
+	return m_indices;
+}
+// ------------------------------------------------------------------------------------------------
+LineGeometry::LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+		Geometry(id, element, name, doc) {
+	const ScopePtr sc = element->Compound();
+	if (!sc) {
+		DOMError("failed to read Geometry object (class: Line), no data scope found");
+	}
+	const ElementPtr Points = GetRequiredElement(sc, "Points", element);
+	const ElementPtr PointsIndex = GetRequiredElement(sc, "PointsIndex", element);
+	ParseVectorDataArray(m_vertices, Points);
+	ParseVectorDataArray(m_indices, PointsIndex);
+}
+
+// ------------------------------------------------------------------------------------------------
+LineGeometry::~LineGeometry() {
+	// empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &LineGeometry::GetVertices() const {
+	return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<int> &LineGeometry::GetIndices() const {
+	return m_indices;
+}
+
+} // namespace FBXDocParser

+ 263 - 0
modules/fbx/fbx_parser/FBXMeshGeometry.h

@@ -0,0 +1,263 @@
+/*************************************************************************/
+/*  FBXMeshGeometry.h                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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 FBX_MESH_GEOMETRY_H
+#define FBX_MESH_GEOMETRY_H
+
+#include "core/color.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+#include "core/vector.h"
+
+#include "FBXDocument.h"
+#include "FBXParser.h"
+
+#include <iostream>
+
+#define AI_MAX_NUMBER_OF_TEXTURECOORDS 4
+#define AI_MAX_NUMBER_OF_COLOR_SETS 8
+
+namespace FBXDocParser {
+
+/*
+ * DOM base class for all kinds of FBX geometry
+ */
+class Geometry : public Object {
+public:
+	Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+	virtual ~Geometry();
+
+	/** Get the Skin attached to this geometry or NULL */
+	const Skin *DeformerSkin() const;
+
+	const std::vector<const BlendShape *> &get_blend_shapes() const;
+
+	size_t get_blend_shape_count() const {
+		return blendShapes.size();
+	}
+
+private:
+	const Skin *skin;
+	std::vector<const BlendShape *> blendShapes;
+};
+
+typedef std::vector<int> MatIndexArray;
+
+/// Map Geometry stores the FBX file information.
+///
+/// # FBX doc.
+/// ## Reference type declared:
+/// 	- Direct (directly related to the mapping information type)
+/// 	- IndexToDirect (Map with key value, meaning depends on the MappingInformationType)
+///
+/// ## Map Type:
+/// 	* None The mapping is undetermined.
+/// 	* ByVertex There will be one mapping coordinate for each surface control point/vertex (ControlPoint is a vertex).
+/// 		* If you have direct reference type verticies[x]
+/// 		* If you have IndexToDirect reference type the UV
+/// 	* ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex)
+/// 	* ByPolygon There can be only one mapping coordinate for the whole polygon.
+/// 		* One mapping per polygon polygon x has this normal x
+/// 		* For each vertex of the polygon then set the normal to x
+/// 	* ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id)
+/// 	* AllSame There can be only one mapping coordinate for the whole surface.
+class MeshGeometry : public Geometry {
+public:
+	enum class MapType {
+		none = 0, // No mapping type. Stored as "None".
+		vertex, // Maps per vertex. Stored as "ByVertice".
+		polygon_vertex, // Maps per polygon vertex. Stored as "ByPolygonVertex".
+		polygon, // Maps per polygon. Stored as "ByPolygon".
+		edge, // Maps per edge. Stored as "ByEdge".
+		all_the_same // Uaps to everything. Stored as "AllSame".
+	};
+
+	enum class ReferenceType {
+		direct = 0,
+		index = 1,
+		index_to_direct = 2
+	};
+
+	template <class T>
+	struct MappingData {
+		MapType map_type = MapType::none;
+		ReferenceType ref_type = ReferenceType::direct;
+		std::vector<T> data;
+		/// The meaning of the indices depends from the `MapType`.
+		/// If `ref_type` is `direct` this map is hollow.
+		std::vector<int> index;
+
+		String debug_info() const {
+			return "indexes: " + itos(index.size()) + " data: " + itos(data.size());
+		}
+	};
+
+	struct Edge {
+		int vertex_0 = 0, vertex_1 = 0;
+		Edge(int v0, int v1) :
+				vertex_0(v0), vertex_1(v1) {}
+		Edge() {}
+	};
+
+public:
+	MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+	virtual ~MeshGeometry();
+
+	const std::vector<Vector3> &get_vertices() const;
+	const std::vector<Edge> &get_edge_map() const;
+	const std::vector<int> &get_polygon_indices() const;
+	const std::vector<int> &get_edges() const;
+	const MappingData<Vector3> &get_normals() const;
+	const MappingData<Vector2> &get_uv_0() const;
+	const MappingData<Vector2> &get_uv_1() const;
+	const MappingData<Color> &get_colors() const;
+	const MappingData<int> &get_material_allocation_id() const;
+
+	/// Returns -1 if the vertices doesn't form an edge. Vertex order, doesn't
+	// matter.
+	static int get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b);
+	// Retuns the edge point bu that ID, or the edge with -1 vertices if the
+	// id is not valid.
+	static Edge get_edge(const std::vector<Edge> &p_map, int p_id);
+
+private:
+	// Read directly from the FBX file.
+	std::vector<Vector3> m_vertices;
+	std::vector<Edge> edge_map;
+	std::vector<int> m_face_indices;
+	std::vector<int> m_edges;
+	MappingData<Vector3> m_normals;
+	MappingData<Vector2> m_uv_0; // first uv coordinates
+	MappingData<Vector2> m_uv_1; // second uv coordinates
+	MappingData<Color> m_colors; // colors for the mesh
+	MappingData<int> m_material_allocation_ids; // slot of material used
+
+	template <class T>
+	MappingData<T> resolve_vertex_data_array(
+			const ScopePtr source,
+			const std::string &MappingInformationType,
+			const std::string &ReferenceInformationType,
+			const std::string &dataElementName);
+};
+
+/*
+ * DOM class for FBX geometry of type "Shape"
+ */
+class ShapeGeometry : public Geometry {
+public:
+	/** The class constructor */
+	ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+	/** The class destructor */
+	virtual ~ShapeGeometry();
+
+	/** Get a list of all vertex points, non-unique*/
+	const std::vector<Vector3> &GetVertices() const;
+
+	/** Get a list of all vertex normals or an empty array if
+    *  no normals are specified. */
+	const std::vector<Vector3> &GetNormals() const;
+
+	/** Return list of vertex indices. */
+	const std::vector<unsigned int> &GetIndices() const;
+
+private:
+	std::vector<Vector3> m_vertices;
+	std::vector<Vector3> m_normals;
+	std::vector<unsigned int> m_indices;
+};
+/**
+*  DOM class for FBX geometry of type "Line"
+*/
+class LineGeometry : public Geometry {
+public:
+	/** The class constructor */
+	LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+	/** The class destructor */
+	virtual ~LineGeometry();
+
+	/** Get a list of all vertex points, non-unique*/
+	const std::vector<Vector3> &GetVertices() const;
+
+	/** Return list of vertex indices. */
+	const std::vector<int> &GetIndices() const;
+
+private:
+	std::vector<Vector3> m_vertices;
+	std::vector<int> m_indices;
+};
+
+} // namespace FBXDocParser
+
+#endif // FBX_MESH_GEOMETRY_H

+ 181 - 0
modules/fbx/fbx_parser/FBXModel.cpp

@@ -0,0 +1,181 @@
+/*************************************************************************/
+/*  FBXModel.cpp                                                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXModel.cpp
+ *  @brief Assimp::FBX::Model implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name), shading("Y") {
+	const ScopePtr sc = GetRequiredScope(element);
+	const ElementPtr Shading = sc->GetElement("Shading");
+	const ElementPtr Culling = sc->GetElement("Culling");
+
+	if (Shading) {
+		shading = GetRequiredToken(Shading, 0)->StringContents();
+	}
+
+	if (Culling) {
+		culling = ParseTokenAsString(GetRequiredToken(Culling, 0));
+	}
+
+	props = GetPropertyTable(doc, "Model.FbxNode", element, sc);
+	ResolveLinks(element, doc);
+}
+
+// ------------------------------------------------------------------------------------------------
+Model::~Model() {
+	if (props != nullptr) {
+		delete props;
+		props = nullptr;
+	}
+}
+
+ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Model(id, element, doc, name){
+
+		};
+
+ModelLimbNode::~ModelLimbNode() {
+}
+
+// ------------------------------------------------------------------------------------------------
+void Model::ResolveLinks(const ElementPtr element, const Document &doc) {
+	const char *const arr[] = { "Geometry", "Material", "NodeAttribute" };
+
+	// resolve material
+	const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), arr, 3);
+
+	materials.reserve(conns.size());
+	geometry.reserve(conns.size());
+	attributes.reserve(conns.size());
+	for (const Connection *con : conns) {
+
+		// material and geometry links should be Object-Object connections
+		if (con->PropertyName().length()) {
+			continue;
+		}
+
+		const Object *const ob = con->SourceObject();
+		if (!ob) {
+			//DOMWarning("failed to read source object for incoming Model link, ignoring",&element);
+			continue;
+		}
+
+		const Material *const mat = dynamic_cast<const Material *>(ob);
+		if (mat) {
+			materials.push_back(mat);
+			continue;
+		}
+
+		const Geometry *const geo = dynamic_cast<const Geometry *>(ob);
+		if (geo) {
+			geometry.push_back(geo);
+			continue;
+		}
+
+		const NodeAttribute *const att = dynamic_cast<const NodeAttribute *>(ob);
+		if (att) {
+			attributes.push_back(att);
+			continue;
+		}
+
+		DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring", element);
+		continue;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Model::IsNull() const {
+	const std::vector<const NodeAttribute *> &attrs = GetAttributes();
+	for (const NodeAttribute *att : attrs) {
+
+		const Null *null_tag = dynamic_cast<const Null *>(att);
+		if (null_tag) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+} // namespace FBXDocParser

+ 184 - 0
modules/fbx/fbx_parser/FBXNodeAttribute.cpp

@@ -0,0 +1,184 @@
+/*************************************************************************/
+/*  FBXNodeAttribute.cpp                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXNoteAttribute.cpp
+ *  @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+#include <iostream>
+
+namespace FBXDocParser {
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name), props() {
+	const ScopePtr sc = GetRequiredScope(element);
+
+	const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+
+	// hack on the deriving type but Null/LimbNode attributes are the only case in which
+	// the property table is by design absent and no warning should be generated
+	// for it.
+	const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode");
+	props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb);
+}
+
+// ------------------------------------------------------------------------------------------------
+NodeAttribute::~NodeAttribute() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+CameraSwitcher::CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		NodeAttribute(id, element, doc, name) {
+	const ScopePtr sc = GetRequiredScope(element);
+	const ElementPtr CameraId = sc->GetElement("CameraId");
+	const ElementPtr CameraName = sc->GetElement("CameraName");
+	const ElementPtr CameraIndexName = sc->GetElement("CameraIndexName");
+
+	if (CameraId) {
+		cameraId = ParseTokenAsInt(GetRequiredToken(CameraId, 0));
+	}
+
+	if (CameraName) {
+		cameraName = GetRequiredToken(CameraName, 0)->StringContents();
+	}
+
+	if (CameraIndexName && CameraIndexName->Tokens().size()) {
+		cameraIndexName = GetRequiredToken(CameraIndexName, 0)->StringContents();
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+CameraSwitcher::~CameraSwitcher() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Camera::Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		NodeAttribute(id, element, doc, name) {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Camera::~Camera() {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Light::Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		NodeAttribute(id, element, doc, name) {
+	// empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Light::~Light() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Null::Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		NodeAttribute(id, element, doc, name) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Null::~Null() {
+}
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		NodeAttribute(id, element, doc, name) {
+	//std::cout << "limb node: " << name << std::endl;
+	//const Scope &sc = GetRequiredScope(element);
+
+	//const ElementPtr const TypeFlag = sc["TypeFlags"];
+
+	// keep this it can dump new properties for you
+	// for( auto element : sc.Elements())
+	// {
+	//     std::cout << "limbnode element: " << element.first << std::endl;
+	// }
+
+	// if(TypeFlag)
+	// {
+	// //    std::cout << "type flag: " << GetRequiredToken(*TypeFlag, 0).StringContents() << std::endl;
+	// }
+}
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::~LimbNode() {
+}
+
+} // namespace FBXDocParser

+ 111 - 0
modules/fbx/fbx_parser/FBXParseTools.h

@@ -0,0 +1,111 @@
+/*************************************************************************/
+/*  FBXParseTools.h                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FBX_PARSE_TOOLS_H
+#define FBX_PARSE_TOOLS_H
+
+#include "core/error_macros.h"
+#include "core/ustring.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <locale>
+
+template <class char_t>
+inline bool IsNewLine(char_t c) {
+	return c == '\n' || c == '\r';
+}
+template <class char_t>
+inline bool IsSpace(char_t c) {
+	return (c == (char_t)' ' || c == (char_t)'\t');
+}
+
+template <class char_t>
+inline bool IsSpaceOrNewLine(char_t c) {
+	return IsNewLine(c) || IsSpace(c);
+}
+
+template <class char_t>
+inline bool IsLineEnd(char_t c) {
+	return (c == (char_t)'\r' || c == (char_t)'\n' || c == (char_t)'\0' || c == (char_t)'\f');
+}
+
+// ------------------------------------------------------------------------------------
+// Special version of the function, providing higher accuracy and safety
+// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
+// ------------------------------------------------------------------------------------
+inline uint64_t strtoul10_64(const char *in, bool &errored, const char **out = 0, unsigned int *max_inout = 0) {
+	unsigned int cur = 0;
+	uint64_t value = 0;
+
+	errored = *in < '0' || *in > '9';
+	ERR_FAIL_COND_V_MSG(errored, 0, "The string cannot be converted parser error");
+
+	for (;;) {
+		if (*in < '0' || *in > '9') {
+			break;
+		}
+
+		const uint64_t new_value = (value * (uint64_t)10) + ((uint64_t)(*in - '0'));
+
+		// numeric overflow, we rely on you
+		if (new_value < value) {
+			//WARN_PRINT( "Converting the string \" " + in + " \" into a value resulted in overflow." );
+			return 0;
+		}
+
+		value = new_value;
+
+		++in;
+		++cur;
+
+		if (max_inout && *max_inout == cur) {
+			if (out) { /* skip to end */
+				while (*in >= '0' && *in <= '9') {
+					++in;
+				}
+				*out = in;
+			}
+
+			return value;
+		}
+	}
+	if (out) {
+		*out = in;
+	}
+
+	if (max_inout) {
+		*max_inout = cur;
+	}
+
+	return value;
+}
+
+#endif // FBX_PARSE_TOOLS_H

+ 1295 - 0
modules/fbx/fbx_parser/FBXParser.cpp

@@ -0,0 +1,1295 @@
+/*************************************************************************/
+/*  FBXParser.cpp                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXParser.cpp
+ *  @brief Implementation of the FBX parser and the rudimentary DOM that we use
+ */
+
+#include "thirdparty/zlib/zlib.h"
+#include <stdlib.h> /* strtol */
+
+#include "ByteSwapper.h"
+#include "FBXParseTools.h"
+#include "FBXParser.h"
+#include "FBXTokenizer.h"
+#include "core/math/math_defs.h"
+#include "core/math/transform.h"
+#include "core/math/vector3.h"
+#include "core/print_string.h"
+
+using namespace FBXDocParser;
+namespace {
+
+// Initially, we did reinterpret_cast, breaking strict aliasing rules.
+// This actually caused trouble on Android, so let's be safe this time.
+// https://github.com/assimp/assimp/issues/24
+template <typename T>
+T SafeParse(const char *data, const char *end) {
+	// Actual size validation happens during Tokenization so
+	// this is valid as an assertion.
+	(void)(end);
+	//ai_assert(static_cast<size_t>(end - data) >= sizeof(T));
+	T result = static_cast<T>(0);
+	::memcpy(&result, data, sizeof(T));
+	return result;
+}
+} // namespace
+
+namespace FBXDocParser {
+
+// ------------------------------------------------------------------------------------------------
+Element::Element(const TokenPtr key_token, Parser &parser) :
+		key_token(key_token) {
+	TokenPtr n = nullptr;
+	do {
+		n = parser.AdvanceToNextToken();
+		if (n == nullptr) {
+			continue;
+		}
+
+		if (!n) {
+			print_error("unexpected end of file, expected closing bracket" + String(parser.LastToken()->StringContents().c_str()));
+		}
+
+		if (n && n->Type() == TokenType_DATA) {
+			tokens.push_back(n);
+			TokenPtr prev = n;
+			n = parser.AdvanceToNextToken();
+
+			if (n == nullptr) {
+				break;
+			}
+
+			if (!n) {
+				print_error("unexpected end of file, expected bracket, comma or key" + String(parser.LastToken()->StringContents().c_str()));
+			}
+
+			const TokenType ty = n->Type();
+
+			// some exporters are missing a comma on the next line
+			if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) {
+				tokens.push_back(n);
+				continue;
+			}
+
+			if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) {
+				print_error("unexpected token; expected bracket, comma or key" + String(n->StringContents().c_str()));
+			}
+		}
+
+		if (n && n->Type() == TokenType_OPEN_BRACKET) {
+			compound = new_Scope(parser);
+			parser.scopes.push_back(compound);
+
+			// current token should be a TOK_CLOSE_BRACKET
+			n = parser.CurrentToken();
+
+			if (n && n->Type() != TokenType_CLOSE_BRACKET) {
+				print_error("expected closing bracket" + String(n->StringContents().c_str()));
+			}
+
+			parser.AdvanceToNextToken();
+			return;
+		}
+	} while (n && n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET);
+}
+
+// ------------------------------------------------------------------------------------------------
+Element::~Element() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::Scope(Parser &parser, bool topLevel) {
+	if (!topLevel) {
+		TokenPtr t = parser.CurrentToken();
+		if (t->Type() != TokenType_OPEN_BRACKET) {
+			print_error("expected open bracket" + String(t->StringContents().c_str()));
+		}
+	}
+
+	TokenPtr n = parser.AdvanceToNextToken();
+	if (n == nullptr) {
+		print_error("unexpected end of file");
+	}
+
+	// note: empty scopes are allowed
+	while (n && n->Type() != TokenType_CLOSE_BRACKET) {
+		if (n->Type() != TokenType_KEY) {
+			print_error("unexpected token, expected TOK_KEY" + String(n->StringContents().c_str()));
+		}
+
+		const std::string str = n->StringContents();
+
+		// std::multimap<std::string, ElementPtr> (key and value)
+		elements.insert(ElementMap::value_type(str, new_Element(n, parser)));
+
+		// Element() should stop at the next Key token (or right after a Close token)
+		n = parser.CurrentToken();
+		if (n == nullptr) {
+			if (topLevel) {
+				return;
+			}
+
+			//print_error("unexpected end of file" + String(parser.LastToken()->StringContents().c_str()));
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::~Scope() {
+	for (ElementMap::value_type &v : elements) {
+		delete v.second;
+		v.second = nullptr;
+	}
+
+	elements.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::Parser(const TokenList &tokens, bool is_binary) :
+		tokens(tokens), last(), current(), cursor(tokens.begin()), is_binary(is_binary) {
+	root = new_Scope(*this, true);
+	scopes.push_back(root);
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::~Parser() {
+	for (ScopePtr scope : scopes) {
+		delete scope;
+		scope = nullptr;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::AdvanceToNextToken() {
+	last = current;
+	if (cursor == tokens.end()) {
+		current = nullptr;
+	} else {
+		current = *cursor++;
+	}
+	return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::CurrentToken() const {
+	return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::LastToken() const {
+	return last;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out) {
+	ERR_FAIL_COND_V_MSG(t == nullptr, 0L, "Invalid token passed to ParseTokenAsID");
+	err_out = nullptr;
+
+	if (t->Type() != TokenType_DATA) {
+		err_out = "expected TOK_DATA token";
+		return 0L;
+	}
+
+	if (t->IsBinary()) {
+		const char *data = t->begin();
+		if (data[0] != 'L') {
+			err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
+			return 0L;
+		}
+
+		uint64_t id = SafeParse<uint64_t>(data + 1, t->end());
+		return id;
+	}
+
+	// XXX: should use size_t here
+	unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+	//ai_assert(length > 0);
+
+	const char *out = nullptr;
+	bool errored = false;
+
+	const uint64_t id = strtoul10_64(t->begin(), errored, &out, &length);
+	if (errored || out > t->end()) {
+		err_out = "failed to parse ID (text)";
+		return 0L;
+	}
+
+	return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsID() with print_error handling
+uint64_t ParseTokenAsID(const TokenPtr t) {
+	const char *err = nullptr;
+	const uint64_t i = ParseTokenAsID(t, err);
+	if (err) {
+		print_error(String(err) + " " + String(t->StringContents().c_str()));
+	}
+	return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out) {
+	// same as ID parsing, except there is a trailing asterisk
+	err_out = nullptr;
+
+	if (t->Type() != TokenType_DATA) {
+		err_out = "expected TOK_DATA token";
+		return 0;
+	}
+
+	if (t->IsBinary()) {
+		const char *data = t->begin();
+		if (data[0] != 'L') {
+			err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
+			return 0;
+		}
+
+		uint64_t id = SafeParse<uint64_t>(data + 1, t->end());
+		AI_SWAP8(id);
+		return static_cast<size_t>(id);
+	}
+
+	if (*t->begin() != '*') {
+		err_out = "expected asterisk before array dimension";
+		return 0;
+	}
+
+	// XXX: should use size_t here
+	unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+	if (length == 0) {
+		err_out = "expected valid integer number after asterisk";
+		return 0;
+	}
+
+	const char *out = nullptr;
+	bool errored = false;
+	const size_t id = static_cast<size_t>(strtoul10_64(t->begin() + 1, errored, &out, &length));
+	if (errored || out > t->end()) {
+		print_error("failed to parse id");
+		err_out = "failed to parse ID";
+		return 0;
+	}
+
+	return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+float ParseTokenAsFloat(const TokenPtr t, const char *&err_out) {
+	err_out = nullptr;
+
+	if (t->Type() != TokenType_DATA) {
+		err_out = "expected TOK_DATA token";
+		return 0.0f;
+	}
+
+	if (t->IsBinary()) {
+		const char *data = t->begin();
+		if (data[0] != 'F' && data[0] != 'D') {
+			err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)";
+			return 0.0f;
+		}
+
+		if (data[0] == 'F') {
+			return SafeParse<float>(data + 1, t->end());
+		} else {
+			return static_cast<float>(SafeParse<double>(data + 1, t->end()));
+		}
+	}
+
+// need to copy the input string to a temporary buffer
+// first - next in the fbx token stream comes ',',
+// which fast_atof could interpret as decimal point.
+#define MAX_FLOAT_LENGTH 31
+	char temp[MAX_FLOAT_LENGTH + 1];
+	const size_t length = static_cast<size_t>(t->end() - t->begin());
+	std::copy(t->begin(), t->end(), temp);
+	temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH), length)] = '\0';
+
+	return atof(temp);
+}
+
+// ------------------------------------------------------------------------------------------------
+int ParseTokenAsInt(const TokenPtr t, const char *&err_out) {
+	err_out = nullptr;
+
+	if (t->Type() != TokenType_DATA) {
+		err_out = "expected TOK_DATA token";
+		return 0;
+	}
+
+	// binary files are simple to parse
+	if (t->IsBinary()) {
+		const char *data = t->begin();
+		if (data[0] != 'I') {
+			err_out = "failed to parse I(nt), unexpected data type (binary)";
+			return 0;
+		}
+
+		int32_t ival = SafeParse<int32_t>(data + 1, t->end());
+		AI_SWAP4(ival);
+		return static_cast<int>(ival);
+	}
+
+	// ASCII files are unsafe.
+	const size_t length = static_cast<size_t>(t->end() - t->begin());
+	if (length == 0) {
+		err_out = "expected valid integer number after asterisk";
+		ERR_FAIL_V_MSG(0, "expected valid integer number after asterisk");
+	}
+
+	// must not be null for strtol to work
+	char *out = (char *)t->end();
+	// string begin, end ptr ref, base 10
+	const int value = strtol(t->begin(), &out, 10);
+	if (out == nullptr || out != t->end()) {
+		err_out = "failed to parse ID";
+		ERR_FAIL_V_MSG(0, "failed to parse ID");
+	}
+
+	return value;
+}
+
+// ------------------------------------------------------------------------------------------------
+int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out) {
+	err_out = nullptr;
+
+	if (t->Type() != TokenType_DATA) {
+		err_out = "expected TOK_DATA token";
+		return 0L;
+	}
+
+	if (t->IsBinary()) {
+		const char *data = t->begin();
+		if (data[0] != 'L') {
+			err_out = "failed to parse Int64, unexpected data type";
+			return 0L;
+		}
+
+		int64_t id = SafeParse<int64_t>(data + 1, t->end());
+		AI_SWAP8(id);
+		return id;
+	}
+
+	// XXX: should use size_t here
+	unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+	//ai_assert(length > 0);
+
+	char *out = nullptr;
+	const int64_t id = strtol(t->begin(), &out, length);
+	if (out > t->end()) {
+		err_out = "failed to parse Int64 (text)";
+		return 0L;
+	}
+
+	return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string ParseTokenAsString(const TokenPtr t, const char *&err_out) {
+	err_out = nullptr;
+
+	if (t->Type() != TokenType_DATA) {
+		err_out = "expected TOK_DATA token";
+		return "";
+	}
+
+	if (t->IsBinary()) {
+		const char *data = t->begin();
+		if (data[0] != 'S') {
+			err_out = "failed to parse String, unexpected data type (binary)";
+			return "";
+		}
+
+		// read string length
+		int32_t len = SafeParse<int32_t>(data + 1, t->end());
+		AI_SWAP4(len);
+
+		//ai_assert(t.end() - data == 5 + len);
+		return std::string(data + 5, len);
+	}
+
+	const size_t length = static_cast<size_t>(t->end() - t->begin());
+	if (length < 2) {
+		err_out = "token is too short to hold a string";
+		return "";
+	}
+
+	const char *s = t->begin(), *e = t->end() - 1;
+	if (*s != '\"' || *e != '\"') {
+		err_out = "expected double quoted string";
+		return "";
+	}
+
+	return std::string(s + 1, length - 2);
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// read the type code and element count of a binary data array and stop there
+void ReadBinaryDataArrayHead(const char *&data, const char *end, char &type, uint32_t &count,
+		const ElementPtr el) {
+	TokenPtr token = el->KeyToken();
+	if (static_cast<size_t>(end - data) < 5) {
+		print_error("binary data array is too short, need five (5) bytes for type signature and element count: " + String(token->StringContents().c_str()));
+	}
+
+	// data type
+	type = *data;
+
+	// read number of elements
+	uint32_t len = SafeParse<uint32_t>(data + 1, end);
+	AI_SWAP4(len);
+
+	count = len;
+	data += 5;
+}
+
+// ------------------------------------------------------------------------------------------------
+// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header)
+void ReadBinaryDataArray(char type, uint32_t count, const char *&data, const char *end,
+		std::vector<char> &buff,
+		const ElementPtr /*el*/) {
+	uint32_t encmode = SafeParse<uint32_t>(data, end);
+	AI_SWAP4(encmode);
+	data += 4;
+
+	// next comes the compressed length
+	uint32_t comp_len = SafeParse<uint32_t>(data, end);
+	AI_SWAP4(comp_len);
+	data += 4;
+
+	//ai_assert(data + comp_len == end);
+
+	// determine the length of the uncompressed data by looking at the type signature
+	uint32_t stride = 0;
+	switch (type) {
+		case 'f':
+		case 'i':
+			stride = 4;
+			break;
+
+		case 'd':
+		case 'l':
+			stride = 8;
+			break;
+	}
+
+	const uint32_t full_length = stride * count;
+	buff.resize(full_length);
+
+	if (encmode == 0) {
+		//ai_assert(full_length == comp_len);
+
+		// plain data, no compression
+		std::copy(data, end, buff.begin());
+	} else if (encmode == 1) {
+		// zlib/deflate, next comes ZIP head (0x78 0x01)
+		// see http://www.ietf.org/rfc/rfc1950.txt
+
+		z_stream zstream;
+		zstream.opaque = Z_NULL;
+		zstream.zalloc = Z_NULL;
+		zstream.zfree = Z_NULL;
+		zstream.data_type = Z_BINARY;
+
+		// http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
+		if (Z_OK != inflateInit(&zstream)) {
+			print_error("failure initializing zlib");
+		}
+
+		zstream.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(data));
+		zstream.avail_in = comp_len;
+
+		zstream.avail_out = static_cast<uInt>(buff.size());
+		zstream.next_out = reinterpret_cast<Bytef *>(&*buff.begin());
+		const int ret = inflate(&zstream, Z_FINISH);
+
+		if (ret != Z_STREAM_END && ret != Z_OK) {
+			print_error("failure decompressing compressed data section");
+		}
+
+		// terminate zlib
+		inflateEnd(&zstream);
+	}
+#ifdef ASSIMP_BUILD_DEBUG
+	else {
+		// runtime check for this happens at tokenization stage
+		//ai_assert(false);
+	}
+#endif
+
+	data += comp_len;
+	//ai_assert(data == end);
+}
+
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+// read an array of float3 tuples
+void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el) {
+	out.resize(0);
+
+	const TokenList &tok = el->Tokens();
+	TokenPtr token = el->KeyToken();
+	if (tok.empty()) {
+		print_error("unexpected empty element" + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (count % 3 != 0) {
+			print_error("number of floats is not a multiple of three (3) (binary)" + String(token->StringContents().c_str()));
+		}
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		const uint32_t count3 = count / 3;
+		out.reserve(count3);
+
+		if (type == 'd') {
+			const double *d = reinterpret_cast<const double *>(&buff[0]);
+			for (unsigned int i = 0; i < count3; ++i, d += 3) {
+				out.push_back(Vector3(static_cast<real_t>(d[0]),
+						static_cast<real_t>(d[1]),
+						static_cast<real_t>(d[2])));
+			}
+			// for debugging
+			/*for ( size_t i = 0; i < out.size(); i++ ) {
+                aiVector3D vec3( out[ i ] );
+                std::stringstream stream;
+                stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl;
+                DefaultLogger::get()->info( stream.str() );
+            }*/
+		} else if (type == 'f') {
+			const float *f = reinterpret_cast<const float *>(&buff[0]);
+			for (unsigned int i = 0; i < count3; ++i, f += 3) {
+				out.push_back(Vector3(f[0], f[1], f[2]));
+			}
+		}
+
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	// may throw bad_alloc if the input is rubbish, but this need
+	// not to be prevented - importing would fail but we wouldn't
+	// crash since assimp handles this case properly.
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	if (a->Tokens().size() % 3 != 0) {
+		print_error("number of floats is not a multiple of three (3)" + String(token->StringContents().c_str()));
+	}
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		Vector3 v;
+		v.x = ParseTokenAsFloat(*it++);
+		v.y = ParseTokenAsFloat(*it++);
+		v.z = ParseTokenAsFloat(*it++);
+
+		out.push_back(v);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of color4 tuples
+void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el) {
+	out.resize(0);
+	const TokenList &tok = el->Tokens();
+
+	TokenPtr token = el->KeyToken();
+
+	if (tok.empty()) {
+		print_error("unexpected empty element" + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (count % 4 != 0) {
+			print_error("number of floats is not a multiple of four (4) (binary)" + String(token->StringContents().c_str()));
+		}
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		const uint32_t count4 = count / 4;
+		out.reserve(count4);
+
+		if (type == 'd') {
+			const double *d = reinterpret_cast<const double *>(&buff[0]);
+			for (unsigned int i = 0; i < count4; ++i, d += 4) {
+				out.push_back(Color(static_cast<float>(d[0]),
+						static_cast<float>(d[1]),
+						static_cast<float>(d[2]),
+						static_cast<float>(d[3])));
+			}
+		} else if (type == 'f') {
+			const float *f = reinterpret_cast<const float *>(&buff[0]);
+			for (unsigned int i = 0; i < count4; ++i, f += 4) {
+				out.push_back(Color(f[0], f[1], f[2], f[3]));
+			}
+		}
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	//  see notes in ParseVectorDataArray() above
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	if (a->Tokens().size() % 4 != 0) {
+		print_error("number of floats is not a multiple of four (4)" + String(token->StringContents().c_str()));
+	}
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		Color v;
+		v.r = ParseTokenAsFloat(*it++);
+		v.g = ParseTokenAsFloat(*it++);
+		v.b = ParseTokenAsFloat(*it++);
+		v.a = ParseTokenAsFloat(*it++);
+
+		out.push_back(v);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of float2 tuples
+void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el) {
+	out.resize(0);
+	const TokenList &tok = el->Tokens();
+	TokenPtr token = el->KeyToken();
+	if (tok.empty()) {
+		print_error("unexpected empty element" + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (count % 2 != 0) {
+			print_error("number of floats is not a multiple of two (2) (binary)" + String(token->StringContents().c_str()));
+		}
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		const uint32_t count2 = count / 2;
+		out.reserve(count2);
+
+		if (type == 'd') {
+			const double *d = reinterpret_cast<const double *>(&buff[0]);
+			for (unsigned int i = 0; i < count2; ++i, d += 2) {
+				out.push_back(Vector2(static_cast<float>(d[0]),
+						static_cast<float>(d[1])));
+			}
+		} else if (type == 'f') {
+			const float *f = reinterpret_cast<const float *>(&buff[0]);
+			for (unsigned int i = 0; i < count2; ++i, f += 2) {
+				out.push_back(Vector2(f[0], f[1]));
+			}
+		}
+
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	// see notes in ParseVectorDataArray() above
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	if (a->Tokens().size() % 2 != 0) {
+		print_error("number of floats is not a multiple of two (2)" + String(token->StringContents().c_str()));
+	}
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		Vector2 v;
+		v.x = ParseTokenAsFloat(*it++);
+		v.y = ParseTokenAsFloat(*it++);
+		out.push_back(v);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of ints
+void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el) {
+	out.resize(0);
+	const TokenList &tok = el->Tokens();
+	TokenPtr token = el->KeyToken();
+	if (tok.empty()) {
+		print_error("unexpected empty element" + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'i') {
+			print_error("expected int array (binary)" + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * 4);
+
+		out.reserve(count);
+
+		const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]);
+		for (unsigned int i = 0; i < count; ++i, ++ip) {
+			int32_t val = *ip;
+			AI_SWAP4(val);
+			out.push_back(val);
+		}
+
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	// see notes in ParseVectorDataArray()
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		const int ival = ParseTokenAsInt(*it++);
+		out.push_back(ival);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of floats
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el) {
+	out.resize(0);
+	const TokenList &tok = el->Tokens();
+	TokenPtr token = el->KeyToken();
+	if (tok.empty()) {
+		print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'd' && type != 'f') {
+			print_error("expected float or double array (binary) " + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+		if (type == 'd') {
+			const double *d = reinterpret_cast<const double *>(&buff[0]);
+			for (unsigned int i = 0; i < count; ++i, ++d) {
+				out.push_back(static_cast<float>(*d));
+			}
+		} else if (type == 'f') {
+			const float *f = reinterpret_cast<const float *>(&buff[0]);
+			for (unsigned int i = 0; i < count; ++i, ++f) {
+				out.push_back(*f);
+			}
+		}
+
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	// see notes in ParseVectorDataArray()
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		const float ival = ParseTokenAsFloat(*it++);
+		out.push_back(ival);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of uints
+void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el) {
+	out.resize(0);
+	const TokenList &tok = el->Tokens();
+	const TokenPtr token = el->KeyToken();
+
+	ERR_FAIL_COND_MSG(!token, "invalid ParseVectorDataArrat token invalid");
+
+	if (tok.empty()) {
+		print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'i') {
+			print_error("expected (u)int array (binary)" + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * 4);
+
+		out.reserve(count);
+
+		const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]);
+		for (unsigned int i = 0; i < count; ++i, ++ip) {
+			int32_t val = *ip;
+			if (val < 0) {
+				print_error("encountered negative integer index (binary)");
+			}
+
+			out.push_back(val);
+		}
+
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	// see notes in ParseVectorDataArray()
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		const int ival = ParseTokenAsInt(*it++);
+		if (ival < 0) {
+			print_error("encountered negative integer index");
+		}
+		out.push_back(static_cast<unsigned int>(ival));
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of uint64_ts
+void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr el) {
+	out.resize(0);
+
+	const TokenList &tok = el->Tokens();
+	TokenPtr token = el->KeyToken();
+	ERR_FAIL_COND(!token);
+
+	if (tok.empty()) {
+		print_error("unexpected empty element " + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'l') {
+			print_error("expected long array (binary): " + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * 8);
+
+		out.reserve(count);
+
+		const uint64_t *ip = reinterpret_cast<const uint64_t *>(&buff[0]);
+		for (unsigned int i = 0; i < count; ++i, ++ip) {
+			uint64_t val = *ip;
+			AI_SWAP8(val);
+			out.push_back(val);
+		}
+
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	// see notes in ParseVectorDataArray()
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		const uint64_t ival = ParseTokenAsID(*it++);
+
+		out.push_back(ival);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of int64_ts
+void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el) {
+	out.resize(0);
+	const TokenList &tok = el->Tokens();
+	TokenPtr token = el->KeyToken();
+	ERR_FAIL_COND(!token);
+	if (tok.empty()) {
+		print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+	}
+
+	if (tok[0]->IsBinary()) {
+		const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+		char type;
+		uint32_t count;
+		ReadBinaryDataArrayHead(data, end, type, count, el);
+
+		if (!count) {
+			return;
+		}
+
+		if (type != 'l') {
+			print_error("expected long array (binary) " + String(token->StringContents().c_str()));
+		}
+
+		std::vector<char> buff;
+		ReadBinaryDataArray(type, count, data, end, buff, el);
+
+		//ai_assert(data == end);
+		//ai_assert(buff.size() == count * 8);
+
+		out.reserve(count);
+
+		const int64_t *ip = reinterpret_cast<const int64_t *>(&buff[0]);
+		for (unsigned int i = 0; i < count; ++i, ++ip) {
+			int64_t val = *ip;
+			AI_SWAP8(val);
+			out.push_back(val);
+		}
+
+		return;
+	}
+
+	const size_t dim = ParseTokenAsDim(tok[0]);
+
+	// see notes in ParseVectorDataArray()
+	out.reserve(dim);
+
+	const ScopePtr scope = GetRequiredScope(el);
+	const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+	for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+		const int64_t val = ParseTokenAsInt64(*it++);
+		out.push_back(val);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+Transform ReadMatrix(const ElementPtr element) {
+	std::vector<float> values;
+	ParseVectorDataArray(values, element);
+
+	if (values.size() != 16) {
+		print_error("expected 16 matrix elements");
+	}
+
+	// clean values to prevent any IBM damage on inverse() / affine_inverse()
+	for (float &value : values) {
+		if (::Math::is_equal_approx(0, value)) {
+			value = 0;
+		}
+	}
+
+	Transform xform;
+	Basis basis;
+
+	basis.set(
+			Vector3(values[0], values[1], values[2]),
+			Vector3(values[4], values[5], values[6]),
+			Vector3(values[8], values[9], values[10]));
+
+	xform.basis = basis;
+	xform.origin = Vector3(values[12], values[13], values[14]);
+	// determine if we need to think about this with dynamic rotation order?
+	// for example:
+	// xform.basis = z_axis * y_axis * x_axis;
+	//xform.basis.transpose();
+
+	print_verbose("xform verbose basis: " + (xform.basis.get_euler() * (180 / Math_PI)) + " xform origin:" + xform.origin);
+
+	return xform;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsString() with print_error handling
+std::string ParseTokenAsString(const TokenPtr t) {
+	ERR_FAIL_COND_V(!t, "");
+	const char *err;
+	const std::string &i = ParseTokenAsString(t, err);
+	if (err) {
+		print_error(String(err) + ", " + String(t->StringContents().c_str()));
+	}
+	return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract a required element from a scope, abort if the element cannot be found
+const ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= NULL*/) {
+	const ElementPtr el = sc->GetElement(index);
+	TokenPtr token = el->KeyToken();
+	ERR_FAIL_COND_V(!token, nullptr);
+	if (!el) {
+		print_error("did not find required element \"" + String(index.c_str()) + "\" " + String(token->StringContents().c_str()));
+	}
+	return el;
+}
+
+bool HasElement(const ScopePtr sc, const std::string &index) {
+	const ElementPtr el = sc->GetElement(index);
+	if (nullptr == el) {
+		return false;
+	}
+
+	return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract a required element from a scope, abort if the element cannot be found
+const ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= NULL*/) {
+	const ElementPtr el = sc->GetElement(index);
+	return el;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract required compound scope
+const ScopePtr GetRequiredScope(const ElementPtr el) {
+	if (el) {
+		ScopePtr s = el->Compound();
+		TokenPtr token = el->KeyToken();
+		ERR_FAIL_COND_V(!token, nullptr);
+		if (s) {
+			return s;
+		}
+
+		ERR_FAIL_V_MSG(nullptr, "expected compound scope " + String(token->StringContents().c_str()));
+	}
+
+	ERR_FAIL_V_MSG(nullptr, "Invalid element supplied to parser");
+}
+
+// ------------------------------------------------------------------------------------------------
+// get token at a particular index
+TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index) {
+	if (el) {
+		const TokenList &x = el->Tokens();
+		TokenPtr token = el->KeyToken();
+
+		ERR_FAIL_COND_V(!token, nullptr);
+
+		if (index >= x.size()) {
+			ERR_FAIL_V_MSG(nullptr, "missing token at index: " + itos(index) + " " + String(token->StringContents().c_str()));
+		}
+
+		return x[index];
+	}
+
+	return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsDim() with print_error handling
+size_t ParseTokenAsDim(const TokenPtr t) {
+	const char *err;
+	const size_t i = ParseTokenAsDim(t, err);
+	if (err) {
+		print_error(String(err) + " " + String(t->StringContents().c_str()));
+	}
+	return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsFloat() with print_error handling
+float ParseTokenAsFloat(const TokenPtr t) {
+	const char *err;
+	const float i = ParseTokenAsFloat(t, err);
+	if (err) {
+		print_error(String(err) + " " + String(t->StringContents().c_str()));
+	}
+	return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt() with print_error handling
+int ParseTokenAsInt(const TokenPtr t) {
+	const char *err;
+	const int i = ParseTokenAsInt(t, err);
+	if (err) {
+		print_error(String(err) + " " + String(t->StringContents().c_str()));
+	}
+	return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt64() with print_error handling
+int64_t ParseTokenAsInt64(const TokenPtr t) {
+	const char *err;
+	const int64_t i = ParseTokenAsInt64(t, err);
+	if (err) {
+		print_error(String(err) + " " + String(t->StringContents().c_str()));
+	}
+	return i;
+}
+
+} // namespace FBXDocParser

+ 264 - 0
modules/fbx/fbx_parser/FBXParser.h

@@ -0,0 +1,264 @@
+/*************************************************************************/
+/*  FBXParser.h                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXParser.h
+ *  @brief FBX parsing code
+ */
+#ifndef FBX_PARSER_H
+#define FBX_PARSER_H
+
+#include <stdint.h>
+#include <map>
+#include <memory>
+
+#include "core/color.h"
+#include "core/math/transform.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+
+#include "FBXTokenizer.h"
+
+namespace FBXDocParser {
+
+class Scope;
+class Parser;
+class Element;
+
+typedef Element *ElementPtr;
+typedef Scope *ScopePtr;
+
+typedef std::vector<ScopePtr> ScopeList;
+typedef std::multimap<std::string, ElementPtr> ElementMap;
+typedef std::pair<ElementMap::const_iterator, ElementMap::const_iterator> ElementCollection;
+
+#define new_Scope new Scope
+#define new_Element new Element
+
+/** FBX data entity that consists of a key:value tuple.
+ *
+ *  Example:
+ *  @verbatim
+ *    AnimationCurve: 23, "AnimCurve::", "" {
+ *        [..]
+ *    }
+ *  @endverbatim
+ *
+ *  As can be seen in this sample, elements can contain nested #Scope
+ *  as their trailing member.  **/
+class Element {
+public:
+	Element(TokenPtr key_token, Parser &parser);
+	~Element();
+
+	ScopePtr Compound() const {
+		return compound;
+	}
+
+	TokenPtr KeyToken() const {
+		return key_token;
+	}
+
+	const TokenList &Tokens() const {
+		return tokens;
+	}
+
+private:
+	TokenList tokens;
+	ScopePtr compound = nullptr;
+	std::vector<ScopePtr> compound_scope;
+	TokenPtr key_token = nullptr;
+};
+
+/** FBX data entity that consists of a 'scope', a collection
+ *  of not necessarily unique #Element instances.
+ *
+ *  Example:
+ *  @verbatim
+ *    GlobalSettings:  {
+ *        Version: 1000
+ *        Properties70:
+ *        [...]
+ *    }
+ *  @endverbatim  */
+class Scope {
+public:
+	Scope(Parser &parser, bool topLevel = false);
+	~Scope();
+
+	ElementPtr GetElement(const std::string &index) const {
+		ElementMap::const_iterator it = elements.find(index);
+		return it == elements.end() ? nullptr : (*it).second;
+	}
+
+	ElementPtr FindElementCaseInsensitive(const std::string &elementName) const {
+		for (auto element = elements.begin(); element != elements.end(); ++element) {
+			if (element->first.compare(elementName)) {
+				return element->second;
+			}
+		}
+
+		// nothing to reference / expired.
+		return nullptr;
+	}
+
+	ElementCollection GetCollection(const std::string &index) const {
+		return elements.equal_range(index);
+	}
+
+	const ElementMap &Elements() const {
+		return elements;
+	}
+
+private:
+	ElementMap elements;
+};
+
+/** FBX parsing class, takes a list of input tokens and generates a hierarchy
+ *  of nested #Scope instances, representing the fbx DOM.*/
+class Parser {
+public:
+	/** Parse given a token list. Does not take ownership of the tokens -
+     *  the objects must persist during the entire parser lifetime */
+	Parser(const TokenList &tokens, bool is_binary);
+	~Parser();
+
+	const ScopePtr GetRootScope() const {
+		return root;
+	}
+
+	bool IsBinary() const {
+		return is_binary;
+	}
+
+private:
+	friend class Scope;
+	friend class Element;
+
+	TokenPtr AdvanceToNextToken();
+	TokenPtr LastToken() const;
+	TokenPtr CurrentToken() const;
+
+private:
+	ScopeList scopes;
+	const TokenList &tokens;
+
+	TokenPtr last = nullptr, current = nullptr;
+	TokenList::const_iterator cursor;
+	ScopePtr root = nullptr;
+
+	const bool is_binary;
+};
+
+/* token parsing - this happens when building the DOM out of the parse-tree*/
+uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out);
+size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out);
+float ParseTokenAsFloat(const TokenPtr t, const char *&err_out);
+int ParseTokenAsInt(const TokenPtr t, const char *&err_out);
+int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out);
+std::string ParseTokenAsString(const TokenPtr t, const char *&err_out);
+
+/* wrapper around ParseTokenAsXXX() with DOMError handling */
+uint64_t ParseTokenAsID(const TokenPtr t);
+size_t ParseTokenAsDim(const TokenPtr t);
+float ParseTokenAsFloat(const TokenPtr t);
+int ParseTokenAsInt(const TokenPtr t);
+int64_t ParseTokenAsInt64(const TokenPtr t);
+std::string ParseTokenAsString(const TokenPtr t);
+
+/* read data arrays */
+void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr ep);
+void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el);
+bool HasElement(const ScopePtr sc, const std::string &index);
+
+// extract a required element from a scope, abort if the element cannot be found
+const ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
+const ScopePtr GetRequiredScope(const ElementPtr el); // New in 2020. (less likely to destroy application)
+const ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
+// extract required compound scope
+const ScopePtr GetRequiredScope(const ElementPtr el);
+// get token at a particular index
+TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index);
+
+// ------------------------------------------------------------------------------------------------
+// read a 4x4 matrix from an array of 16 floats
+Transform ReadMatrix(const ElementPtr element);
+
+} // namespace FBXDocParser
+
+#endif // FBX_PARSER_H

+ 105 - 0
modules/fbx/fbx_parser/FBXPose.cpp

@@ -0,0 +1,105 @@
+/*************************************************************************/
+/*  FBXPose.cpp                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXNoteAttribute.cpp
+ *  @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXParser.h"
+#include <iostream>
+
+namespace FBXDocParser {
+
+class FbxPoseNode;
+// ------------------------------------------------------------------------------------------------
+FbxPose::FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+		Object(id, element, name) {
+	const ScopePtr sc = GetRequiredScope(element);
+	//const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+
+	const ElementCollection &PoseNodes = sc->GetCollection("PoseNode");
+	for (ElementMap::const_iterator it = PoseNodes.first; it != PoseNodes.second; ++it) {
+		std::string entry_name = (*it).first;
+		ElementPtr some_element = (*it).second;
+		FbxPoseNode *pose_node = new FbxPoseNode(some_element, doc, entry_name);
+		pose_nodes.push_back(pose_node);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+FbxPose::~FbxPose() {
+	pose_nodes.clear();
+	// empty
+}
+
+} // namespace FBXDocParser

+ 238 - 0
modules/fbx/fbx_parser/FBXProperties.cpp

@@ -0,0 +1,238 @@
+/*************************************************************************/
+/*  FBXProperties.cpp                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXProperties.cpp
+ *  @brief Implementation of the FBX dynamic properties system
+ */
+
+#include "FBXProperties.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+#include "FBXTokenizer.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Property::Property() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Property::~Property() {
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// read a typed property out of a FBX element. The return value is NULL if the property cannot be read.
+PropertyPtr ReadTypedProperty(const ElementPtr element) {
+	//ai_assert(element.KeyToken().StringContents() == "P");
+
+	const TokenList &tok = element->Tokens();
+	//ai_assert(tok.size() >= 5);
+
+	const std::string &s = ParseTokenAsString(tok[1]);
+	const char *const cs = s.c_str();
+	if (!strcmp(cs, "KString")) {
+		return new TypedProperty<std::string>(ParseTokenAsString(tok[4]));
+	} else if (!strcmp(cs, "bool") || !strcmp(cs, "Bool")) {
+		return new TypedProperty<bool>(ParseTokenAsInt(tok[4]) != 0);
+	} else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) {
+		return new TypedProperty<int>(ParseTokenAsInt(tok[4]));
+	} else if (!strcmp(cs, "ULongLong")) {
+		return new TypedProperty<uint64_t>(ParseTokenAsID(tok[4]));
+	} else if (!strcmp(cs, "KTime")) {
+		return new TypedProperty<int64_t>(ParseTokenAsInt64(tok[4]));
+	} else if (!strcmp(cs, "Vector3D") ||
+			   !strcmp(cs, "ColorRGB") ||
+			   !strcmp(cs, "Vector") ||
+			   !strcmp(cs, "Color") ||
+			   !strcmp(cs, "Lcl Translation") ||
+			   !strcmp(cs, "Lcl Rotation") ||
+			   !strcmp(cs, "Lcl Scaling")) {
+		return new TypedProperty<Vector3>(Vector3(
+				ParseTokenAsFloat(tok[4]),
+				ParseTokenAsFloat(tok[5]),
+				ParseTokenAsFloat(tok[6])));
+	} else if (!strcmp(cs, "double") || !strcmp(cs, "Number") || !strcmp(cs, "Float") || !strcmp(cs, "FieldOfView") || !strcmp(cs, "UnitScaleFactor")) {
+		return new TypedProperty<float>(ParseTokenAsFloat(tok[4]));
+	}
+
+	return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// peek into an element and check if it contains a FBX property, if so return its name.
+std::string PeekPropertyName(const Element &element) {
+	//ai_assert(element.KeyToken().StringContents() == "P");
+	const TokenList &tok = element.Tokens();
+	if (tok.size() < 4) {
+		return "";
+	}
+
+	return ParseTokenAsString(tok[0]);
+}
+
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::PropertyTable() :
+		templateProps(), element() {
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *templateProps) :
+		templateProps(templateProps), element(element) {
+	const ScopePtr scope = GetRequiredScope(element);
+	ERR_FAIL_COND(!scope);
+	for (const ElementMap::value_type &v : scope->Elements()) {
+		if (v.first != "P") {
+			DOMWarning("expected only P elements in property table", v.second);
+			continue;
+		}
+
+		const std::string &name = PeekPropertyName(*v.second);
+		if (!name.length()) {
+			DOMWarning("could not read property name", v.second);
+			continue;
+		}
+
+		LazyPropertyMap::const_iterator it = lazyProps.find(name);
+		if (it != lazyProps.end()) {
+			DOMWarning("duplicate property name, will hide previous value: " + name, v.second);
+			continue;
+		}
+
+		// since the above checks for duplicates we can be sure to insert the only match here.
+		lazyProps[name] = v.second;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::~PropertyTable() {
+	for (PropertyMap::value_type &v : props) {
+		delete v.second;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyPtr PropertyTable::Get(const std::string &name) const {
+	PropertyMap::const_iterator it = props.find(name);
+	if (it == props.end()) {
+		// hasn't been parsed yet?
+		LazyPropertyMap::const_iterator lit = lazyProps.find(name);
+		if (lit != lazyProps.end()) {
+			props[name] = ReadTypedProperty(lit->second);
+			it = props.find(name);
+
+			//ai_assert(it != props.end());
+		}
+
+		if (it == props.end()) {
+			// check property template
+			if (templateProps) {
+				return templateProps->Get(name);
+			}
+
+			return nullptr;
+		}
+	}
+
+	return (*it).second;
+}
+
+DirectPropertyMap PropertyTable::GetUnparsedProperties() const {
+	DirectPropertyMap result;
+
+	// Loop through all the lazy properties (which is all the properties)
+	for (const LazyPropertyMap::value_type &element : lazyProps) {
+
+		// Skip parsed properties
+		if (props.end() != props.find(element.first)) continue;
+
+		// Read the element's value.
+		// Wrap the naked pointer (since the call site is required to acquire ownership)
+		// std::unique_ptr from C++11 would be preferred both as a wrapper and a return value.
+		Property *prop = ReadTypedProperty(element.second);
+
+		// Element could not be read. Skip it.
+		if (!prop) continue;
+
+		// Add to result
+		result[element.first] = prop;
+	}
+
+	return result;
+}
+
+} // namespace FBXDocParser

+ 222 - 0
modules/fbx/fbx_parser/FBXProperties.h

@@ -0,0 +1,222 @@
+/*************************************************************************/
+/*  FBXProperties.h                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXProperties.h
+ *  @brief FBX dynamic properties
+ */
+#ifndef FBX_PROPERTIES_H
+#define FBX_PROPERTIES_H
+
+#include "FBXParser.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace FBXDocParser {
+
+// Forward declarations
+class Element;
+
+/** Represents a dynamic property. Type info added by deriving classes,
+ *  see #TypedProperty.
+ Example:
+ @verbatim
+   P: "ShininessExponent", "double", "Number", "",0.5
+ @endvebatim
+*/
+class Property {
+protected:
+	Property();
+
+public:
+	virtual ~Property();
+
+public:
+	template <typename T>
+	const T *As() const {
+		return dynamic_cast<const T *>(this);
+	}
+};
+
+template <typename T>
+class TypedProperty : public Property {
+public:
+	explicit TypedProperty(const T &value) :
+			value(value) {
+		// empty
+	}
+
+	const T &Value() const {
+		return value;
+	}
+
+private:
+	T value;
+};
+
+#define new_Property new Property
+typedef Property *PropertyPtr;
+typedef std::map<std::string, PropertyPtr> DirectPropertyMap;
+typedef std::map<std::string, PropertyPtr> PropertyMap;
+typedef std::map<std::string, ElementPtr> LazyPropertyMap;
+
+/**
+ *  Represents a property table as can be found in the newer FBX files (Properties60, Properties70)
+ */
+class PropertyTable {
+public:
+	// in-memory property table with no source element
+	PropertyTable();
+	PropertyTable(const ElementPtr element, const PropertyTable *templateProps);
+	~PropertyTable();
+
+	PropertyPtr Get(const std::string &name) const;
+
+	// PropertyTable's need not be coupled with FBX elements so this can be NULL
+	const ElementPtr GetElement() const {
+		return element;
+	}
+
+	const PropertyMap &GetProperties() const {
+		return props;
+	}
+
+	const LazyPropertyMap &GetLazyProperties() const {
+		return lazyProps;
+	}
+
+	const PropertyTable *TemplateProps() const {
+		return templateProps;
+	}
+
+	DirectPropertyMap GetUnparsedProperties() const;
+
+private:
+	LazyPropertyMap lazyProps;
+	mutable PropertyMap props;
+	const PropertyTable *templateProps = nullptr;
+	const ElementPtr element = nullptr;
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline T PropertyGet(const PropertyTable *in, const std::string &name, const T &defaultValue) {
+	PropertyPtr prop = in->Get(name);
+	if (nullptr == prop) {
+		return defaultValue;
+	}
+
+	// strong typing, no need to be lenient
+	const TypedProperty<T> *const tprop = prop->As<TypedProperty<T> >();
+	if (nullptr == tprop) {
+		return defaultValue;
+	}
+
+	return tprop->Value();
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline T PropertyGet(const PropertyTable *in, const std::string &name, bool &result, bool useTemplate = false) {
+	PropertyPtr prop = in->Get(name);
+	if (nullptr == prop) {
+		if (!useTemplate) {
+			result = false;
+			return T();
+		}
+		const PropertyTable *templ = in->TemplateProps();
+		if (nullptr == templ) {
+			result = false;
+			return T();
+		}
+		prop = templ->Get(name);
+		if (nullptr == prop) {
+			result = false;
+			return T();
+		}
+	}
+
+	// strong typing, no need to be lenient
+	const TypedProperty<T> *const tprop = prop->As<TypedProperty<T> >();
+	if (nullptr == tprop) {
+		result = false;
+		return T();
+	}
+
+	result = true;
+	return tprop->Value();
+}
+
+} // namespace FBXDocParser
+
+#endif // FBX_PROPERTIES_H

+ 249 - 0
modules/fbx/fbx_parser/FBXTokenizer.cpp

@@ -0,0 +1,249 @@
+/*************************************************************************/
+/*  FBXTokenizer.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXTokenizer.cpp
+ *  @brief Implementation of the FBX broadphase lexer
+ */
+
+// tab width for logging columns
+#define ASSIMP_FBX_TAB_WIDTH 4
+
+#include "FBXTokenizer.h"
+#include "core/print_string.h"
+
+namespace FBXDocParser {
+
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column) :
+		sbegin(p_sbegin),
+		send(p_send),
+		type(p_type),
+		line(p_line),
+		column(p_column) {
+#ifdef DEBUG_ENABLED
+	contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+Token::~Token() {
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string &message, unsigned int line, unsigned int column) {
+	print_error("[FBX-Tokenize]" + String(message.c_str()) + " " + itos(line) + ":" + itos(column));
+}
+
+// process a potential data token up to 'cur', adding it to 'output_tokens'.
+// ------------------------------------------------------------------------------------------------
+void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *&end,
+		unsigned int line,
+		unsigned int column,
+		TokenType type = TokenType_DATA,
+		bool must_have_token = false) {
+	if (start && end) {
+		// sanity check:
+		// tokens should have no whitespace outside quoted text and [start,end] should
+		// properly delimit the valid range.
+		bool in_double_quotes = false;
+		for (const char *c = start; c != end + 1; ++c) {
+			if (*c == '\"') {
+				in_double_quotes = !in_double_quotes;
+			}
+
+			if (!in_double_quotes && IsSpaceOrNewLine(*c)) {
+				TokenizeError("unexpected whitespace in token", line, column);
+			}
+		}
+
+		if (in_double_quotes) {
+			TokenizeError("non-terminated double quotes", line, column);
+		}
+
+		output_tokens.push_back(new_Token(start, end + 1, type, line, column));
+	} else if (must_have_token) {
+		TokenizeError("unexpected character, expected data token", line, column);
+	}
+
+	start = end = nullptr;
+}
+
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+void Tokenize(TokenList &output_tokens, const char *input) {
+	// line and column numbers numbers are one-based
+	unsigned int line = 1;
+	unsigned int column = 1;
+
+	bool comment = false;
+	bool in_double_quotes = false;
+	bool pending_data_token = false;
+
+	const char *token_begin = nullptr, *token_end = nullptr;
+	for (const char *cur = input; *cur; column += (*cur == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1), ++cur) {
+		const char c = *cur;
+
+		if (IsLineEnd(c)) {
+			comment = false;
+
+			column = 0;
+			++line;
+		}
+
+		if (comment) {
+			continue;
+		}
+
+		if (in_double_quotes) {
+			if (c == '\"') {
+				in_double_quotes = false;
+				token_end = cur;
+
+				ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+				pending_data_token = false;
+			}
+			continue;
+		}
+
+		switch (c) {
+			case '\"':
+				if (token_begin) {
+					TokenizeError("unexpected double-quote", line, column);
+				}
+				token_begin = cur;
+				in_double_quotes = true;
+				continue;
+
+			case ';':
+				ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+				comment = true;
+				continue;
+
+			case '{':
+				ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+				output_tokens.push_back(new_Token(cur, cur + 1, TokenType_OPEN_BRACKET, line, column));
+				continue;
+
+			case '}':
+				ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+				output_tokens.push_back(new_Token(cur, cur + 1, TokenType_CLOSE_BRACKET, line, column));
+				continue;
+
+			case ',':
+				if (pending_data_token) {
+					ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_DATA, true);
+				}
+				output_tokens.push_back(new_Token(cur, cur + 1, TokenType_COMMA, line, column));
+				continue;
+
+			case ':':
+				if (pending_data_token) {
+					ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_KEY, true);
+				} else {
+					TokenizeError("unexpected colon", line, column);
+				}
+				continue;
+		}
+
+		if (IsSpaceOrNewLine(c)) {
+
+			if (token_begin) {
+				// peek ahead and check if the next token is a colon in which
+				// case this counts as KEY token.
+				TokenType type = TokenType_DATA;
+				for (const char *peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) {
+					if (*peek == ':') {
+						type = TokenType_KEY;
+						cur = peek;
+						break;
+					}
+				}
+
+				ProcessDataToken(output_tokens, token_begin, token_end, line, column, type);
+			}
+
+			pending_data_token = false;
+		} else {
+			token_end = cur;
+			if (!token_begin) {
+				token_begin = cur;
+			}
+
+			pending_data_token = true;
+		}
+	}
+}
+
+} // namespace FBXDocParser

+ 204 - 0
modules/fbx/fbx_parser/FBXTokenizer.h

@@ -0,0 +1,204 @@
+/*************************************************************************/
+/*  FBXTokenizer.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXTokenizer.h
+ *  @brief FBX lexer
+ */
+#ifndef FBX_TOKENIZER_H
+#define FBX_TOKENIZER_H
+
+#include "FBXParseTools.h"
+#include "core/ustring.h"
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace FBXDocParser {
+/** Rough classification for text FBX tokens used for constructing the
+ *  basic scope hierarchy. */
+enum TokenType {
+	// {
+	TokenType_OPEN_BRACKET = 0,
+
+	// }
+	TokenType_CLOSE_BRACKET,
+
+	// '"blablubb"', '2', '*14' - very general token class,
+	// further processing happens at a later stage.
+	TokenType_DATA,
+
+	//
+	TokenType_BINARY_DATA,
+
+	// ,
+	TokenType_COMMA,
+
+	// blubb:
+	TokenType_KEY
+};
+
+/** Represents a single token in a FBX file. Tokens are
+ *  classified by the #TokenType enumerated types.
+ *
+ *  Offers iterator protocol. Tokens are immutable. */
+class Token {
+private:
+	static const unsigned int BINARY_MARKER = static_cast<unsigned int>(-1);
+
+public:
+	/** construct a textual token */
+	Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column);
+
+	/** construct a binary token */
+	Token(const char *p_sbegin, const char *p_send, TokenType p_type, size_t p_offset);
+	~Token();
+
+public:
+	std::string StringContents() const {
+		return std::string(begin(), end());
+	}
+
+	bool IsBinary() const {
+		return column == BINARY_MARKER;
+	}
+
+	const char *begin() const {
+		return sbegin;
+	}
+
+	const char *end() const {
+		return send;
+	}
+
+	TokenType Type() const {
+		return type;
+	}
+
+	size_t Offset() const {
+		return offset;
+	}
+
+	unsigned int Line() const {
+		return static_cast<unsigned int>(line);
+	}
+
+	unsigned int Column() const {
+		return column;
+	}
+
+private:
+#ifdef DEBUG_ENABLED
+	// full string copy for the sole purpose that it nicely appears
+	// in msvc's debugger window.
+	std::string contents;
+#endif
+
+	const char *sbegin = nullptr;
+	const char *send = nullptr;
+	const TokenType type;
+
+	union {
+		size_t line;
+		size_t offset;
+	};
+	const unsigned int column = 0;
+};
+
+// Fixed leak by using shared_ptr for tokens
+typedef Token *TokenPtr;
+typedef std::vector<TokenPtr> TokenList;
+
+#define new_Token new Token
+
+/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
+ *
+ *  Skips over comments and generates line and column numbers.
+ *
+ * @param output_tokens Receives a list of all tokens in the input data.
+ * @param input_buffer Textual input buffer to be processed, 0-terminated.
+ * @print_error if something goes wrong */
+void Tokenize(TokenList &output_tokens, const char *input);
+
+/** Tokenizer function for binary FBX files.
+ *
+ *  Emits a token list suitable for direct parsing.
+ *
+ * @param output_tokens Receives a list of all tokens in the input data.
+ * @param input_buffer Binary input buffer to be processed.
+ * @param length Length of input buffer, in bytes. There is no 0-terminal.
+ * @print_error if something goes wrong */
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length);
+
+} // namespace FBXDocParser
+
+#endif // FBX_TOKENIZER_H

+ 221 - 0
modules/fbx/fbx_parser/FBXUtil.cpp

@@ -0,0 +1,221 @@
+/*************************************************************************/
+/*  FBXUtil.cpp                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+/*
+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  FBXUtil.cpp
+ *  @brief Implementation of internal FBX utility functions
+ */
+
+#include "FBXUtil.h"
+#include "FBXTokenizer.h"
+#include <cstring>
+#include <string>
+
+namespace FBXDocParser {
+namespace Util {
+
+// ------------------------------------------------------------------------------------------------
+const char *TokenTypeString(TokenType t) {
+	switch (t) {
+		case TokenType_OPEN_BRACKET:
+			return "TOK_OPEN_BRACKET";
+
+		case TokenType_CLOSE_BRACKET:
+			return "TOK_CLOSE_BRACKET";
+
+		case TokenType_DATA:
+			return "TOK_DATA";
+
+		case TokenType_COMMA:
+			return "TOK_COMMA";
+
+		case TokenType_KEY:
+			return "TOK_KEY";
+
+		case TokenType_BINARY_DATA:
+			return "TOK_BINARY_DATA";
+	}
+
+	//ai_assert(false);
+	return "";
+}
+
+// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
+static const uint8_t base64DecodeTable[128] = {
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255,
+	255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+	255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255
+};
+
+uint8_t DecodeBase64(char ch) {
+	const auto idx = static_cast<uint8_t>(ch);
+	if (idx > 127)
+		return 255;
+	return base64DecodeTable[idx];
+}
+
+size_t ComputeDecodedSizeBase64(const char *in, size_t inLength) {
+	if (inLength < 2) {
+		return 0;
+	}
+	const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '=');
+	const size_t full_length = (inLength * 3) >> 2; // div by 4
+	if (full_length < equals) {
+		return 0;
+	}
+	return full_length - equals;
+}
+
+size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength) {
+	if (maxOutLength == 0 || inLength < 2) {
+		return 0;
+	}
+	const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
+	size_t dst_offset = 0;
+	int val = 0, valb = -8;
+	for (size_t src_offset = 0; src_offset < realLength; ++src_offset) {
+		const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
+		if (table_value == 255) {
+			return 0;
+		}
+		val = (val << 6) + table_value;
+		valb += 6;
+		if (valb >= 0) {
+			out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF);
+			valb -= 8;
+			val &= 0xFFF;
+		}
+	}
+	return dst_offset;
+}
+
+static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+char EncodeBase64(char byte) {
+	return to_base64_string[(size_t)byte];
+}
+
+/** Encodes a block of 4 bytes to base64 encoding
+*
+*  @param bytes Bytes to encode.
+*  @param out_string String to write encoded values to.
+*  @param string_pos Position in out_string.*/
+void EncodeByteBlock(const char *bytes, std::string &out_string, size_t string_pos) {
+	char b0 = (bytes[0] & 0xFC) >> 2;
+	char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4);
+	char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6);
+	char b3 = (bytes[2] & 0x3F);
+
+	out_string[string_pos + 0] = EncodeBase64(b0);
+	out_string[string_pos + 1] = EncodeBase64(b1);
+	out_string[string_pos + 2] = EncodeBase64(b2);
+	out_string[string_pos + 3] = EncodeBase64(b3);
+}
+
+std::string EncodeBase64(const char *data, size_t length) {
+	// calculate extra bytes needed to get a multiple of 3
+	size_t extraBytes = 3 - length % 3;
+
+	// number of base64 bytes
+	size_t encodedBytes = 4 * (length + extraBytes) / 3;
+
+	std::string encoded_string(encodedBytes, '=');
+
+	// read blocks of 3 bytes
+	for (size_t ib3 = 0; ib3 < length / 3; ib3++) {
+		const size_t iByte = ib3 * 3;
+		const size_t iEncodedByte = ib3 * 4;
+		const char *currData = &data[iByte];
+
+		EncodeByteBlock(currData, encoded_string, iEncodedByte);
+	}
+
+	// if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed)
+	if (extraBytes > 0) {
+		char finalBytes[4] = { 0, 0, 0, 0 };
+		memcpy(&finalBytes[0], &data[length - length % 3], length % 3);
+
+		const size_t iEncodedByte = encodedBytes - 4;
+		EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte);
+
+		// add '=' at the end
+		for (size_t i = 0; i < 4 * extraBytes / 3; i++)
+			encoded_string[encodedBytes - i - 1] = '=';
+	}
+	return encoded_string;
+}
+
+} // namespace Util
+} // namespace FBXDocParser

+ 45 - 51
thirdparty/assimp/code/FBX/FBXUtil.h → modules/fbx/fbx_parser/FBXUtil.h

@@ -1,3 +1,33 @@
+/*************************************************************************/
+/*  FBXUtil.h                                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
 /*
 /*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
@@ -43,61 +73,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file  FBXUtil.h
 /** @file  FBXUtil.h
  *  @brief FBX utility functions for internal use
  *  @brief FBX utility functions for internal use
  */
  */
-#ifndef INCLUDED_AI_FBX_UTIL_H
-#define INCLUDED_AI_FBX_UTIL_H
+#ifndef FBX_UTIL_H
+#define FBX_UTIL_H
 
 
-#include "FBXCompileConfig.h"
 #include "FBXTokenizer.h"
 #include "FBXTokenizer.h"
 #include <stdint.h>
 #include <stdint.h>
 
 
-namespace Assimp {
-namespace FBX {
-
+namespace FBXDocParser {
 
 
 namespace Util {
 namespace Util {
 
 
-
 /** helper for std::for_each to delete all heap-allocated items in a container */
 /** helper for std::for_each to delete all heap-allocated items in a container */
-template<typename T>
-struct delete_fun
-{
-    void operator()(const volatile T* del) {
-        delete del;
-    }
+template <typename T>
+struct delete_fun {
+	void operator()(const volatile T *del) {
+		delete del;
+	}
 };
 };
 
 
 /** Get a string representation for a #TokenType. */
 /** Get a string representation for a #TokenType. */
-const char* TokenTypeString(TokenType t);
-
-
-
-/** Format log/error messages using a given offset in the source binary file
- *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
- *  @param line Line index, 1-based
- *  @param column Column index, 1-based
- *  @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/
-std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset);
-
-
-/** Format log/error messages using a given line location in the source file.
- *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
- *  @param line Line index, 1-based
- *  @param column Column index, 1-based
- *  @return A string of the following format: {prefix} (line {line}, col {column}) {text}*/
-std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column);
-
-
-/** Format log/error messages using a given cursor token.
- *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
- *  @param tok Token where parsing/processing stopped
- *  @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/
-std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok);
+const char *TokenTypeString(TokenType t);
 
 
 /** Decode a single Base64-encoded character.
 /** Decode a single Base64-encoded character.
 *
 *
@@ -110,7 +105,7 @@ uint8_t DecodeBase64(char ch);
 *  @param in Characters to decode.
 *  @param in Characters to decode.
 *  @param inLength Number of characters to decode.
 *  @param inLength Number of characters to decode.
 *  @return size of the decoded data (number of bytes)*/
 *  @return size of the decoded data (number of bytes)*/
-size_t ComputeDecodedSizeBase64(const char* in, size_t inLength);
+size_t ComputeDecodedSizeBase64(const char *in, size_t inLength);
 
 
 /** Decode a Base64-encoded string
 /** Decode a Base64-encoded string
 *
 *
@@ -119,7 +114,7 @@ size_t ComputeDecodedSizeBase64(const char* in, size_t inLength);
 *  @param out Pointer where we will store the decoded data.
 *  @param out Pointer where we will store the decoded data.
 *  @param maxOutLength Size of output buffer.
 *  @param maxOutLength Size of output buffer.
 *  @return size of the decoded data (number of bytes)*/
 *  @return size of the decoded data (number of bytes)*/
-size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOutLength);
+size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength);
 
 
 char EncodeBase64(char byte);
 char EncodeBase64(char byte);
 
 
@@ -128,10 +123,9 @@ char EncodeBase64(char byte);
 *  @param data Binary data to encode.
 *  @param data Binary data to encode.
 *  @param inLength Number of bytes to encode.
 *  @param inLength Number of bytes to encode.
 *  @return base64-encoded string*/
 *  @return base64-encoded string*/
-std::string EncodeBase64(const char* data, size_t length);
+std::string EncodeBase64(const char *data, size_t length);
 
 
-}
-}
-}
+} // namespace Util
+} // namespace FBXDocParser
 
 
-#endif // ! INCLUDED_AI_FBX_UTIL_H
+#endif // FBX_UTIL_H

+ 2 - 4
thirdparty/assimp/LICENSE → modules/fbx/fbx_parser/LICENSE

@@ -1,6 +1,6 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 
 
-Copyright (c) 2006-2016, assimp team
+Copyright (c) 2006-2020, assimp team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -33,8 +33,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
-
-
 ******************************************************************************
 ******************************************************************************
 
 
 AN EXCEPTION applies to all files in the ./test/models-nonbsd folder.
 AN EXCEPTION applies to all files in the ./test/models-nonbsd folder.
@@ -43,7 +41,7 @@ on the internet. They are - unless otherwise stated - copyright of
 their respective creators, which may impose additional requirements
 their respective creators, which may impose additional requirements
 on the use of their work. For any of these models, see
 on the use of their work. For any of these models, see
 <model-name>.source.txt for more legal information. Contact us if you
 <model-name>.source.txt for more legal information. Contact us if you
-are a copyright holder and believe that we credited you inproperly or
+are a copyright holder and believe that we credited you improperly or
 if you don't want your files to appear in the repository.
 if you don't want your files to appear in the repository.
 
 
 
 

+ 197 - 0
modules/fbx/readme.md

@@ -0,0 +1,197 @@
+# Open Source FBX Specification for the Importer
+
+The goal of this document is to make everything in FBX clearly stated, any errors will be corrected over time this
+is a first draft.
+
+## fbx parser - originally from assimp
+
+- Folder: /modules/fbx/fbx_parser
+- Upstream: assimp
+- Original Version: git (308db73d0b3c2d1870cd3e465eaa283692a4cf23, 2019)
+- License: BSD-3-Clause
+
+This can never be updated from upstream, we have heavily modified the parser to provide memory safety and add some
+functionality. If anything we should give this parser back to assimp at some point as it has a lot of new features.
+
+# Updating assimp fbx parser
+
+Don't. it's not possible the code is rewritten in many areas to remove thirdparty deps and various bugs are fixed.
+
+Many days were put into rewriting the parser to use safe code and safe memory accessors.
+
+# File Headers
+
+FBX Binaries start with the header "Kaydara FBX Binary"
+
+FBX ASCII documents contain a larger header, sometimes with copyright information for a file.
+
+Detecting these is pretty simple.
+
+# What is an OP link?
+It's an object to property link. It lists the properties for that object in some cases. Source and destination based by
+ID.
+
+# What is a OO link?
+Its an object to object link, it contains the ID source and destination ID.
+
+# FBX Node connections
+
+Nodes in FBX are connected using OO links, This means Object to Object.
+
+FBX has a single other kind of link which is Object Property, this is used for Object to Property Links, this can be
+ extra attributes, defaults, or even some simple settings.
+
+# Bones / Joints / Locators
+
+Bones in FBX are nodes, they initially have the Model:: Type, then have links to SubDeformer the sub deformer
+is part of the skin there is also an explicit Skin link, which then links to the geometry using OO links in the
+document.
+
+# Rotation Order in FBX compared to Godot
+
+**Godot uses the rotation order:** YXZ
+
+**FBX has dynamic rotation order to prevent gimbal lock with complex animations**
+
+```cpp
+enum RotOrder {
+	RotOrder_EulerXYZ = 0
+	RotOrder_EulerXZY,
+	RotOrder_EulerYZX,
+	RotOrder_EulerYXZ,
+	RotOrder_EulerZXY,
+	RotOrder_EulerZYX,
+	RotOrder_SphericXYZ // nobody uses this - as far as we can tell
+};
+```
+
+
+# Pivot transforms
+
+### Pivot description:
+- Maya and 3DS max consider everything to be in node space (bones joints, skins, lights, cameras, etc)
+- Everything is a node, this means essentially nodes are auto or variants
+- They are local to the node in the tree.
+- They are used to calculate where a node is in space
+```c++
+// For a better reference you can check editor_scene_importer_fbx.h
+// references: GenFBXTransform / read the data in
+// references: ComputePivotTransform / run the calculation
+// This is the local pivot transform for the node, not the global transforms
+Transform ComputePivotTransform(
+		Transform chain[TransformationComp_MAXIMUM],
+		Transform &geometric_transform) {
+
+	// Maya pivots
+	Transform T = chain[TransformationComp_Translation];
+	Transform Roff = chain[TransformationComp_RotationOffset];
+	Transform Rp = chain[TransformationComp_RotationPivot];
+	Transform Rpre = chain[TransformationComp_PreRotation];
+	Transform R = chain[TransformationComp_Rotation];
+	Transform Rpost = chain[TransformationComp_PostRotation];
+	Transform Soff = chain[TransformationComp_ScalingOffset];
+	Transform Sp = chain[TransformationComp_ScalingPivot];
+	Transform S = chain[TransformationComp_Scaling];
+
+	// 3DS Max Pivots
+	Transform OT = chain[TransformationComp_GeometricTranslation];
+	Transform OR = chain[TransformationComp_GeometricRotation];
+	Transform OS = chain[TransformationComp_GeometricScaling];
+
+	// Calculate 3DS max pivot transform - use geometric space (e.g doesn't effect children nodes only the current node)
+	geometric_transform = OT * OR * OS;
+	// Calculate standard maya pivots
+	return T * Roff * Rp * Rpre * R * Rpost.inverse() * Rp.inverse() * Soff * Sp * S * Sp.inverse();
+}
+```
+
+# Transform inheritance for FBX Nodes
+
+The goal of below is to explain why they implement this in the first place.
+
+The use case is to make nodes have an option to override their local scaling or to make scaling influenced by orientation, which i would imagine would be useful for when you need to rotate a node and the child to scale based on the orientation rather than setting on the rotation matrix planes.
+```cpp
+// not modified the formatting here since this code must remain clear
+enum TransformInheritance {
+	Transform_RrSs = 0,
+	// Parent Rotation * Local Rotation * Parent Scale * Local Scale  -- Parent Rotation Offset * Parent ScalingOffset (Local scaling is offset by rotation of parent node)
+	Transform_RSrs = 1, // Parent Rotation * Parent Scale * Local Rotation * Local Scale -- Parent * Local (normal mode)
+	Transform_Rrs = 2, // Parent Rotation * Local Rotation * Local Scale -- Node transform scale is the only relevant component
+	TransformInheritance_MAX // end-of-enum sentinel
+};
+
+enum TransformInheritance {
+	Transform_RrSs = 0,
+	// Local scaling is offset by rotation of parent node
+	Transform_RSrs = 1,
+	// Parent * Local (normal mode)
+	Transform_Rrs = 2,
+	// Node transform scale is the only relevant component
+	TransformInheritance_MAX // end-of-enum sentinel
+};
+```
+
+# Axis in FBX
+
+Godot has one format for the declared axis
+
+AXIS X, AXIS Y, -AXIS Z
+
+FBX supports any format you can think of. As it has to support Maya and 3DS Max.
+
+#### FBX File Header
+```json
+GlobalSettings:  {
+	Version: 1000
+	Properties70:  {
+		P: "UpAxis", "int", "Integer", "",1
+		P: "UpAxisSign", "int", "Integer", "",1
+		P: "FrontAxis", "int", "Integer", "",2
+		P: "FrontAxisSign", "int", "Integer", "",1
+		P: "CoordAxis", "int", "Integer", "",0
+		P: "CoordAxisSign", "int", "Integer", "",1
+		P: "OriginalUpAxis", "int", "Integer", "",1
+		P: "OriginalUpAxisSign", "int", "Integer", "",1
+		P: "UnitScaleFactor", "double", "Number", "",1
+		P: "OriginalUnitScaleFactor", "double", "Number", "",1
+		P: "AmbientColor", "ColorRGB", "Color", "",0,0,0
+		P: "DefaultCamera", "KString", "", "", "Producer Perspective"
+		P: "TimeMode", "enum", "", "",6
+		P: "TimeProtocol", "enum", "", "",2
+		P: "SnapOnFrameMode", "enum", "", "",0
+		P: "TimeSpanStart", "KTime", "Time", "",0
+		P: "TimeSpanStop", "KTime", "Time", "",92372316000
+		P: "CustomFrameRate", "double", "Number", "",-1
+		P: "TimeMarker", "Compound", "", ""
+		P: "CurrentTimeMarker", "int", "Integer", "",-1
+	}
+}
+```
+
+#### FBX FILE declares axis dynamically using FBX header
+Coord is X
+Up is Y
+Front is Z
+
+#### GODOT - constant reference point
+Coord is X positive,
+Y is up positive,
+Front is -Z negative
+
+### Explaining MeshGeometry indexing
+
+Reference type declared:
+- Direct (directly related to the mapping information type)
+- IndexToDirect (Map with key value, meaning depends on the MappingInformationType)
+
+ControlPoint is a vertex
+* None The mapping is undetermined.
+* ByVertex There will be one mapping coordinate for each surface control point/vertex.
+    * If you have direct reference type vertices [x]
+    * If you have IndexToDirect reference type the UV
+* ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex)
+* ByPolygon There can be only one mapping coordinate for the whole polygon.
+    * One mapping per polygon polygon x has this normal x
+    * For each vertex of the polygon then set the normal to x
+* ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id)
+* AllSame There can be only one mapping coordinate for the whole surface.

+ 7 - 7
modules/assimp/register_types.cpp → modules/fbx/register_types.cpp

@@ -31,23 +31,23 @@
 #include "register_types.h"
 #include "register_types.h"
 
 
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
-#include "editor_scene_importer_assimp.h"
+#include "editor_scene_importer_fbx.h"
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 static void _editor_init() {
 static void _editor_init() {
-	Ref<EditorSceneImporterAssimp> import_assimp;
-	import_assimp.instance();
-	ResourceImporterScene::get_singleton()->add_importer(import_assimp);
+	Ref<EditorSceneImporterFBX> import_fbx;
+	import_fbx.instance();
+	ResourceImporterScene::get_singleton()->add_importer(import_fbx);
 }
 }
 #endif
 #endif
 
 
-void register_assimp_types() {
+void register_fbx_types() {
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	ClassDB::APIType prev_api = ClassDB::get_current_api();
 	ClassDB::APIType prev_api = ClassDB::get_current_api();
 	ClassDB::set_current_api(ClassDB::API_EDITOR);
 	ClassDB::set_current_api(ClassDB::API_EDITOR);
 
 
-	ClassDB::register_class<EditorSceneImporterAssimp>();
+	ClassDB::register_class<EditorSceneImporterFBX>();
 
 
 	ClassDB::set_current_api(prev_api);
 	ClassDB::set_current_api(prev_api);
 
 
@@ -55,5 +55,5 @@ void register_assimp_types() {
 #endif
 #endif
 }
 }
 
 
-void unregister_assimp_types() {
+void unregister_fbx_types() {
 }
 }

+ 5 - 5
modules/assimp/register_types.h → modules/fbx/register_types.h

@@ -28,10 +28,10 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#ifndef ASSIMP_REGISTER_TYPES_H
-#define ASSIMP_REGISTER_TYPES_H
+#ifndef FBX_REGISTER_TYPES_H
+#define FBX_REGISTER_TYPES_H
 
 
-void register_assimp_types();
-void unregister_assimp_types();
+void register_fbx_types();
+void unregister_fbx_types();
 
 
-#endif // ASSIMP_REGISTER_TYPES_H
+#endif // FBX_REGISTER_TYPES_H

+ 152 - 0
modules/fbx/tools/import_utils.cpp

@@ -0,0 +1,152 @@
+/*************************************************************************/
+/*  import_utils.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "import_utils.h"
+
+Vector3 ImportUtils::deg2rad(const Vector3 &p_rotation) {
+	return p_rotation / 180.0 * Math_PI;
+}
+
+Vector3 ImportUtils::rad2deg(const Vector3 &p_rotation) {
+	return p_rotation / Math_PI * 180.0;
+}
+
+Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) {
+	Basis ret;
+
+	// FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot
+	// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
+	switch (mode) {
+		case FBXDocParser::Model::RotOrder_EulerXYZ:
+			ret.set_euler_zyx(p_rotation);
+			break;
+
+		case FBXDocParser::Model::RotOrder_EulerXZY:
+			ret.set_euler_yzx(p_rotation);
+			break;
+
+		case FBXDocParser::Model::RotOrder_EulerYZX:
+			ret.set_euler_xzy(p_rotation);
+			break;
+
+		case FBXDocParser::Model::RotOrder_EulerYXZ:
+			ret.set_euler_zxy(p_rotation);
+			break;
+
+		case FBXDocParser::Model::RotOrder_EulerZXY:
+			ret.set_euler_yxz(p_rotation);
+			break;
+
+		case FBXDocParser::Model::RotOrder_EulerZYX:
+			ret.set_euler_xyz(p_rotation);
+			break;
+
+		case FBXDocParser::Model::RotOrder_SphericXYZ:
+			// TODO do this.
+			break;
+
+		default:
+			// If you land here, Please integrate all enums.
+			CRASH_NOW_MSG("This is not unreachable.");
+	}
+
+	return ret;
+}
+
+Quat ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) {
+	return ImportUtils::EulerToBasis(mode, p_rotation);
+}
+
+Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation) {
+
+	// FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot
+	// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
+	switch (mode) {
+		case FBXDocParser::Model::RotOrder_EulerXYZ:
+			return p_rotation.get_euler_zyx();
+
+		case FBXDocParser::Model::RotOrder_EulerXZY:
+			return p_rotation.get_euler_yzx();
+
+		case FBXDocParser::Model::RotOrder_EulerYZX:
+			return p_rotation.get_euler_xzy();
+
+		case FBXDocParser::Model::RotOrder_EulerYXZ:
+			return p_rotation.get_euler_zxy();
+
+		case FBXDocParser::Model::RotOrder_EulerZXY:
+			return p_rotation.get_euler_yxz();
+
+		case FBXDocParser::Model::RotOrder_EulerZYX:
+			return p_rotation.get_euler_xyz();
+
+		case FBXDocParser::Model::RotOrder_SphericXYZ:
+			// TODO
+			return Vector3();
+
+		default:
+			// If you land here, Please integrate all enums.
+			CRASH_NOW_MSG("This is not unreachable.");
+			return Vector3();
+	}
+}
+
+Vector3 ImportUtils::QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quat &p_rotation) {
+	return BasisToEuler(mode, p_rotation);
+}
+
+Transform get_unscaled_transform(const Transform &p_initial, real_t p_scale) {
+	Transform unscaled = Transform(p_initial.basis, p_initial.origin * p_scale);
+	ERR_FAIL_COND_V_MSG(unscaled.basis.determinant() == 0, Transform(), "det is zero unscaled?");
+	return unscaled;
+}
+
+Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices) {
+	ERR_FAIL_COND_V_MSG(p_vertices.size() < 3, Vector3(0, 0, 0), "At least 3 vertices are necesary");
+	// Using long double to make sure that normal is computed for even really tiny objects.
+	typedef long double ldouble;
+	ldouble x = 0.0;
+	ldouble y = 0.0;
+	ldouble z = 0.0;
+	for (size_t i = 0; i < p_vertices.size(); i += 1) {
+		const Vector3 current = p_vertices[i];
+		const Vector3 next = p_vertices[(i + 1) % p_vertices.size()];
+		x += (ldouble(current.y) - ldouble(next.y)) * (ldouble(current.z) + ldouble(next.z));
+		y += (ldouble(current.z) - ldouble(next.z)) * (ldouble(current.x) + ldouble(next.x));
+		z += (ldouble(current.x) - ldouble(next.x)) * (ldouble(current.y) + ldouble(next.y));
+	}
+	const ldouble l2 = x * x + y * y + z * z;
+	if (l2 == 0.0) {
+		return (p_vertices[0] - p_vertices[1]).normalized().cross((p_vertices[0] - p_vertices[2]).normalized()).normalized();
+	} else {
+		const double l = Math::sqrt(double(l2));
+		return Vector3(x / l, y / l, z / l);
+	}
+}

+ 386 - 0
modules/fbx/tools/import_utils.h

@@ -0,0 +1,386 @@
+/*************************************************************************/
+/*  import_utils.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef IMPORT_UTILS_FBX_IMPORTER_H
+#define IMPORT_UTILS_FBX_IMPORTER_H
+
+#include "core/io/image_loader.h"
+
+#include "data/import_state.h"
+#include "fbx_parser/FBXDocument.h"
+
+#include <string>
+
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
+
+/**
+ * Import Utils
+ * Conversion tools / glue code to convert from FBX to Godot
+*/
+class ImportUtils {
+public:
+	///	Convert a vector from degrees to radians.
+	static Vector3 deg2rad(const Vector3 &p_rotation);
+
+	///	Convert a vector from radians to degrees.
+	static Vector3 rad2deg(const Vector3 &p_rotation);
+
+	/// Converts rotation order vector (in rad) to quaternion.
+	static Basis EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation);
+
+	/// Converts rotation order vector (in rad) to quaternion.
+	static Quat EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation);
+
+	/// Converts basis into rotation order vector (in rad).
+	static Vector3 BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation);
+
+	/// Converts quaternion into rotation order vector (in rad).
+	static Vector3 QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quat &p_rotation);
+
+	static void debug_xform(String name, const Transform &t) {
+		print_verbose(name + " " + t.origin + " rotation: " + (t.basis.get_euler() * (180 / Math_PI)));
+	}
+
+	static String FBXNodeToName(const std::string &name) {
+		// strip Model:: prefix, avoiding ambiguities (i.e. don't strip if
+		// this causes ambiguities, well possible between empty identifiers,
+		// such as "Model::" and ""). Make sure the behaviour is consistent
+		// across multiple calls to FixNodeName().
+
+		// We must remove this from the name
+		// Some bones have this
+		// SubDeformer::
+		// Meshes, Joints have this, some other IK elements too.
+		// Model::
+
+		String node_name = String(name.c_str());
+
+		if (node_name.substr(0, 7) == "Model::") {
+			node_name = node_name.substr(7, node_name.length() - 7);
+			return node_name.replace(":", "");
+		}
+
+		if (node_name.substr(0, 13) == "SubDeformer::") {
+			node_name = node_name.substr(13, node_name.length() - 13);
+			return node_name.replace(":", "");
+		}
+
+		if (node_name.substr(0, 11) == "AnimStack::") {
+			node_name = node_name.substr(11, node_name.length() - 11);
+			return node_name.replace(":", "");
+		}
+
+		if (node_name.substr(0, 15) == "AnimCurveNode::") {
+			node_name = node_name.substr(15, node_name.length() - 15);
+			return node_name.replace(":", "");
+		}
+
+		if (node_name.substr(0, 11) == "AnimCurve::") {
+			node_name = node_name.substr(11, node_name.length() - 11);
+			return node_name.replace(":", "");
+		}
+
+		if (node_name.substr(0, 10) == "Geometry::") {
+			node_name = node_name.substr(10, node_name.length() - 10);
+			return node_name.replace(":", "");
+		}
+
+		if (node_name.substr(0, 10) == "Material::") {
+			node_name = node_name.substr(10, node_name.length() - 10);
+			return node_name.replace(":", "");
+		}
+
+		if (node_name.substr(0, 9) == "Texture::") {
+			node_name = node_name.substr(9, node_name.length() - 9);
+			return node_name.replace(":", "");
+		}
+
+		return node_name.replace(":", "");
+	}
+
+	static std::string FBXAnimMeshName(const std::string &name) {
+		if (name.length()) {
+			size_t indexOf = name.find_first_of("::");
+			if (indexOf != std::string::npos && indexOf < name.size() - 2) {
+				return name.substr(indexOf + 2);
+			}
+		}
+		return name.length() ? name : "AnimMesh";
+	}
+
+	static Vector3 safe_import_vector3(const Vector3 &p_vec) {
+		Vector3 vector = p_vec;
+		if (Math::is_equal_approx(0, vector.x)) {
+			vector.x = 0;
+		}
+
+		if (Math::is_equal_approx(0, vector.y)) {
+			vector.y = 0;
+		}
+
+		if (Math::is_equal_approx(0, vector.z)) {
+			vector.z = 0;
+		}
+		return vector;
+	}
+
+	static void debug_xform(String name, const Basis &t) {
+		//print_verbose(name + " rotation: " + (t.get_euler() * (180 / Math_PI)));
+	}
+
+	static Vector3 FixAxisConversions(Vector3 input) {
+		return Vector3(input.x, input.y, input.z);
+	}
+
+	static void AlignMeshAxes(std::vector<Vector3> &vertex_data) {
+		for (size_t x = 0; x < vertex_data.size(); x++) {
+			vertex_data[x] = FixAxisConversions(vertex_data[x]);
+		}
+	}
+
+	struct AssetImportFbx {
+		enum ETimeMode {
+			TIME_MODE_DEFAULT = 0,
+			TIME_MODE_120 = 1,
+			TIME_MODE_100 = 2,
+			TIME_MODE_60 = 3,
+			TIME_MODE_50 = 4,
+			TIME_MODE_48 = 5,
+			TIME_MODE_30 = 6,
+			TIME_MODE_30_DROP = 7,
+			TIME_MODE_NTSC_DROP_FRAME = 8,
+			TIME_MODE_NTSC_FULL_FRAME = 9,
+			TIME_MODE_PAL = 10,
+			TIME_MODE_CINEMA = 11,
+			TIME_MODE_1000 = 12,
+			TIME_MODE_CINEMA_ND = 13,
+			TIME_MODE_CUSTOM = 14,
+			TIME_MODE_TIME_MODE_COUNT = 15
+		};
+		enum UpAxis {
+			UP_VECTOR_AXIS_X = 1,
+			UP_VECTOR_AXIS_Y = 2,
+			UP_VECTOR_AXIS_Z = 3
+		};
+		enum FrontAxis {
+			FRONT_PARITY_EVEN = 1,
+			FRONT_PARITY_ODD = 2,
+		};
+
+		enum CoordAxis {
+			COORD_RIGHT = 0,
+			COORD_LEFT = 1
+		};
+	};
+
+	/** Get fbx fps for time mode meta data
+     */
+	static float get_fbx_fps(int32_t time_mode) {
+		switch (time_mode) {
+			case AssetImportFbx::TIME_MODE_DEFAULT: return 24;
+			case AssetImportFbx::TIME_MODE_120: return 120;
+			case AssetImportFbx::TIME_MODE_100: return 100;
+			case AssetImportFbx::TIME_MODE_60: return 60;
+			case AssetImportFbx::TIME_MODE_50: return 50;
+			case AssetImportFbx::TIME_MODE_48: return 48;
+			case AssetImportFbx::TIME_MODE_30: return 30;
+			case AssetImportFbx::TIME_MODE_30_DROP: return 30;
+			case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f;
+			case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f;
+			case AssetImportFbx::TIME_MODE_PAL: return 25;
+			case AssetImportFbx::TIME_MODE_CINEMA: return 24;
+			case AssetImportFbx::TIME_MODE_1000: return 1000;
+			case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f;
+			case AssetImportFbx::TIME_MODE_CUSTOM: return -1;
+		}
+		return 0;
+	}
+
+	static float get_fbx_fps(const FBXDocParser::FileGlobalSettings *FBXSettings) {
+		int time_mode = FBXSettings->TimeMode();
+
+		// get the animation FPS
+		float frames_per_second = get_fbx_fps(time_mode);
+
+		// handle animation custom FPS time.
+		if (time_mode == ImportUtils::AssetImportFbx::TIME_MODE_CUSTOM) {
+			print_verbose("FBX Animation has custom FPS setting");
+			frames_per_second = FBXSettings->CustomFrameRate();
+
+			// not our problem this is the modeller, we can print as an error so they can fix the source.
+			if (frames_per_second == 0) {
+				print_error("Custom animation time in file is set to 0 value, animation won't play, please edit your file to correct the FPS value");
+			}
+		}
+		return frames_per_second;
+	}
+
+	/**
+	  * Find hardcoded textures from assimp which could be in many different directories
+	  */
+
+	/**
+	  * set_texture_mapping_mode
+	  * Helper to check the mapping mode of the texture (repeat, clamp and mirror)
+	  */
+	// static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
+	// 	ERR_FAIL_COND(texture.is_null());
+	// 	ERR_FAIL_COND(map_mode == NULL);
+	// 	aiTextureMapMode tex_mode = map_mode[0];
+
+	// 	int32_t flags = Texture::FLAGS_DEFAULT;
+	// 	if (tex_mode == aiTextureMapMode_Wrap) {
+	// 		//Default
+	// 	} else if (tex_mode == aiTextureMapMode_Clamp) {
+	// 		flags = flags & ~Texture::FLAG_REPEAT;
+	// 	} else if (tex_mode == aiTextureMapMode_Mirror) {
+	// 		flags = flags | Texture::FLAG_MIRRORED_REPEAT;
+	// 	}
+	// 	texture->set_flags(flags);
+	// }
+
+	/**
+	  * Load or load from cache image :)
+	  * We need to upgrade this in the later version :) should not be hard
+	  */
+	//static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path){
+
+	// Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path);
+
+	// // if our cache contains this image then don't bother
+	// if (match) {
+	// 	return match->get();
+	// }
+
+	// Vector<String> split_path = p_path.get_basename().split("*");
+	// if (split_path.size() == 2) {
+	// 	size_t texture_idx = split_path[1].to_int();
+	// 	ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
+	// 	aiTexture *tex = p_scene->mTextures[texture_idx];
+	// 	String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
+	// 	filename = filename.get_file();
+	// 	print_verbose("Open Asset Import: Loading embedded texture " + filename);
+	// 	if (tex->mHeight == 0) {
+	// 		if (tex->CheckFormat("png")) {
+	// 			Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+	// 			ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+	// 			state.path_to_image_cache.insert(p_path, img);
+	// 			return img;
+	// 		} else if (tex->CheckFormat("jpg")) {
+	// 			Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+	// 			ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+	// 			state.path_to_image_cache.insert(p_path, img);
+	// 			return img;
+	// 		} else if (tex->CheckFormat("dds")) {
+	// 			ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented");
+	// 		}
+	// 	} else {
+	// 		Ref<Image> img;
+	// 		img.instance();
+	// 		PoolByteArray arr;
+	// 		uint32_t size = tex->mWidth * tex->mHeight;
+	// 		arr.resize(size);
+	// 		memcpy(arr.write().ptr(), tex->pcData, size);
+	// 		ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
+	// 		//ARGB8888 to RGBA8888
+	// 		for (int32_t i = 0; i < arr.size() / 4; i++) {
+	// 			arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
+	// 			arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
+	// 			arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
+	// 			arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
+	// 		}
+	// 		img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
+	// 		ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+	// 		state.path_to_image_cache.insert(p_path, img);
+	// 		return img;
+	// 	}
+	// 	return Ref<Image>();
+	// } else {
+	// 	Ref<Texture> texture = ResourceLoader::load(p_path);
+	// 	ERR_FAIL_COND_V(texture.is_null(), Ref<Image>());
+	// 	Ref<Image> image = texture->get_data();
+	// 	ERR_FAIL_COND_V(image.is_null(), Ref<Image>());
+	// 	state.path_to_image_cache.insert(p_path, image);
+	// 	return image;
+	// }
+
+	// return Ref<Image>();
+	//}
+
+	// /* create texture from assimp data, if found in path */
+	// static bool CreateAssimpTexture(
+	// 		AssimpImporter::ImportState &state,
+	// 		aiString texture_path,
+	// 		String &filename,
+	// 		String &path,
+	// 		AssimpImageData &image_state) {
+	// 	filename = get_raw_string_from_assimp(texture_path);
+	// 	path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
+	// 	bool found = false;
+	// 	find_texture_path(state.path, path, found);
+	// 	if (found) {
+	// 		image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
+	// 		if (image_state.raw_image.is_valid()) {
+	// 			image_state.texture.instance();
+	// 			image_state.texture->create_from_image(image_state.raw_image);
+	// 			image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+	// 			return true;
+	// 		}
+	// 	}
+
+	// 	return false;
+	// }
+	// /** GetAssimpTexture
+	//   * Designed to retrieve textures for you
+	//   */
+	// static bool GetAssimpTexture(
+	// 		AssimpImporter::ImportState &state,
+	// 		aiMaterial *ai_material,
+	// 		aiTextureType texture_type,
+	// 		String &filename,
+	// 		String &path,
+	// 		AssimpImageData &image_state) {
+	// 	aiString ai_filename = aiString();
+	// 	if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, NULL, NULL, NULL, NULL, image_state.map_mode)) {
+	// 		return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
+	// 	}
+
+	// 	return false;
+	// }
+};
+
+// Apply the transforms so the basis will have scale 1.
+Transform get_unscaled_transform(const Transform &p_initial, real_t p_scale);
+
+/// Uses the Newell's method to compute any polygon normal.
+/// The polygon must be at least size of 3 or bigger.
+Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices);
+
+#endif // IMPORT_UTILS_FBX_IMPORTER_H

+ 0 - 18
thirdparty/README.md

@@ -1,24 +1,6 @@
 # Third party libraries
 # Third party libraries
 
 
 
 
-## assimp
-
-- Upstream: http://github.com/assimp/assimp
-- Version: git (308db73d0b3c2d1870cd3e465eaa283692a4cf23, 2019)
-- License: BSD-3-Clause
-
-Files extracted from upstream source:
-
-- Run `cmake .` in root folder to generate files
-- `code/{CApi,Common,FBX,Material,PostProcessing}/`
-- `contrib/utf8cpp/source/`
-- `include/`
-- `revision.h`
-- `CREDITS` and `LICENSE` files
-- `rm -f code/Common/ZipArchiveIOSystem.cpp include/assimp/ZipArchiveIOSystem.h
-   include/assimp/irrXMLWrapper.h`
-
-
 ## bullet
 ## bullet
 
 
 - Upstream: https://github.com/bulletphysics/bullet3
 - Upstream: https://github.com/bulletphysics/bullet3

+ 0 - 156
thirdparty/assimp/code/CApi/AssimpCExport.cpp

@@ -1,156 +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 AssimpCExport.cpp
-Assimp C export interface. See Exporter.cpp for some notes.
-*/
-
-#ifndef ASSIMP_BUILD_NO_EXPORT
-
-#include "CInterfaceIOWrapper.h"
-#include <assimp/SceneCombiner.h>
-#include "Common/ScenePrivate.h"
-#include <assimp/Exporter.hpp>
-
-using namespace Assimp;
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API size_t aiGetExportFormatCount(void)
-{
-    return Exporter().GetExportFormatCount();
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t index)
-{
-    // Note: this is valid as the index always pertains to a built-in exporter,
-    // for which the returned structure is guaranteed to be of static storage duration.
-    Exporter exporter;
-    const aiExportFormatDesc* orig( exporter.GetExportFormatDescription( index ) );
-    if (NULL == orig) {
-        return NULL;
-    }
-
-    aiExportFormatDesc *desc = new aiExportFormatDesc;
-    desc->description = new char[ strlen( orig->description ) + 1 ]();
-    ::strncpy( (char*) desc->description, orig->description, strlen( orig->description ) );
-    desc->fileExtension = new char[ strlen( orig->fileExtension ) + 1 ]();
-    ::strncpy( ( char* ) desc->fileExtension, orig->fileExtension, strlen( orig->fileExtension ) );
-    desc->id = new char[ strlen( orig->id ) + 1 ]();
-    ::strncpy( ( char* ) desc->id, orig->id, strlen( orig->id ) );
-
-    return desc;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiReleaseExportFormatDescription( const aiExportFormatDesc *desc ) {
-    if (NULL == desc) {
-        return;
-    }
-
-    delete [] desc->description;
-    delete [] desc->fileExtension;
-    delete [] desc->id;
-    delete desc;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut)
-{
-    if (!pOut || !pIn) {
-        return;
-    }
-
-    SceneCombiner::CopyScene(pOut,pIn,true);
-    ScenePriv(*pOut)->mIsCopy = true;
-}
-
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn)
-{
-    // note: aiReleaseImport() is also able to delete scene copies, but in addition
-    // it also handles scenes with import metadata.
-    delete pIn;
-}
-
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing )
-{
-    return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing);
-}
-
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing )
-{
-    Exporter exp;
-
-    if (pIO) {
-        exp.SetIOHandler(new CIOSystemWrapper(pIO));
-    }
-    return exp.Export(pScene,pFormatId,pFileName,pPreprocessing);
-}
-
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing  )
-{
-    Exporter exp;
-    if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) {
-        return NULL;
-    }
-    const aiExportDataBlob* blob = exp.GetOrphanedBlob();
-    ai_assert(blob);
-
-    return blob;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData )
-{
-    delete pData;
-}
-
-#endif // !ASSIMP_BUILD_NO_EXPORT

+ 0 - 136
thirdparty/assimp/code/CApi/CInterfaceIOWrapper.cpp

@@ -1,136 +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 aiFileIO -> IOSystem wrapper*/
-
-#include "CInterfaceIOWrapper.h"
-
-namespace Assimp    {
-
-CIOStreamWrapper::~CIOStreamWrapper(void)
-{
-    /* Various places depend on this destructor to close the file */
-    if (mFile) {
-        mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile);
-        mFile = nullptr;
-    }
-}
-
-// ...................................................................
-size_t CIOStreamWrapper::Read(void* pvBuffer,
-    size_t pSize,
-    size_t pCount
-){
-    // need to typecast here as C has no void*
-    return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount);
-}
-
-// ...................................................................
-size_t CIOStreamWrapper::Write(const void* pvBuffer,
-    size_t pSize,
-    size_t pCount
-){
-    // need to typecast here as C has no void*
-    return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount);
-}
-
-// ...................................................................
-aiReturn CIOStreamWrapper::Seek(size_t pOffset,
-    aiOrigin pOrigin
-){
-    return mFile->SeekProc(mFile,pOffset,pOrigin);
-}
-
-// ...................................................................
-size_t CIOStreamWrapper::Tell(void) const {
-    return mFile->TellProc(mFile);
-}
-
-// ...................................................................
-size_t CIOStreamWrapper::FileSize() const {
-    return mFile->FileSizeProc(mFile);
-}
-
-// ...................................................................
-void CIOStreamWrapper::Flush () {
-    return mFile->FlushProc(mFile);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Custom IOStream implementation for the C-API
-bool CIOSystemWrapper::Exists( const char* pFile) const {
-    aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,"rb");
-    if (p){
-        mFileSystem->CloseProc(mFileSystem,p);
-        return true;
-    }
-    return false;
-}
-
-// ...................................................................
-char CIOSystemWrapper::getOsSeparator() const {
-#ifndef _WIN32
-    return '/';
-#else
-    return '\\';
-#endif
-}
-
-// ...................................................................
-IOStream* CIOSystemWrapper::Open(const char* pFile,const char* pMode) {
-    aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode);
-    if (!p) {
-        return NULL;
-    }
-    return new CIOStreamWrapper(p, this);
-}
-
-// ...................................................................
-void CIOSystemWrapper::Close( IOStream* pFile) {
-    if (!pFile) {
-        return;
-    }
-    delete pFile;
-}
-
-}

+ 0 - 99
thirdparty/assimp/code/CApi/CInterfaceIOWrapper.h

@@ -1,99 +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 aiFileIO -> IOSystem wrapper*/
-
-#ifndef AI_CIOSYSTEM_H_INCLUDED
-#define AI_CIOSYSTEM_H_INCLUDED
-
-#include <assimp/cfileio.h>
-#include <assimp/IOStream.hpp>
-#include <assimp/IOSystem.hpp>
-
-namespace Assimp    {
-
-class CIOSystemWrapper;
-
-// ------------------------------------------------------------------------------------------------
-// Custom IOStream implementation for the C-API
-class CIOStreamWrapper : public IOStream
-{
-public:
-    explicit CIOStreamWrapper(aiFile* pFile, CIOSystemWrapper* io)
-        : mFile(pFile),
-        mIO(io)
-    {}
-    ~CIOStreamWrapper(void);
-
-    size_t Read(void* pvBuffer, size_t pSize, size_t pCount);
-    size_t Write(const void* pvBuffer, size_t pSize, size_t pCount);
-    aiReturn Seek(size_t pOffset, aiOrigin pOrigin);
-    size_t Tell(void) const;
-    size_t FileSize() const;
-    void Flush();
-
-private:
-    aiFile* mFile;
-    CIOSystemWrapper* mIO;
-};
-
-class CIOSystemWrapper : public IOSystem
-{
-    friend class CIOStreamWrapper;
-public:
-    explicit CIOSystemWrapper(aiFileIO* pFile)
-        : mFileSystem(pFile)
-    {}
-
-    bool Exists( const char* pFile) const;
-    char getOsSeparator() const;
-    IOStream* Open(const char* pFile,const char* pMode = "rb");
-    void Close( IOStream* pFile);
-private:
-    aiFileIO* mFileSystem;
-};
-
-}
-
-#endif
-

+ 0 - 695
thirdparty/assimp/code/Common/Assimp.cpp

@@ -1,695 +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  Assimp.cpp
- *  @brief Implementation of the Plain-C API
- */
-
-#include <assimp/cimport.h>
-#include <assimp/LogStream.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/Importer.hpp>
-#include <assimp/importerdesc.h>
-#include <assimp/scene.h>
-#include <assimp/GenericProperty.h>
-#include <assimp/Exceptional.h>
-#include <assimp/BaseImporter.h>
-
-#include "CApi/CInterfaceIOWrapper.h"
-#include "Importer.h"
-#include "ScenePrivate.h"
-
-#include <list>
-
-// ------------------------------------------------------------------------------------------------
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-#   include <thread>
-#   include <mutex>
-#endif
-// ------------------------------------------------------------------------------------------------
-using namespace Assimp;
-
-namespace Assimp {
-    // underlying structure for aiPropertyStore
-    typedef BatchLoader::PropertyMap PropertyMap;
-
-    /** Stores the LogStream objects for all active C log streams */
-    struct mpred {
-        bool operator  () (const aiLogStream& s0, const aiLogStream& s1) const  {
-            return s0.callback<s1.callback&&s0.user<s1.user;
-        }
-    };
-    typedef std::map<aiLogStream, Assimp::LogStream*, mpred> LogStreamMap;
-
-    /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */
-    typedef std::list<Assimp::LogStream*> PredefLogStreamMap;
-
-    /** Local storage of all active log streams */
-    static LogStreamMap gActiveLogStreams;
-
-    /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */
-    static PredefLogStreamMap gPredefinedStreams;
-
-    /** Error message of the last failed import process */
-    static std::string gLastErrorString;
-
-    /** Verbose logging active or not? */
-    static aiBool gVerboseLogging = false;
-
-    /** will return all registered importers. */
-    void GetImporterInstanceList(std::vector< BaseImporter* >& out);
-
-    /** will delete all registered importers. */
-    void DeleteImporterInstanceList(std::vector< BaseImporter* >& out);
-} // namespace assimp
-
-
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-/** Global mutex to manage the access to the log-stream map */
-static std::mutex gLogStreamMutex;
-#endif
-
-// ------------------------------------------------------------------------------------------------
-// Custom LogStream implementation for the C-API
-class LogToCallbackRedirector : public LogStream {
-public:
-    explicit LogToCallbackRedirector(const aiLogStream& s)
-    : stream (s)    {
-        ai_assert(NULL != s.callback);
-    }
-
-    ~LogToCallbackRedirector()  {
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-        std::lock_guard<std::mutex> lock(gLogStreamMutex);
-#endif
-        // (HACK) Check whether the 'stream.user' pointer points to a
-        // custom LogStream allocated by #aiGetPredefinedLogStream.
-        // In this case, we need to delete it, too. Of course, this
-        // might cause strange problems, but the chance is quite low.
-
-        PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(),
-            gPredefinedStreams.end(), (Assimp::LogStream*)stream.user);
-
-        if (it != gPredefinedStreams.end()) {
-            delete *it;
-            gPredefinedStreams.erase(it);
-        }
-    }
-
-    /** @copydoc LogStream::write */
-    void write(const char* message) {
-        stream.callback(message,stream.user);
-    }
-
-private:
-    aiLogStream stream;
-};
-
-// ------------------------------------------------------------------------------------------------
-void ReportSceneNotFoundError() {
-    ASSIMP_LOG_ERROR("Unable to find the Assimp::Importer for this aiScene. "
-        "The C-API does not accept scenes produced by the C++ API and vice versa");
-
-    ai_assert(false);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the given file and returns its content.
-const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) {
-    return aiImportFileEx(pFile,pFlags,NULL);
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags,  aiFileIO* pFS) {
-    return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL);
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags, 
-        aiFileIO* pFS, const aiPropertyStore* props) {
-    ai_assert(NULL != pFile);
-
-    const aiScene* scene = NULL;
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // create an Importer for this file
-    Assimp::Importer* imp = new Assimp::Importer();
-
-    // copy properties
-    if(props) {
-        const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
-        ImporterPimpl* pimpl = imp->Pimpl();
-        pimpl->mIntProperties = pp->ints;
-        pimpl->mFloatProperties = pp->floats;
-        pimpl->mStringProperties = pp->strings;
-        pimpl->mMatrixProperties = pp->matrices;
-    }
-    // setup a custom IO system if necessary
-    if (pFS) {
-        imp->SetIOHandler( new CIOSystemWrapper (pFS) );
-    }
-
-    // and have it read the file
-    scene = imp->ReadFile( pFile, pFlags);
-
-    // if succeeded, store the importer in the scene and keep it alive
-    if( scene)  {
-        ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
-        priv->mOrigImporter = imp;
-    } else {
-        // if failed, extract error code and destroy the import
-        gLastErrorString = imp->GetErrorString();
-        delete imp;
-    }
-
-    // return imported data. If the import failed the pointer is NULL anyways
-    ASSIMP_END_EXCEPTION_REGION(const aiScene*);
-    
-    return scene;
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiScene* aiImportFileFromMemory(
-    const char* pBuffer,
-    unsigned int pLength,
-    unsigned int pFlags,
-    const char* pHint)
-{
-    return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL);
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiScene* aiImportFileFromMemoryWithProperties(
-    const char* pBuffer,
-    unsigned int pLength,
-    unsigned int pFlags,
-    const char* pHint,
-    const aiPropertyStore* props)
-{
-    ai_assert( NULL != pBuffer );
-    ai_assert( 0 != pLength );
-
-    const aiScene* scene = NULL;
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // create an Importer for this file
-    Assimp::Importer* imp = new Assimp::Importer();
-
-    // copy properties
-    if(props) {
-        const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
-        ImporterPimpl* pimpl = imp->Pimpl();
-        pimpl->mIntProperties = pp->ints;
-        pimpl->mFloatProperties = pp->floats;
-        pimpl->mStringProperties = pp->strings;
-        pimpl->mMatrixProperties = pp->matrices;
-    }
-
-    // and have it read the file from the memory buffer
-    scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint);
-
-    // if succeeded, store the importer in the scene and keep it alive
-    if( scene)  {
-         ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
-         priv->mOrigImporter = imp;
-    }
-    else    {
-        // if failed, extract error code and destroy the import
-        gLastErrorString = imp->GetErrorString();
-        delete imp;
-    }
-    // return imported data. If the import failed the pointer is NULL anyways
-    ASSIMP_END_EXCEPTION_REGION(const aiScene*);
-    return scene;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Releases all resources associated with the given import process.
-void aiReleaseImport( const aiScene* pScene)
-{
-    if (!pScene) {
-        return;
-    }
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // find the importer associated with this data
-    const ScenePrivateData* priv = ScenePriv(pScene);
-    if( !priv || !priv->mOrigImporter)  {
-        delete pScene;
-    }
-    else {
-        // deleting the Importer also deletes the scene
-        // Note: the reason that this is not written as 'delete priv->mOrigImporter'
-        // is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339)
-        Importer* importer = priv->mOrigImporter;
-        delete importer;
-    }
-
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene,
-    unsigned int pFlags)
-{
-    const aiScene* sc = NULL;
-
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // find the importer associated with this data
-    const ScenePrivateData* priv = ScenePriv(pScene);
-    if( !priv || !priv->mOrigImporter)  {
-        ReportSceneNotFoundError();
-        return NULL;
-    }
-
-    sc = priv->mOrigImporter->ApplyPostProcessing(pFlags);
-
-    if (!sc) {
-        aiReleaseImport(pScene);
-        return NULL;
-    }
-
-    ASSIMP_END_EXCEPTION_REGION(const aiScene*);
-    return sc;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API const aiScene *aiApplyCustomizedPostProcessing( const aiScene *scene,
-                                                           BaseProcess* process,
-                                                           bool requestValidation ) {
-    const aiScene* sc( NULL );
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // find the importer associated with this data
-    const ScenePrivateData* priv = ScenePriv( scene );
-    if ( NULL == priv || NULL == priv->mOrigImporter ) {
-        ReportSceneNotFoundError();
-        return NULL;
-    }
-
-    sc = priv->mOrigImporter->ApplyCustomizedPostProcessing( process, requestValidation );
-
-    if ( !sc ) {
-        aiReleaseImport( scene );
-        return NULL;
-    }
-
-    ASSIMP_END_EXCEPTION_REGION( const aiScene* );
-
-    return sc;
-}
-
-// ------------------------------------------------------------------------------------------------
-void CallbackToLogRedirector (const char* msg, char* dt)
-{
-    ai_assert( NULL != msg );
-    ai_assert( NULL != dt );
-    LogStream* s = (LogStream*)dt;
-
-    s->write(msg);
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file)
-{
-    aiLogStream sout;
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    LogStream* stream = LogStream::createDefaultStream(pStream,file);
-    if (!stream) {
-        sout.callback = NULL;
-        sout.user = NULL;
-    }
-    else {
-        sout.callback = &CallbackToLogRedirector;
-        sout.user = (char*)stream;
-    }
-    gPredefinedStreams.push_back(stream);
-    ASSIMP_END_EXCEPTION_REGION(aiLogStream);
-    return sout;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiAttachLogStream( const aiLogStream* stream )
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-    std::lock_guard<std::mutex> lock(gLogStreamMutex);
-#endif
-
-    LogStream* lg = new LogToCallbackRedirector(*stream);
-    gActiveLogStreams[*stream] = lg;
-
-    if (DefaultLogger::isNullLogger()) {
-        DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
-    }
-    DefaultLogger::get()->attachStream(lg);
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-    std::lock_guard<std::mutex> lock(gLogStreamMutex);
-#endif
-    // find the log-stream associated with this data
-    LogStreamMap::iterator it = gActiveLogStreams.find( *stream);
-    // it should be there... else the user is playing fools with us
-    if( it == gActiveLogStreams.end())  {
-        return AI_FAILURE;
-    }
-    DefaultLogger::get()->detatchStream( it->second );
-    delete it->second;
-
-    gActiveLogStreams.erase( it);
-
-    if (gActiveLogStreams.empty()) {
-        DefaultLogger::kill();
-    }
-    ASSIMP_END_EXCEPTION_REGION(aiReturn);
-    return AI_SUCCESS;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiDetachAllLogStreams(void)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-    std::lock_guard<std::mutex> lock(gLogStreamMutex);
-#endif
-    Logger *logger( DefaultLogger::get() );
-    if ( NULL == logger ) {
-        return;
-    }
-
-    for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) {
-        logger->detatchStream( it->second );
-        delete it->second;
-    }
-    gActiveLogStreams.clear();
-    DefaultLogger::kill();
-
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiEnableVerboseLogging(aiBool d)
-{
-    if (!DefaultLogger::isNullLogger()) {
-        DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
-    }
-    gVerboseLogging = d;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns the error text of the last failed import process.
-const char* aiGetErrorString()
-{
-    return gLastErrorString.c_str();
-}
-
-// -----------------------------------------------------------------------------------------------
-// Return the description of a importer given its index
-const aiImporterDesc* aiGetImportFormatDescription( size_t pIndex)
-{
-    return Importer().GetImporterInfo(pIndex);
-}
-
-// -----------------------------------------------------------------------------------------------
-// Return the number of importers
-size_t aiGetImportFormatCount(void)
-{
-    return Importer().GetImporterCount();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns the error text of the last failed import process.
-aiBool aiIsExtensionSupported(const char* szExtension)
-{
-    ai_assert(NULL != szExtension);
-    aiBool candoit=AI_FALSE;
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // FIXME: no need to create a temporary Importer instance just for that ..
-    Assimp::Importer tmp;
-    candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE;
-
-    ASSIMP_END_EXCEPTION_REGION(aiBool);
-    return candoit;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a list of all file extensions supported by ASSIMP
-void aiGetExtensionList(aiString* szOut)
-{
-    ai_assert(NULL != szOut);
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // FIXME: no need to create a temporary Importer instance just for that ..
-    Assimp::Importer tmp;
-    tmp.GetExtensionList(*szOut);
-
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the memory requirements for a particular import.
-void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
-    C_STRUCT aiMemoryInfo* in)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // find the importer associated with this data
-    const ScenePrivateData* priv = ScenePriv(pIn);
-    if( !priv || !priv->mOrigImporter)  {
-        ReportSceneNotFoundError();
-        return;
-    }
-
-    return priv->mOrigImporter->GetMemoryRequirements(*in);
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void)
-{
-    return reinterpret_cast<aiPropertyStore*>( new PropertyMap() );
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p)
-{
-    delete reinterpret_cast<PropertyMap*>(p);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Importer::SetPropertyInteger
-ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
-    SetGenericProperty<int>(pp->ints,szName,value);
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Importer::SetPropertyFloat
-ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, ai_real value)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
-    SetGenericProperty<ai_real>(pp->floats,szName,value);
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Importer::SetPropertyString
-ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName,
-    const C_STRUCT aiString* st)
-{
-    if (!st) {
-        return;
-    }
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
-    SetGenericProperty<std::string>(pp->strings,szName,std::string(st->C_Str()));
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Importer::SetPropertyMatrix
-ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore* p, const char* szName,
-    const C_STRUCT aiMatrix4x4* mat)
-{
-    if (!mat) {
-        return;
-    }
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
-    SetGenericProperty<aiMatrix4x4>(pp->matrices,szName,*mat);
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Rotation matrix to quaternion
-ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat)
-{
-    ai_assert( NULL != quat );
-    ai_assert( NULL != mat );
-    *quat = aiQuaternion(*mat);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Matrix decomposition
-ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling,
-    aiQuaternion* rotation,
-    aiVector3D* position)
-{
-    ai_assert( NULL != rotation );
-    ai_assert( NULL != position );
-    ai_assert( NULL != scaling );
-    ai_assert( NULL != mat );
-    mat->Decompose(*scaling,*rotation,*position);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Matrix transpose
-ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat)
-{
-    ai_assert(NULL != mat);
-    mat->Transpose();
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat)
-{
-    ai_assert(NULL != mat);
-    mat->Transpose();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Vector transformation
-ASSIMP_API void aiTransformVecByMatrix3(aiVector3D* vec,
-    const aiMatrix3x3* mat)
-{
-    ai_assert( NULL != mat );
-    ai_assert( NULL != vec);
-    *vec *= (*mat);
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec,
-    const aiMatrix4x4* mat)
-{
-    ai_assert( NULL != mat );
-    ai_assert( NULL != vec );
-
-    *vec *= (*mat);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Matrix multiplication
-ASSIMP_API void aiMultiplyMatrix4(
-    aiMatrix4x4* dst,
-    const aiMatrix4x4* src)
-{
-    ai_assert( NULL != dst );
-    ai_assert( NULL != src );
-    *dst = (*dst) * (*src);
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiMultiplyMatrix3(
-    aiMatrix3x3* dst,
-    const aiMatrix3x3* src)
-{
-    ai_assert( NULL != dst );
-    ai_assert( NULL != src );
-    *dst = (*dst) * (*src);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Matrix identity
-ASSIMP_API void aiIdentityMatrix3(
-    aiMatrix3x3* mat)
-{
-    ai_assert(NULL != mat);
-    *mat = aiMatrix3x3();
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API void aiIdentityMatrix4(
-    aiMatrix4x4* mat)
-{
-    ai_assert(NULL != mat);
-    *mat = aiMatrix4x4();
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API C_STRUCT const aiImporterDesc* aiGetImporterDesc( const char *extension ) {
-    if( NULL == extension ) {
-        return NULL;
-    }
-    const aiImporterDesc *desc( NULL );
-    std::vector< BaseImporter* > out;
-    GetImporterInstanceList( out );
-    for( size_t i = 0; i < out.size(); ++i ) {
-        if( 0 == strncmp( out[ i ]->GetInfo()->mFileExtensions, extension, strlen( extension ) ) ) {
-            desc = out[ i ]->GetInfo();
-            break;
-        }
-    }
-
-    DeleteImporterInstanceList(out);
-
-    return desc;
-}
-
-// ------------------------------------------------------------------------------------------------

+ 0 - 656
thirdparty/assimp/code/Common/BaseImporter.cpp

@@ -1,656 +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  BaseImporter.cpp
- *  @brief Implementation of BaseImporter
- */
-
-#include <assimp/BaseImporter.h>
-#include <assimp/ParsingUtils.h>
-#include "FileSystemFilter.h"
-#include "Importer.h"
-#include <assimp/ByteSwapper.h>
-#include <assimp/scene.h>
-#include <assimp/Importer.hpp>
-#include <assimp/postprocess.h>
-#include <assimp/importerdesc.h>
-
-#include <ios>
-#include <list>
-#include <memory>
-#include <sstream>
-#include <cctype>
-
-using namespace Assimp;
-
-// ------------------------------------------------------------------------------------------------
-// Constructor to be privately used by Importer
-BaseImporter::BaseImporter() AI_NO_EXCEPT
-: m_progress() {
-    /**
-    * Assimp Importer
-    * unit conversions available
-    * if you need another measurment unit add it below.
-    * it's currently defined in assimp that we prefer meters.
-    *
-    * NOTE: Initialised here rather than in the header file
-    * to workaround a VS2013 bug with brace initialisers
-    * */
-    importerUnits[ImporterUnits::M] = 1.0;
-    importerUnits[ImporterUnits::CM] = 0.01;
-    importerUnits[ImporterUnits::MM] = 0.001;
-    importerUnits[ImporterUnits::INCHES] = 0.0254;
-    importerUnits[ImporterUnits::FEET] = 0.3048;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-BaseImporter::~BaseImporter() {
-    // nothing to do here
-}
-
-void BaseImporter::UpdateImporterScale( Importer* pImp )
-{
-    ai_assert(pImp != nullptr);
-    ai_assert(importerScale != 0.0);
-    ai_assert(fileScale != 0.0);
-
-    double activeScale = importerScale * fileScale;
-
-    // Set active scaling
-    pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, static_cast<float>( activeScale) );
-
-    ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
-}
-
-// ------------------------------------------------------------------------------------------------
-// Imports the given file and returns the imported data.
-aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
-
-
-    m_progress = pImp->GetProgressHandler();
-    if (nullptr == m_progress) {
-        return nullptr;
-    }
-
-    ai_assert(m_progress);
-
-    // Gather configuration properties for this run
-    SetupProperties( pImp );
-
-    // Construct a file system filter to improve our success ratio at reading external files
-    FileSystemFilter filter(pFile,pIOHandler);
-
-    // create a scene object to hold the data
-    std::unique_ptr<aiScene> sc(new aiScene());
-
-    // dispatch importing
-    try
-    {
-        InternReadFile( pFile, sc.get(), &filter);
-
-        // Calculate import scale hook - required because pImp not available anywhere else
-        // passes scale into ScaleProcess
-        UpdateImporterScale(pImp);
-
-
-    } catch( const std::exception& err )    {
-        // extract error description
-        m_ErrorText = err.what();
-        ASSIMP_LOG_ERROR(m_ErrorText);
-        return nullptr;
-    }
-
-    // return what we gathered from the import.
-    return sc.release();
-}
-
-// ------------------------------------------------------------------------------------------------
-void BaseImporter::SetupProperties(const Importer* pImp)
-{
-    // the default implementation does nothing
-}
-
-// ------------------------------------------------------------------------------------------------
-void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
-    const aiImporterDesc* desc = GetInfo();
-    ai_assert(desc != nullptr);
-
-    const char* ext = desc->mFileExtensions;
-    ai_assert(ext != nullptr );
-
-    const char* last = ext;
-    do {
-        if (!*ext || *ext == ' ') {
-            extensions.insert(std::string(last,ext-last));
-            ai_assert(ext-last > 0);
-            last = ext;
-            while(*last == ' ') {
-                ++last;
-            }
-        }
-    }
-    while(*ext++);
-}
-
-// ------------------------------------------------------------------------------------------------
-/*static*/ bool BaseImporter::SearchFileHeaderForToken( IOSystem* pIOHandler,
-    const std::string&  pFile,
-    const char**        tokens,
-    unsigned int        numTokens,
-    unsigned int        searchBytes /* = 200 */,
-    bool                tokensSol /* false */,
-    bool                noAlphaBeforeTokens /* false */)
-{
-    ai_assert( nullptr != tokens );
-    ai_assert( 0 != numTokens );
-    ai_assert( 0 != searchBytes);
-
-    if ( nullptr == pIOHandler ) {
-        return false;
-    }
-
-    std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile));
-    if (pStream.get() ) {
-        // read 200 characters from the file
-        std::unique_ptr<char[]> _buffer (new char[searchBytes+1 /* for the '\0' */]);
-        char *buffer( _buffer.get() );
-        const size_t read( pStream->Read(buffer,1,searchBytes) );
-        if( 0 == read ) {
-            return false;
-        }
-
-        for( size_t i = 0; i < read; ++i ) {
-            buffer[ i ] = static_cast<char>( ::tolower( buffer[ i ] ) );
-        }
-
-        // It is not a proper handling of unicode files here ...
-        // ehm ... but it works in most cases.
-        char* cur = buffer,*cur2 = buffer,*end = &buffer[read];
-        while (cur != end)  {
-            if( *cur ) {
-                *cur2++ = *cur;
-            }
-            ++cur;
-        }
-        *cur2 = '\0';
-
-        std::string token;
-        for (unsigned int i = 0; i < numTokens; ++i ) {
-            ai_assert( nullptr != tokens[i] );
-            const size_t len( strlen( tokens[ i ] ) );
-            token.clear();
-            const char *ptr( tokens[ i ] );
-            for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) {
-                token.push_back( static_cast<char>( tolower( *ptr ) ) );
-                ++ptr;
-            }
-            const char* r = strstr( buffer, token.c_str() );
-            if( !r ) {
-                continue;
-            }
-            // We need to make sure that we didn't accidentially identify the end of another token as our token,
-            // e.g. in a previous version the "gltf " present in some gltf files was detected as "f "
-            if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) {
-                continue;
-            }
-            // We got a match, either we don't care where it is, or it happens to
-            // be in the beginning of the file / line
-            if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') {
-                ASSIMP_LOG_DEBUG_F( "Found positive match for header keyword: ", tokens[i] );
-                return true;
-            }
-        }
-    }
-
-    return false;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Simple check for file extension
-/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile,
-    const char* ext0,
-    const char* ext1,
-    const char* ext2)
-{
-    std::string::size_type pos = pFile.find_last_of('.');
-
-    // no file extension - can't read
-    if( pos == std::string::npos)
-        return false;
-
-    const char* ext_real = & pFile[ pos+1 ];
-    if( !ASSIMP_stricmp(ext_real,ext0) )
-        return true;
-
-    // check for other, optional, file extensions
-    if (ext1 && !ASSIMP_stricmp(ext_real,ext1))
-        return true;
-
-    if (ext2 && !ASSIMP_stricmp(ext_real,ext2))
-        return true;
-
-    return false;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get file extension from path
-std::string BaseImporter::GetExtension( const std::string& file ) {
-    std::string::size_type pos = file.find_last_of('.');
-
-    // no file extension at all
-    if (pos == std::string::npos) {
-        return "";
-    }
-
-
-    // thanks to Andy Maloney for the hint
-    std::string ret = file.substr( pos + 1 );
-    std::transform( ret.begin(), ret.end(), ret.begin(), ToLower<char>);
-
-    return ret;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Check for magic bytes at the beginning of the file.
-/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile,
-    const void* _magic, unsigned int num, unsigned int offset, unsigned int size)
-{
-    ai_assert( size <= 16 );
-    ai_assert( _magic );
-
-    if (!pIOHandler) {
-        return false;
-    }
-    union {
-        const char* magic;
-        const uint16_t* magic_u16;
-        const uint32_t* magic_u32;
-    };
-    magic = reinterpret_cast<const char*>(_magic);
-    std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile));
-    if (pStream.get() ) {
-
-        // skip to offset
-        pStream->Seek(offset,aiOrigin_SET);
-
-        // read 'size' characters from the file
-        union {
-            char data[16];
-            uint16_t data_u16[8];
-            uint32_t data_u32[4];
-        };
-        if(size != pStream->Read(data,1,size)) {
-            return false;
-        }
-
-        for (unsigned int i = 0; i < num; ++i) {
-            // also check against big endian versions of tokens with size 2,4
-            // that's just for convenience, the chance that we cause conflicts
-            // is quite low and it can save some lines and prevent nasty bugs
-            if (2 == size) {
-                uint16_t rev = *magic_u16;
-                ByteSwap::Swap(&rev);
-                if (data_u16[0] == *magic_u16 || data_u16[0] == rev) {
-                    return true;
-                }
-            }
-            else if (4 == size) {
-                uint32_t rev = *magic_u32;
-                ByteSwap::Swap(&rev);
-                if (data_u32[0] == *magic_u32 || data_u32[0] == rev) {
-                    return true;
-                }
-            }
-            else {
-                // any length ... just compare
-                if(!memcmp(magic,data,size)) {
-                    return true;
-                }
-            }
-            magic += size;
-        }
-    }
-    return false;
-}
-
-#ifdef ASSIMP_USE_HUNTER
-#  include <utf8/utf8.h>
-#else
-#  include "../contrib/utf8cpp/source/utf8.h"
-#endif
-
-// ------------------------------------------------------------------------------------------------
-// Convert to UTF8 data
-void BaseImporter::ConvertToUTF8(std::vector<char>& data)
-{
-    //ConversionResult result;
-    if(data.size() < 8) {
-        throw DeadlyImportError("File is too small");
-    }
-
-    // UTF 8 with BOM
-    if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) {
-        ASSIMP_LOG_DEBUG("Found UTF-8 BOM ...");
-
-        std::copy(data.begin()+3,data.end(),data.begin());
-        data.resize(data.size()-3);
-        return;
-    }
-    
-    
-    // UTF 32 BE with BOM
-    if(*((uint32_t*)&data.front()) == 0xFFFE0000) {
-
-        // swap the endianness ..
-        for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) {
-            AI_SWAP4P(p);
-        }
-    }
-
-    // UTF 32 LE with BOM
-    if(*((uint32_t*)&data.front()) == 0x0000FFFE) {
-        ASSIMP_LOG_DEBUG("Found UTF-32 BOM ...");
-
-        std::vector<char> output;
-        int *ptr = (int*)&data[ 0 ];
-        int *end = ptr + ( data.size() / sizeof(int) ) +1;
-        utf8::utf32to8( ptr, end, back_inserter(output));
-        return;
-    }
-
-    // UTF 16 BE with BOM
-    if(*((uint16_t*)&data.front()) == 0xFFFE) {
-
-        // swap the endianness ..
-        for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) {
-            ByteSwap::Swap2(p);
-        }
-    }
-
-    // UTF 16 LE with BOM
-    if(*((uint16_t*)&data.front()) == 0xFEFF) {
-        ASSIMP_LOG_DEBUG("Found UTF-16 BOM ...");
-
-        std::vector<unsigned char> output;
-        utf8::utf16to8(data.begin(), data.end(), back_inserter(output));
-        return;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Convert to UTF8 data to ISO-8859-1
-void BaseImporter::ConvertUTF8toISO8859_1(std::string& data)
-{
-    size_t size = data.size();
-    size_t i = 0, j = 0;
-
-    while(i < size) {
-        if ((unsigned char) data[i] < (size_t) 0x80) {
-            data[j] = data[i];
-        } else if(i < size - 1) {
-            if((unsigned char) data[i] == 0xC2) {
-                data[j] = data[++i];
-            } else if((unsigned char) data[i] == 0xC3) {
-                data[j] = ((unsigned char) data[++i] + 0x40);
-            } else {
-                std::stringstream stream;
-                stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1.";
-                ASSIMP_LOG_ERROR( stream.str() );
-
-                data[j++] = data[i++];
-                data[j] = data[i];
-            }
-        } else {
-            ASSIMP_LOG_ERROR("UTF8 code but only one character remaining");
-
-            data[j] = data[i];
-        }
-
-        i++; j++;
-    }
-
-    data.resize(j);
-}
-
-// ------------------------------------------------------------------------------------------------
-void BaseImporter::TextFileToBuffer(IOStream* stream,
-    std::vector<char>& data,
-    TextFileMode mode)
-{
-    ai_assert(nullptr != stream);
-
-    const size_t fileSize = stream->FileSize();
-    if (mode == FORBID_EMPTY) {
-        if(!fileSize) {
-            throw DeadlyImportError("File is empty");
-        }
-    }
-
-    data.reserve(fileSize+1);
-    data.resize(fileSize);
-    if(fileSize > 0) {
-        if(fileSize != stream->Read( &data[0], 1, fileSize)) {
-            throw DeadlyImportError("File read error");
-        }
-
-        ConvertToUTF8(data);
-    }
-
-    // append a binary zero to simplify string parsing
-    data.push_back(0);
-}
-
-// ------------------------------------------------------------------------------------------------
-namespace Assimp {
-    // Represents an import request
-    struct LoadRequest {
-        LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id)
-        : file(_file)
-        , flags(_flags)
-        , refCnt(1)
-        , scene(NULL)
-        , loaded(false)
-        , id(_id) {
-            if ( _map ) {
-                map = *_map;
-            }
-        }
-
-        bool operator== ( const std::string& f ) const {
-            return file == f;
-        }
-
-        const std::string        file;
-        unsigned int             flags;
-        unsigned int             refCnt;
-        aiScene                 *scene;
-        bool                     loaded;
-        BatchLoader::PropertyMap map;
-        unsigned int             id;
-    };
-}
-
-// ------------------------------------------------------------------------------------------------
-// BatchLoader::pimpl data structure
-struct Assimp::BatchData {
-    BatchData( IOSystem* pIO, bool validate )
-    : pIOSystem( pIO )
-    , pImporter( nullptr )
-    , next_id(0xffff)
-    , validate( validate ) {
-        ai_assert( nullptr != pIO );
-        
-        pImporter = new Importer();
-        pImporter->SetIOHandler( pIO );
-    }
-
-    ~BatchData() {
-        pImporter->SetIOHandler( nullptr ); /* get pointer back into our possession */
-        delete pImporter;
-    }
-
-    // IO system to be used for all imports
-    IOSystem* pIOSystem;
-
-    // Importer used to load all meshes
-    Importer* pImporter;
-
-    // List of all imports
-    std::list<LoadRequest> requests;
-
-    // Base path
-    std::string pathBase;
-
-    // Id for next item
-    unsigned int next_id;
-
-    // Validation enabled state
-    bool validate;
-};
-
-typedef std::list<LoadRequest>::iterator LoadReqIt;
-
-// ------------------------------------------------------------------------------------------------
-BatchLoader::BatchLoader(IOSystem* pIO, bool validate ) {
-    ai_assert(nullptr != pIO);
-
-    m_data = new BatchData( pIO, validate );
-}
-
-// ------------------------------------------------------------------------------------------------
-BatchLoader::~BatchLoader()
-{
-    // delete all scenes what have not been polled by the user
-    for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
-        delete (*it).scene;
-    }
-    delete m_data;
-}
-
-// ------------------------------------------------------------------------------------------------
-void BatchLoader::setValidation( bool enabled ) {
-    m_data->validate = enabled;
-}
-
-// ------------------------------------------------------------------------------------------------
-bool BatchLoader::getValidation() const {
-    return m_data->validate;
-}
-
-// ------------------------------------------------------------------------------------------------
-unsigned int BatchLoader::AddLoadRequest(const std::string& file,
-    unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
-{
-    ai_assert(!file.empty());
-
-    // check whether we have this loading request already
-    for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it)  {
-        // Call IOSystem's path comparison function here
-        if ( m_data->pIOSystem->ComparePaths((*it).file,file)) {
-            if (map) {
-                if ( !( ( *it ).map == *map ) ) {
-                    continue;
-                }
-            }
-            else if ( !( *it ).map.empty() ) {
-                continue;
-            }
-
-            (*it).refCnt++;
-            return (*it).id;
-        }
-    }
-
-    // no, we don't have it. So add it to the queue ...
-    m_data->requests.push_back(LoadRequest(file,steps,map, m_data->next_id));
-    return m_data->next_id++;
-}
-
-// ------------------------------------------------------------------------------------------------
-aiScene* BatchLoader::GetImport( unsigned int which )
-{
-    for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
-        if ((*it).id == which && (*it).loaded)  {
-            aiScene* sc = (*it).scene;
-            if (!(--(*it).refCnt))  {
-                m_data->requests.erase(it);
-            }
-            return sc;
-        }
-    }
-    return nullptr;
-}
-
-
-
-// ------------------------------------------------------------------------------------------------
-void BatchLoader::LoadAll()
-{
-    // no threaded implementation for the moment
-    for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
-        // force validation in debug builds
-        unsigned int pp = (*it).flags;
-        if ( m_data->validate ) {
-            pp |= aiProcess_ValidateDataStructure;
-        }
-
-        // setup config properties if necessary
-        ImporterPimpl* pimpl = m_data->pImporter->Pimpl();
-        pimpl->mFloatProperties  = (*it).map.floats;
-        pimpl->mIntProperties    = (*it).map.ints;
-        pimpl->mStringProperties = (*it).map.strings;
-        pimpl->mMatrixProperties = (*it).map.matrices;
-
-        if (!DefaultLogger::isNullLogger())
-        {
-            ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%");
-            ASSIMP_LOG_INFO_F("File: ", (*it).file);
-        }
-        m_data->pImporter->ReadFile((*it).file,pp);
-        (*it).scene = m_data->pImporter->GetOrphanedScene();
-        (*it).loaded = true;
-
-        ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%");
-    }
-}

+ 0 - 107
thirdparty/assimp/code/Common/BaseProcess.cpp

@@ -1,107 +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 Implementation of BaseProcess */
-
-#include <assimp/BaseImporter.h>
-#include "BaseProcess.h"
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/scene.h>
-#include "Importer.h"
-
-using namespace Assimp;
-
-// ------------------------------------------------------------------------------------------------
-// Constructor to be privately used by Importer
-BaseProcess::BaseProcess() AI_NO_EXCEPT
-: shared()
-, progress()
-{
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-BaseProcess::~BaseProcess()
-{
-    // nothing to do here
-}
-
-// ------------------------------------------------------------------------------------------------
-void BaseProcess::ExecuteOnScene( Importer* pImp)
-{
-    ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene);
-
-    progress = pImp->GetProgressHandler();
-    ai_assert(progress);
-
-    SetupProperties( pImp );
-
-    // catch exceptions thrown inside the PostProcess-Step
-    try
-    {
-        Execute(pImp->Pimpl()->mScene);
-
-    } catch( const std::exception& err )    {
-
-        // extract error description
-        pImp->Pimpl()->mErrorString = err.what();
-        ASSIMP_LOG_ERROR(pImp->Pimpl()->mErrorString);
-
-        // and kill the partially imported data
-        delete pImp->Pimpl()->mScene;
-        pImp->Pimpl()->mScene = nullptr;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-void BaseProcess::SetupProperties(const Importer* /*pImp*/)
-{
-    // the default implementation does nothing
-}
-
-// ------------------------------------------------------------------------------------------------
-bool BaseProcess::RequireVerboseFormat() const
-{
-    return true;
-}
-

+ 0 - 290
thirdparty/assimp/code/Common/BaseProcess.h

@@ -1,290 +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 Base class of all import post processing steps */
-#ifndef INCLUDED_AI_BASEPROCESS_H
-#define INCLUDED_AI_BASEPROCESS_H
-
-#include <map>
-#include <assimp/GenericProperty.h>
-
-struct aiScene;
-
-namespace Assimp    {
-
-class Importer;
-
-// ---------------------------------------------------------------------------
-/** Helper class to allow post-processing steps to interact with each other.
- *
- *  The class maintains a simple property list that can be used by pp-steps
- *  to provide additional information to other steps. This is primarily
- *  intended for cross-step optimizations.
- */
-class SharedPostProcessInfo
-{
-public:
-
-    struct Base
-    {
-        virtual ~Base()
-        {}
-    };
-
-    //! Represents data that is allocated on the heap, thus needs to be deleted
-    template <typename T>
-    struct THeapData : public Base
-    {
-        explicit THeapData(T* in)
-            : data (in)
-        {}
-
-        ~THeapData()
-        {
-            delete data;
-        }
-        T* data;
-    };
-
-    //! Represents static, by-value data not allocated on the heap
-    template <typename T>
-    struct TStaticData : public Base
-    {
-        explicit TStaticData(T in)
-            : data (in)
-        {}
-
-        ~TStaticData()
-        {}
-
-        T data;
-    };
-
-    // some typedefs for cleaner code
-    typedef unsigned int KeyType;
-    typedef std::map<KeyType, Base*>  PropertyMap;
-
-public:
-
-    //! Destructor
-    ~SharedPostProcessInfo()
-    {
-        Clean();
-    }
-
-    //! Remove all stored properties from the table
-    void Clean()
-    {
-        // invoke the virtual destructor for all stored properties
-        for (PropertyMap::iterator it = pmap.begin(), end = pmap.end();
-             it != end; ++it)
-        {
-            delete (*it).second;
-        }
-        pmap.clear();
-    }
-
-    //! Add a heap property to the list
-    template <typename T>
-    void AddProperty( const char* name, T* in ){
-        AddProperty(name,(Base*)new THeapData<T>(in));
-    }
-
-    //! Add a static by-value property to the list
-    template <typename T>
-    void AddProperty( const char* name, T in ){
-        AddProperty(name,(Base*)new TStaticData<T>(in));
-    }
-
-
-    //! Get a heap property
-    template <typename T>
-    bool GetProperty( const char* name, T*& out ) const
-    {
-        THeapData<T>* t = (THeapData<T>*)GetPropertyInternal(name);
-        if(!t)
-        {
-            out = NULL;
-            return false;
-        }
-        out = t->data;
-        return true;
-    }
-
-    //! Get a static, by-value property
-    template <typename T>
-    bool GetProperty( const char* name, T& out ) const
-    {
-        TStaticData<T>* t = (TStaticData<T>*)GetPropertyInternal(name);
-        if(!t)return false;
-        out = t->data;
-        return true;
-    }
-
-    //! Remove a property of a specific type
-    void RemoveProperty( const char* name)  {
-        SetGenericPropertyPtr<Base>(pmap,name,NULL);
-    }
-
-private:
-
-    void AddProperty( const char* name, Base* data) {
-        SetGenericPropertyPtr<Base>(pmap,name,data);
-    }
-
-    Base* GetPropertyInternal( const char* name) const  {
-        return GetGenericProperty<Base*>(pmap,name,NULL);
-    }
-
-private:
-
-    //! Map of all stored properties
-    PropertyMap pmap;
-};
-
-#if 0
-
-// ---------------------------------------------------------------------------
-/** @brief Represents a dependency table for a postprocessing steps.
- *
- *  For future use.
- */
- struct PPDependencyTable
- {
-     unsigned int execute_me_before_these;
-     unsigned int execute_me_after_these;
-     unsigned int only_if_these_are_not_specified;
-     unsigned int mutually_exclusive_with;
- };
-
-#endif
-
-
-#define AI_SPP_SPATIAL_SORT "$Spat"
-
-// ---------------------------------------------------------------------------
-/** The BaseProcess defines a common interface for all post processing steps.
- * A post processing step is run after a successful import if the caller
- * specified the corresponding flag when calling ReadFile().
- * Enum #aiPostProcessSteps defines which flags are available.
- * After a successful import the Importer iterates over its internal array
- * of processes and calls IsActive() on each process to evaluate if the step
- * should be executed. If the function returns true, the class' Execute()
- * function is called subsequently.
- */
-class ASSIMP_API_WINONLY BaseProcess {
-    friend class Importer;
-
-public:
-    /** Constructor to be privately used by Importer */
-    BaseProcess() AI_NO_EXCEPT;
-
-    /** Destructor, private as well */
-    virtual ~BaseProcess();
-
-    // -------------------------------------------------------------------
-    /** Returns whether the processing step is present in the given flag.
-     * @param pFlags The processing flags the importer was called with. A
-     *   bitwise combination of #aiPostProcessSteps.
-     * @return true if the process is present in this flag fields,
-     *   false if not.
-    */
-    virtual bool IsActive( unsigned int pFlags) const = 0;
-
-    // -------------------------------------------------------------------
-    /** Check whether this step expects its input vertex data to be
-     *  in verbose format. */
-    virtual bool RequireVerboseFormat() const;
-
-    // -------------------------------------------------------------------
-    /** Executes the post processing step on the given imported data.
-    * The function deletes the scene if the postprocess step fails (
-    * the object pointer will be set to NULL).
-    * @param pImp Importer instance (pImp->mScene must be valid)
-    */
-    void ExecuteOnScene( Importer* pImp);
-
-    // -------------------------------------------------------------------
-    /** Called prior to ExecuteOnScene().
-    * The function is a request to the process to update its configuration
-    * basing on the Importer's configuration property list.
-    */
-    virtual void SetupProperties(const Importer* pImp);
-
-    // -------------------------------------------------------------------
-    /** Executes the post processing step on the given imported data.
-    * A process should throw an ImportErrorException* if it fails.
-    * This method must be implemented by deriving classes.
-    * @param pScene The imported data to work at.
-    */
-    virtual void Execute( aiScene* pScene) = 0;
-
-
-    // -------------------------------------------------------------------
-    /** Assign a new SharedPostProcessInfo to the step. This object
-     *  allows multiple postprocess steps to share data.
-     * @param sh May be NULL
-    */
-    inline void SetSharedData(SharedPostProcessInfo* sh)    {
-        shared = sh;
-    }
-
-    // -------------------------------------------------------------------
-    /** Get the shared data that is assigned to the step.
-    */
-    inline SharedPostProcessInfo* GetSharedData()   {
-        return shared;
-    }
-
-protected:
-
-    /** See the doc of #SharedPostProcessInfo for more details */
-    SharedPostProcessInfo* shared;
-
-    /** Currently active progress handler */
-    ProgressHandler* progress;
-};
-
-
-} // end of namespace Assimp
-
-#endif // AI_BASEPROCESS_H_INC

+ 0 - 155
thirdparty/assimp/code/Common/Bitmap.cpp

@@ -1,155 +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 Bitmap.cpp
- *  @brief Defines bitmap format helper for textures
- *
- * Used for file formats which embed their textures into the model file.
- */
-
-
-#include <assimp/Bitmap.h>
-#include <assimp/texture.h>
-#include <assimp/IOStream.hpp>
-#include <assimp/ByteSwapper.h>
-
-namespace Assimp {
-
-    void Bitmap::Save(aiTexture* texture, IOStream* file) {
-        if(file != NULL) {
-            Header header;
-            DIB dib;
-
-            dib.size = DIB::dib_size;
-            dib.width = texture->mWidth;
-            dib.height = texture->mHeight;
-            dib.planes = 1;
-            dib.bits_per_pixel = 8 * mBytesPerPixel;
-            dib.compression = 0;
-            dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height;
-            dib.x_resolution = 0;
-            dib.y_resolution = 0;
-            dib.nb_colors = 0;
-            dib.nb_important_colors = 0;
-
-            header.type = 0x4D42; // 'BM'
-            header.offset = Header::header_size + DIB::dib_size;
-            header.size = header.offset + dib.image_size;
-            header.reserved1 = 0;
-            header.reserved2 = 0;
-
-            WriteHeader(header, file);
-            WriteDIB(dib, file);
-            WriteData(texture, file);
-        }
-    }
-
-    template<typename T>
-    inline 
-    std::size_t Copy(uint8_t* data, const T &field) {
-#ifdef AI_BUILD_BIG_ENDIAN
-        T field_swapped=AI_BE(field);
-        std::memcpy(data, &field_swapped, sizeof(field)); return sizeof(field);
-#else
-        std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field);
-#endif
-    }
-
-    void Bitmap::WriteHeader(Header& header, IOStream* file) {
-        uint8_t data[Header::header_size];
-
-        std::size_t offset = 0;
-
-        offset += Copy(&data[offset], header.type);
-        offset += Copy(&data[offset], header.size);
-        offset += Copy(&data[offset], header.reserved1);
-        offset += Copy(&data[offset], header.reserved2);
-                  Copy(&data[offset], header.offset);
-
-        file->Write(data, Header::header_size, 1);
-    }
-
-    void Bitmap::WriteDIB(DIB& dib, IOStream* file) {
-        uint8_t data[DIB::dib_size];
-
-        std::size_t offset = 0;
-
-        offset += Copy(&data[offset], dib.size);
-        offset += Copy(&data[offset], dib.width);
-        offset += Copy(&data[offset], dib.height);
-        offset += Copy(&data[offset], dib.planes);
-        offset += Copy(&data[offset], dib.bits_per_pixel);
-        offset += Copy(&data[offset], dib.compression);
-        offset += Copy(&data[offset], dib.image_size);
-        offset += Copy(&data[offset], dib.x_resolution);
-        offset += Copy(&data[offset], dib.y_resolution);
-        offset += Copy(&data[offset], dib.nb_colors);
-                  Copy(&data[offset], dib.nb_important_colors);
-
-        file->Write(data, DIB::dib_size, 1);
-    }
-
-    void Bitmap::WriteData(aiTexture* texture, IOStream* file) {
-        static const std::size_t padding_offset = 4;
-        static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0};
-
-        unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset;
-        uint8_t pixel[mBytesPerPixel];
-
-        for(std::size_t i = 0; i < texture->mHeight; ++i) {
-            for(std::size_t j = 0; j < texture->mWidth; ++j) {
-                const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format
-
-                pixel[0] = texel.r;
-                pixel[1] = texel.g;
-                pixel[2] = texel.b;
-                pixel[3] = texel.a;
-
-                file->Write(pixel, mBytesPerPixel, 1);
-            }
-
-            file->Write(padding_data, padding, 1);
-        }
-    }
-
-}

+ 0 - 88
thirdparty/assimp/code/Common/CreateAnimMesh.cpp

@@ -1,88 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (C) 2016 The Qt Company Ltd.
-Copyright (c) 2006-2012, 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 <assimp/CreateAnimMesh.h>
-
-namespace Assimp    {
-
-aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh)
-{
-    aiAnimMesh *animesh = new aiAnimMesh;
-    animesh->mNumVertices = mesh->mNumVertices;
-    if (mesh->mVertices) {
-        animesh->mVertices = new aiVector3D[animesh->mNumVertices];
-        std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D));
-    }
-    if (mesh->mNormals) {
-        animesh->mNormals = new aiVector3D[animesh->mNumVertices];
-        std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D));
-    }
-    if (mesh->mTangents) {
-        animesh->mTangents = new aiVector3D[animesh->mNumVertices];
-        std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D));
-    }
-    if (mesh->mBitangents) {
-        animesh->mBitangents = new aiVector3D[animesh->mNumVertices];
-        std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D));
-    }
-
-    for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
-        if (mesh->mColors[i]) {
-            animesh->mColors[i] = new aiColor4D[animesh->mNumVertices];
-            std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D));
-        } else {
-            animesh->mColors[i] = NULL;
-        }
-    }
-
-    for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
-        if (mesh->mTextureCoords[i]) {
-            animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices];
-            std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D));
-        } else {
-            animesh->mTextureCoords[i] = NULL;
-        }
-    }
-    return animesh;
-}
-
-} // end of namespace Assimp

+ 0 - 154
thirdparty/assimp/code/Common/DefaultIOStream.cpp

@@ -1,154 +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  DefaultIOStream.cpp
- *  @brief Default File I/O implementation for #Importer
- */
-
-
-#include <assimp/ai_assert.h>
-#include <assimp/DefaultIOStream.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-using namespace Assimp;
-
-// ----------------------------------------------------------------------------------
-DefaultIOStream::~DefaultIOStream()
-{
-    if (mFile) {
-        ::fclose(mFile);
-        mFile = nullptr;
-    }
-}
-
-// ----------------------------------------------------------------------------------
-size_t DefaultIOStream::Read(void* pvBuffer,
-    size_t pSize,
-    size_t pCount)
-{
-    ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
-    return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0);
-}
-
-// ----------------------------------------------------------------------------------
-size_t DefaultIOStream::Write(const void* pvBuffer,
-    size_t pSize,
-    size_t pCount)
-{
-    ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
-    return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0);
-}
-
-// ----------------------------------------------------------------------------------
-aiReturn DefaultIOStream::Seek(size_t pOffset,
-     aiOrigin pOrigin)
-{
-    if (!mFile) {
-        return AI_FAILURE;
-    }
-
-    // Just to check whether our enum maps one to one with the CRT constants
-    static_assert(aiOrigin_CUR == SEEK_CUR &&
-        aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET, "aiOrigin_CUR == SEEK_CUR && \
-        aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET");
-
-    // do the seek
-    return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE);
-}
-
-// ----------------------------------------------------------------------------------
-size_t DefaultIOStream::Tell() const
-{
-    if (!mFile) {
-        return 0;
-    }
-    return ::ftell(mFile);
-}
-
-// ----------------------------------------------------------------------------------
-size_t DefaultIOStream::FileSize() const
-{
-    if (! mFile || mFilename.empty()) {
-        return 0;
-    }
-
-    if (SIZE_MAX == mCachedSize ) {
-
-        // Although fseek/ftell would allow us to reuse the existing file handle here,
-        // it is generally unsafe because:
-        //  - For binary streams, it is not technically well-defined
-        //  - For text files the results are meaningless
-        // That's why we use the safer variant fstat here.
-        //
-        // See here for details:
-        // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file
-#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
-        struct __stat64 fileStat;
-        //using fileno + fstat avoids having to handle the filename
-        int err = _fstat64(  _fileno(mFile), &fileStat );
-        if (0 != err)
-            return 0;
-        mCachedSize = (size_t) (fileStat.st_size);
-#elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__
-        struct stat fileStat;
-        int err = stat(mFilename.c_str(), &fileStat );
-        if (0 != err)
-            return 0;
-        const unsigned long long cachedSize = fileStat.st_size;
-        mCachedSize = static_cast< size_t >( cachedSize );
-#else
-#   error "Unknown platform"
-#endif
-    }
-    return mCachedSize;
-}
-
-// ----------------------------------------------------------------------------------
-void DefaultIOStream::Flush()
-{
-    if (mFile) {
-        ::fflush(mFile);
-    }
-}
-
-// ----------------------------------------------------------------------------------

+ 0 - 216
thirdparty/assimp/code/Common/DefaultIOSystem.cpp

@@ -1,216 +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 Default implementation of IOSystem using the standard C file functions */
-
-#include <assimp/StringComparison.h>
-
-#include <assimp/DefaultIOSystem.h>
-#include <assimp/DefaultIOStream.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/ai_assert.h>
-#include <stdlib.h>
-
-#ifdef __unix__
-#include <sys/param.h>
-#include <stdlib.h>
-#endif
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-using namespace Assimp;
-
-#ifdef _WIN32
-static std::wstring Utf8ToWide(const char* in)
-{
-    int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
-    // size includes terminating null; std::wstring adds null automatically
-    std::wstring out(static_cast<size_t>(size) - 1, L'\0');
-    MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
-    return out;
-}
-
-static std::string WideToUtf8(const wchar_t* in)
-{
-    int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
-    // size includes terminating null; std::string adds null automatically
-    std::string out(static_cast<size_t>(size) - 1, '\0');
-    WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
-    return out;
-}
-#endif
-
-// ------------------------------------------------------------------------------------------------
-// Tests for the existence of a file at the given path.
-bool DefaultIOSystem::Exists(const char* pFile) const
-{
-#ifdef _WIN32
-    struct __stat64 filestat;
-    if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) {
-        return false;
-    }
-#else
-    FILE* file = ::fopen(pFile, "rb");
-    if (!file)
-        return false;
-
-    ::fclose(file);
-#endif
-    return true;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Open a new file with a given path.
-IOStream* DefaultIOSystem::Open(const char* strFile, const char* strMode)
-{
-    ai_assert(strFile != nullptr);
-    ai_assert(strMode != nullptr);
-    FILE* file;
-#ifdef _WIN32
-    file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
-#else
-    file = ::fopen(strFile, strMode);
-#endif
-    if (!file)
-        return nullptr;
-
-    return new DefaultIOStream(file, strFile);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Closes the given file and releases all resources associated with it.
-void DefaultIOSystem::Close(IOStream* pFile)
-{
-    delete pFile;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns the operation specific directory separator
-char DefaultIOSystem::getOsSeparator() const
-{
-#ifndef _WIN32
-    return '/';
-#else
-    return '\\';
-#endif
-}
-
-// ------------------------------------------------------------------------------------------------
-// IOSystem default implementation (ComparePaths isn't a pure virtual function)
-bool IOSystem::ComparePaths(const char* one, const char* second) const
-{
-    return !ASSIMP_stricmp(one, second);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Convert a relative path into an absolute path
-inline static std::string MakeAbsolutePath(const char* in)
-{
-    ai_assert(in);
-    std::string out;
-#ifdef _WIN32
-    wchar_t* ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
-    if (ret) {
-        out = WideToUtf8(ret);
-        free(ret);
-    }
-#else
-    char* ret = realpath(in, nullptr);
-    if (ret) {
-        out = ret;
-        free(ret);
-    }
-#endif
-    if (!ret) {
-        // preserve the input path, maybe someone else is able to fix
-        // the path before it is accessed (e.g. our file system filter)
-        ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
-        out = in;
-    }
-    return out;
-}
-
-// ------------------------------------------------------------------------------------------------
-// DefaultIOSystem's more specialized implementation
-bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
-{
-    // chances are quite good both paths are formatted identically,
-    // so we can hopefully return here already
-    if (!ASSIMP_stricmp(one, second))
-        return true;
-
-    std::string temp1 = MakeAbsolutePath(one);
-    std::string temp2 = MakeAbsolutePath(second);
-
-    return !ASSIMP_stricmp(temp1, temp2);
-}
-
-// ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::fileName(const std::string& path)
-{
-    std::string ret = path;
-    std::size_t last = ret.find_last_of("\\/");
-    if (last != std::string::npos) ret = ret.substr(last + 1);
-    return ret;
-}
-
-// ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::completeBaseName(const std::string& path)
-{
-    std::string ret = fileName(path);
-    std::size_t pos = ret.find_last_of('.');
-    if (pos != std::string::npos) ret = ret.substr(0, pos);
-    return ret;
-}
-
-// ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::absolutePath(const std::string& path)
-{
-    std::string ret = path;
-    std::size_t last = ret.find_last_of("\\/");
-    if (last != std::string::npos) ret = ret.substr(0, last);
-    return ret;
-}
-
-// ------------------------------------------------------------------------------------------------

+ 0 - 418
thirdparty/assimp/code/Common/DefaultLogger.cpp

@@ -1,418 +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  DefaultLogger.cpp
- *  @brief Implementation of DefaultLogger (and Logger)
- */
-
-// Default log streams
-#include "Win32DebugLogStream.h"
-#include "StdOStreamLogStream.h"
-#include "FileLogStream.h"
-#include <assimp/StringUtils.h>
-
-#include <assimp/DefaultIOSystem.h>
-#include <assimp/NullLogger.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/ai_assert.h>
-#include <iostream>
-#include <stdio.h>
-
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-#   include <thread>
-#   include <mutex>
-    std::mutex loggerMutex;
-#endif
-
-namespace Assimp    {
-
-// ----------------------------------------------------------------------------------
-NullLogger DefaultLogger::s_pNullLogger;
-Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger;
-
-static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
-
-// ----------------------------------------------------------------------------------
-// Represents a log-stream + its error severity
-struct LogStreamInfo {
-    unsigned int  m_uiErrorSeverity;
-    LogStream    *m_pStream;
-
-    // Constructor
-    LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) :
-        m_uiErrorSeverity( uiErrorSev ),
-        m_pStream( pStream ) {
-        // empty
-    }
-
-    // Destructor
-    ~LogStreamInfo() {
-        delete m_pStream;
-    }
-};
-
-// ----------------------------------------------------------------------------------
-// Construct a default log stream
-LogStream* LogStream::createDefaultStream(aiDefaultLogStream    streams,
-    const char* name /*= "AssimpLog.txt"*/,
-    IOSystem* io            /*= NULL*/)
-{
-    switch (streams)
-    {
-        // This is a platform-specific feature
-    case aiDefaultLogStream_DEBUGGER:
-#ifdef WIN32
-        return new Win32DebugLogStream();
-#else
-        return nullptr;
-#endif
-
-        // Platform-independent default streams
-    case aiDefaultLogStream_STDERR:
-        return new StdOStreamLogStream(std::cerr);
-    case aiDefaultLogStream_STDOUT:
-        return new StdOStreamLogStream(std::cout);
-    case aiDefaultLogStream_FILE:
-        return (name && *name ? new FileLogStream(name,io) : nullptr );
-    default:
-        // We don't know this default log stream, so raise an assertion
-        ai_assert(false);
-
-    };
-
-    // For compilers without dead code path detection
-    return NULL;
-}
-
-// ----------------------------------------------------------------------------------
-//  Creates the only singleton instance
-Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
-    LogSeverity severity                       /*= NORMAL*/,
-    unsigned int defStreams                    /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/,
-    IOSystem* io                               /*= NULL*/) {
-    // enter the mutex here to avoid concurrency problems
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-    std::lock_guard<std::mutex> lock(loggerMutex);
-#endif
-
-    if ( m_pLogger && !isNullLogger() ) {
-        delete m_pLogger;
-    }
-
-    m_pLogger = new DefaultLogger( severity );
-
-    // Attach default log streams
-    // Stream the log to the MSVC debugger?
-    if ( defStreams & aiDefaultLogStream_DEBUGGER ) {
-        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) );
-    }
-
-    // Stream the log to COUT?
-    if ( defStreams & aiDefaultLogStream_STDOUT ) {
-        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) );
-    }
-
-    // Stream the log to CERR?
-    if ( defStreams & aiDefaultLogStream_STDERR ) {
-        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) );
-    }
-
-    // Stream the log to a file
-    if ( defStreams & aiDefaultLogStream_FILE && name && *name ) {
-        m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) );
-    }
-
-    return m_pLogger;
-}
-
-// ----------------------------------------------------------------------------------
-void Logger::debug(const char* message) {
-
-    // SECURITY FIX: otherwise it's easy to produce overruns since
-    // sometimes importers will include data from the input file
-    // (i.e. node names) in their messages.
-    if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
-        return;
-    }
-    return OnDebug(message);
-}
-
-// ----------------------------------------------------------------------------------
-void Logger::info(const char* message)  {
-
-    // SECURITY FIX: see above
-    if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
-        return;
-    }
-    return OnInfo(message);
-}
-
-// ----------------------------------------------------------------------------------
-void Logger::warn(const char* message)  {
-
-    // SECURITY FIX: see above
-    if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
-        return;
-    }
-    return OnWarn(message);
-}
-
-// ----------------------------------------------------------------------------------
-void Logger::error(const char* message) {
-    // SECURITY FIX: see above
-    if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
-        return;
-    }
-    return OnError(message);
-}
-
-// ----------------------------------------------------------------------------------
-void DefaultLogger::set( Logger *logger ) {
-    // enter the mutex here to avoid concurrency problems
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-    std::lock_guard<std::mutex> lock(loggerMutex);
-#endif
-
-    if ( nullptr == logger ) {
-        logger = &s_pNullLogger;
-    }
-    if ( nullptr != m_pLogger && !isNullLogger() ) {
-        delete m_pLogger;
-    }
-
-    DefaultLogger::m_pLogger = logger;
-}
-
-// ----------------------------------------------------------------------------------
-bool DefaultLogger::isNullLogger() {
-    return m_pLogger == &s_pNullLogger;
-}
-
-// ----------------------------------------------------------------------------------
-Logger *DefaultLogger::get() {
-    return m_pLogger;
-}
-
-// ----------------------------------------------------------------------------------
-//  Kills the only instance
-void DefaultLogger::kill() {
-    // enter the mutex here to avoid concurrency problems
-#ifndef ASSIMP_BUILD_SINGLETHREADED
-    std::lock_guard<std::mutex> lock(loggerMutex);
-#endif
-
-	if ( m_pLogger == &s_pNullLogger ) {
-		return;
-	}
-    delete m_pLogger;
-    m_pLogger = &s_pNullLogger;
-}
-
-// ----------------------------------------------------------------------------------
-//  Debug message
-void DefaultLogger::OnDebug( const char* message ) {
-    if ( m_Severity == Logger::NORMAL ) {
-        return;
-    }
-
-	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
-	char msg[Size];
-	ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message);
-
-    WriteToStreams( msg, Logger::Debugging );
-}
-
-// ----------------------------------------------------------------------------------
-//  Logs an info
-void DefaultLogger::OnInfo( const char* message ){
-	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
-	char msg[Size];
-    ai_snprintf(msg, Size, "Info,  T%u: %s", GetThreadID(), message );
-
-    WriteToStreams( msg , Logger::Info );
-}
-
-// ----------------------------------------------------------------------------------
-//  Logs a warning
-void DefaultLogger::OnWarn( const char* message ) {
-	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
-	char msg[Size];
-	ai_snprintf(msg, Size, "Warn,  T%u: %s", GetThreadID(), message );
-
-    WriteToStreams( msg, Logger::Warn );
-}
-
-// ----------------------------------------------------------------------------------
-//  Logs an error
-void DefaultLogger::OnError( const char* message ) {
-	static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
-	char msg[ Size ];
-    ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message );
-
-    WriteToStreams( msg, Logger::Err );
-}
-
-// ----------------------------------------------------------------------------------
-//  Will attach a new stream
-bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) {
-    if ( nullptr == pStream ) {
-        return false;
-    }
-
-    if (0 == severity)  {
-        severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
-    }
-
-    for ( StreamIt it = m_StreamArray.begin();
-        it != m_StreamArray.end();
-        ++it )
-    {
-        if ( (*it)->m_pStream == pStream ) {
-            (*it)->m_uiErrorSeverity |= severity;
-            return true;
-        }
-    }
-
-    LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
-    m_StreamArray.push_back( pInfo );
-    return true;
-}
-
-// ----------------------------------------------------------------------------------
-//  Detach a stream
-bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) {
-    if ( nullptr == pStream ) {
-        return false;
-    }
-
-    if (0 == severity)  {
-        severity = SeverityAll;
-    }
-
-    bool res( false );
-    for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
-        if ( (*it)->m_pStream == pStream ) {
-            (*it)->m_uiErrorSeverity &= ~severity;
-            if ( (*it)->m_uiErrorSeverity == 0 ) {
-                // don't delete the underlying stream 'cause the caller gains ownership again
-                (**it).m_pStream = nullptr;
-                delete *it;
-                m_StreamArray.erase( it );
-                res = true;
-                break;
-            }
-            return true;
-        }
-    }
-    return res;
-}
-
-// ----------------------------------------------------------------------------------
-//  Constructor
-DefaultLogger::DefaultLogger(LogSeverity severity)
-    :   Logger  ( severity )
-    ,   noRepeatMsg (false)
-    ,   lastLen( 0 ) {
-    lastMsg[0] = '\0';
-}
-
-// ----------------------------------------------------------------------------------
-//  Destructor
-DefaultLogger::~DefaultLogger() {
-    for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
-        // also frees the underlying stream, we are its owner.
-        delete *it;
-    }
-}
-
-// ----------------------------------------------------------------------------------
-//  Writes message to stream
-void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) {
-    ai_assert(nullptr != message);
-
-    // Check whether this is a repeated message
-    if (! ::strncmp( message,lastMsg, lastLen-1))
-    {
-        if (!noRepeatMsg)
-        {
-            noRepeatMsg = true;
-            message = "Skipping one or more lines with the same contents\n";
-        }
-        else return;
-    }
-    else
-    {
-        // append a new-line character to the message to be printed
-        lastLen = ::strlen(message);
-        ::memcpy(lastMsg,message,lastLen+1);
-        ::strcat(lastMsg+lastLen,"\n");
-
-        message = lastMsg;
-        noRepeatMsg = false;
-        ++lastLen;
-    }
-    for ( ConstStreamIt it = m_StreamArray.begin();
-        it != m_StreamArray.end();
-        ++it)
-    {
-        if ( ErrorSev & (*it)->m_uiErrorSeverity )
-            (*it)->m_pStream->write( message);
-    }
-}
-
-// ----------------------------------------------------------------------------------
-//  Returns thread id, if not supported only a zero will be returned.
-unsigned int DefaultLogger::GetThreadID()
-{
-    // fixme: we can get this value via std::threads
-    // std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case.
-#ifdef WIN32
-    return (unsigned int)::GetCurrentThreadId();
-#else
-    return 0; // not supported
-#endif
-}
-
-// ----------------------------------------------------------------------------------
-
-} // !namespace Assimp

+ 0 - 65
thirdparty/assimp/code/Common/DefaultProgressHandler.h

@@ -1,65 +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 ProgressHandler.hpp
- *  @brief Abstract base class 'ProgressHandler'.
- */
-#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
-#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
-
-#include <assimp/ProgressHandler.hpp>
-
-namespace Assimp    {
-
-// ------------------------------------------------------------------------------------
-/** @brief Internal default implementation of the #ProgressHandler interface. */
-class DefaultProgressHandler : public ProgressHandler    {
-
-    virtual bool Update(float /*percentage*/) {
-        return false;
-    }
-
-
-}; // !class DefaultProgressHandler
-} // Namespace Assimp
-
-#endif

+ 0 - 636
thirdparty/assimp/code/Common/Exporter.cpp

@@ -1,636 +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 Exporter.cpp
-
-Assimp export interface. While it's public interface bears many similarities
-to the import interface (in fact, it is largely symmetric), the internal
-implementations differs a lot. Exporters are considered stateless and are
-simple callbacks which we maintain in a global list along with their
-description strings.
-
-Here we implement only the C++ interface (Assimp::Exporter).
-*/
-
-#ifndef ASSIMP_BUILD_NO_EXPORT
-
-#include <assimp/BlobIOSystem.h>
-#include <assimp/SceneCombiner.h>
-#include <assimp/DefaultIOSystem.h>
-#include <assimp/Exporter.hpp>
-#include <assimp/mesh.h>
-#include <assimp/postprocess.h>
-#include <assimp/scene.h>
-#include <assimp/Exceptional.h>
-
-#include "Common/DefaultProgressHandler.h"
-#include "Common/BaseProcess.h"
-#include "Common/ScenePrivate.h"
-#include "PostProcessing/CalcTangentsProcess.h"
-#include "PostProcessing/MakeVerboseFormat.h"
-#include "PostProcessing/JoinVerticesProcess.h"
-#include "PostProcessing/ConvertToLHProcess.h"
-#include "PostProcessing/PretransformVertices.h"
-
-#include <memory>
-
-namespace Assimp {
-
-// PostStepRegistry.cpp
-void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
-
-// ------------------------------------------------------------------------------------------------
-// Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype
-// do not use const, because some exporter need to convert the scene temporary
-void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*);
-void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
-void ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportSceneA3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
-void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
-
-// ------------------------------------------------------------------------------------------------
-// global array of all export formats which Assimp supports in its current build
-Exporter::ExportFormatEntry gExporters[] =
-{
-#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
-    Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_X_EXPORTER
-    Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile,
-        aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
-    Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
-    Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
-        aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
-    Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
-        aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_STL_EXPORTER
-    Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL,
-        aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
-    ),
-    Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary,
-        aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
-    ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
-    Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly,
-        aiProcess_PreTransformVertices
-    ),
-    Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
-        aiProcess_PreTransformVertices
-    ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
-    Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS,
-        aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
-    Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-    Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-    Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-    Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
-    Exporter::ExportFormatEntry( "assbin", "Assimp Binary File", "assbin" , &ExportSceneAssbin, 0 ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
-    Exporter::ExportFormatEntry( "assxml", "Assimp XML Document", "assxml" , &ExportSceneAssxml, 0 ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
-    Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
-    Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ),
-    Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
-    Exporter::ExportFormatEntry( "m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0 ),
-    Exporter::ExportFormatEntry( "a3d", "Model 3D (ascii)",  "m3d", &ExportSceneA3D, 0 ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
-    Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ),
-#endif
-
-#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
-    Exporter::ExportFormatEntry( "assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0)
-#endif
-};
-
-#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))
-
-
-class ExporterPimpl {
-public:
-    ExporterPimpl()
-    : blob()
-    , mIOSystem(new Assimp::DefaultIOSystem())
-    , mIsDefaultIOHandler(true)
-    , mProgressHandler( nullptr )
-    , mIsDefaultProgressHandler( true )
-    , mPostProcessingSteps()
-    , mError()
-    , mExporters() {
-        GetPostProcessingStepInstanceList(mPostProcessingSteps);
-
-        // grab all built-in exporters
-        if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) {
-            mExporters.resize( ASSIMP_NUM_EXPORTERS );
-            std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() );
-        }
-    }
-
-    ~ExporterPimpl() {
-        delete blob;
-
-        // Delete all post-processing plug-ins
-        for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) {
-            delete mPostProcessingSteps[a];
-        }
-        delete mProgressHandler;
-    }
-
-public:
-    aiExportDataBlob* blob;
-    std::shared_ptr< Assimp::IOSystem > mIOSystem;
-    bool mIsDefaultIOHandler;
-
-    /** The progress handler */
-    ProgressHandler *mProgressHandler;
-    bool mIsDefaultProgressHandler;
-
-    /** Post processing steps we can apply at the imported data. */
-    std::vector< BaseProcess* > mPostProcessingSteps;
-
-    /** Last fatal export error */
-    std::string mError;
-
-    /** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */
-    std::vector<Exporter::ExportFormatEntry> mExporters;
-};
-
-} // end of namespace Assimp
-
-using namespace Assimp;
-
-// ------------------------------------------------------------------------------------------------
-Exporter :: Exporter()
-: pimpl(new ExporterPimpl()) {
-    pimpl->mProgressHandler = new DefaultProgressHandler();
-}
-
-// ------------------------------------------------------------------------------------------------
-Exporter::~Exporter() {
-    FreeBlob();
-    delete pimpl;
-}
-
-// ------------------------------------------------------------------------------------------------
-void Exporter::SetIOHandler( IOSystem* pIOHandler) {
-    pimpl->mIsDefaultIOHandler = !pIOHandler;
-    pimpl->mIOSystem.reset(pIOHandler);
-}
-
-// ------------------------------------------------------------------------------------------------
-IOSystem* Exporter::GetIOHandler() const {
-    return pimpl->mIOSystem.get();
-}
-
-// ------------------------------------------------------------------------------------------------
-bool Exporter::IsDefaultIOHandler() const {
-    return pimpl->mIsDefaultIOHandler;
-}
-
-// ------------------------------------------------------------------------------------------------
-void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
-    ai_assert(nullptr != pimpl);
-
-    if ( nullptr == pHandler) {
-        // Release pointer in the possession of the caller
-        pimpl->mProgressHandler = new DefaultProgressHandler();
-        pimpl->mIsDefaultProgressHandler = true;
-        return;
-    }
-
-    if (pimpl->mProgressHandler == pHandler) {
-        return;
-    }
-
-    delete pimpl->mProgressHandler;
-    pimpl->mProgressHandler = pHandler;
-    pimpl->mIsDefaultProgressHandler = false;
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
-                                                unsigned int pPreprocessing, const ExportProperties* pProperties) {
-    if (pimpl->blob) {
-        delete pimpl->blob;
-        pimpl->blob = nullptr;
-    }
-
-    std::shared_ptr<IOSystem> old = pimpl->mIOSystem;
-    BlobIOSystem* blobio = new BlobIOSystem();
-    pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
-
-    if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
-        pimpl->mIOSystem = old;
-        return nullptr;
-    }
-
-    pimpl->blob = blobio->GetBlobChain();
-    pimpl->mIOSystem = old;
-
-    return pimpl->blob;
-}
-
-// ------------------------------------------------------------------------------------------------
-aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
-        unsigned int pPreprocessing, const ExportProperties* pProperties) {
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // when they create scenes from scratch, users will likely create them not in verbose
-    // format. They will likely not be aware that there is a flag in the scene to indicate
-    // this, however. To avoid surprises and bug reports, we check for duplicates in
-    // meshes upfront.
-    const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::IsVerboseFormat(pScene);
-
-    pimpl->mProgressHandler->UpdateFileWrite(0, 4);
-
-    pimpl->mError = "";
-    for (size_t i = 0; i < pimpl->mExporters.size(); ++i) {
-        const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i];
-        if (!strcmp(exp.mDescription.id,pFormatId)) {
-            try {
-                // Always create a full copy of the scene. We might optimize this one day,
-                // but for now it is the most pragmatic way.
-                aiScene* scenecopy_tmp = nullptr;
-                SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
-
-                pimpl->mProgressHandler->UpdateFileWrite(1, 4);
-
-                std::unique_ptr<aiScene> scenecopy(scenecopy_tmp);
-                const ScenePrivateData* const priv = ScenePriv(pScene);
-
-                // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the
-                // original state before the step was applied first. When checking which steps we don't need
-                // to run, those are excluded.
-                const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded;
-
-                // Erase all pp steps that were already applied to this scene
-                const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy
-                    ? (priv->mPPStepsApplied & ~nonIdempotentSteps)
-                    : 0u);
-
-                // If no extra post-processing was specified, and we obtained this scene from an
-                // Assimp importer, apply the reverse steps automatically.
-                // TODO: either drop this, or document it. Otherwise it is just a bad surprise.
-                //if (!pPreprocessing && priv) {
-                //  pp |= (nonIdempotentSteps & priv->mPPStepsApplied);
-                //}
-
-                // If the input scene is not in verbose format, but there is at least post-processing step that relies on it,
-                // we need to run the MakeVerboseFormat step first.
-                bool must_join_again = false;
-                if (!is_verbose_format) {
-                    bool verbosify = false;
-                    for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
-                        BaseProcess* const p = pimpl->mPostProcessingSteps[a];
-
-                        if (p->IsActive(pp) && p->RequireVerboseFormat()) {
-                            verbosify = true;
-                            break;
-                        }
-                    }
-
-                    if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
-                        ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first");
-
-                        MakeVerboseFormatProcess proc;
-                        proc.Execute(scenecopy.get());
-
-                        if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
-                            must_join_again = true;
-                        }
-                    }
-                }
-
-                pimpl->mProgressHandler->UpdateFileWrite(2, 4);
-
-                if (pp) {
-                    // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout
-                    {
-                        FlipWindingOrderProcess step;
-                        if (step.IsActive(pp)) {
-                            step.Execute(scenecopy.get());
-                        }
-                    }
-
-                    {
-                        FlipUVsProcess step;
-                        if (step.IsActive(pp)) {
-                            step.Execute(scenecopy.get());
-                        }
-                    }
-
-                    {
-                        MakeLeftHandedProcess step;
-                        if (step.IsActive(pp)) {
-                            step.Execute(scenecopy.get());
-                        }
-                    }
-
-                    bool exportPointCloud(false);
-                    if (nullptr != pProperties) {
-                        exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
-                    }
-
-                    // dispatch other processes
-                    for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
-                        BaseProcess* const p = pimpl->mPostProcessingSteps[a];
-
-                        if (p->IsActive(pp)
-                            && !dynamic_cast<FlipUVsProcess*>(p)
-                            && !dynamic_cast<FlipWindingOrderProcess*>(p)
-                            && !dynamic_cast<MakeLeftHandedProcess*>(p)) {
-                            if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) {
-                                continue;
-                            }
-                            p->Execute(scenecopy.get());
-                        }
-                    }
-                    ScenePrivateData* const privOut = ScenePriv(scenecopy.get());
-                    ai_assert(nullptr != privOut);
-
-                    privOut->mPPStepsApplied |= pp;
-                }
-
-                pimpl->mProgressHandler->UpdateFileWrite(3, 4);
-
-                if(must_join_again) {
-                    JoinVerticesProcess proc;
-                    proc.Execute(scenecopy.get());
-                }
-
-                ExportProperties emptyProperties;  // Never pass NULL ExportProperties so Exporters don't have to worry.
-                ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
-                                pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again);
-                                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
-                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
-
-                pimpl->mProgressHandler->UpdateFileWrite(4, 4);
-            } catch (DeadlyExportError& err) {
-                pimpl->mError = err.what();
-                return AI_FAILURE;
-            }
-            return AI_SUCCESS;
-        }
-    }
-
-    pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId;
-    ASSIMP_END_EXCEPTION_REGION(aiReturn);
-
-    return AI_FAILURE;
-}
-
-// ------------------------------------------------------------------------------------------------
-const char* Exporter::GetErrorString() const {
-    return pimpl->mError.c_str();
-}
-
-// ------------------------------------------------------------------------------------------------
-void Exporter::FreeBlob() {
-    delete pimpl->blob;
-    pimpl->blob = nullptr;
-
-    pimpl->mError = "";
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiExportDataBlob* Exporter::GetBlob() const {
-    return pimpl->blob;
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
-    const aiExportDataBlob* tmp = pimpl->blob;
-    pimpl->blob = nullptr;
-    return tmp;
-}
-
-// ------------------------------------------------------------------------------------------------
-size_t Exporter::GetExportFormatCount() const {
-    return pimpl->mExporters.size();
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
-    if (index >= GetExportFormatCount()) {
-        return nullptr;
-    }
-
-    // Return from static storage if the requested index is built-in.
-    if (index < sizeof(gExporters) / sizeof(gExporters[0])) {
-        return &gExporters[index].mDescription;
-    }
-
-    return &pimpl->mExporters[index].mDescription;
-}
-
-// ------------------------------------------------------------------------------------------------
-aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
-    for(const ExportFormatEntry& e : pimpl->mExporters) {
-        if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
-            return aiReturn_FAILURE;
-        }
-    }
-
-    pimpl->mExporters.push_back(desc);
-    return aiReturn_SUCCESS;
-}
-
-// ------------------------------------------------------------------------------------------------
-void Exporter::UnregisterExporter(const char* id) {
-    for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
-            it != pimpl->mExporters.end(); ++it) {
-        if (!strcmp((*it).mDescription.id,id)) {
-            pimpl->mExporters.erase(it);
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-ExportProperties::ExportProperties() {
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-ExportProperties::ExportProperties(const ExportProperties &other)
-: mIntProperties(other.mIntProperties)
-, mFloatProperties(other.mFloatProperties)
-, mStringProperties(other.mStringProperties)
-, mMatrixProperties(other.mMatrixProperties) {
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) {
-    return SetGenericProperty<int>(mIntProperties, szName,iValue);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool ExportProperties::SetPropertyFloat(const char* szName, ai_real iValue) {
-    return SetGenericProperty<ai_real>(mFloatProperties, szName,iValue);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool ExportProperties::SetPropertyString(const char* szName, const std::string& value) {
-    return SetGenericProperty<std::string>(mStringProperties, szName,value);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool ExportProperties::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
-    return SetGenericProperty<aiMatrix4x4>(mMatrixProperties, szName,value);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a configuration property
-int ExportProperties::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
-    return GetGenericProperty<int>(mIntProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a configuration property
-ai_real ExportProperties::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
-    return GetGenericProperty<ai_real>(mFloatProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a configuration property
-const std::string ExportProperties::GetPropertyString(const char* szName,
-        const std::string& iErrorReturn /*= ""*/) const {
-    return GetGenericProperty<std::string>(mStringProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Has a configuration property
-const aiMatrix4x4 ExportProperties::GetPropertyMatrix(const char* szName,
-        const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
-    return GetGenericProperty<aiMatrix4x4>(mMatrixProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Has a configuration property
-bool ExportProperties::HasPropertyInteger(const char* szName) const {
-    return HasGenericProperty<int>(mIntProperties, szName);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Has a configuration property
-bool ExportProperties::HasPropertyBool(const char* szName) const {
-    return HasGenericProperty<int>(mIntProperties, szName);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Has a configuration property
-bool ExportProperties::HasPropertyFloat(const char* szName) const {
-    return HasGenericProperty<ai_real>(mFloatProperties, szName);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Has a configuration property
-bool ExportProperties::HasPropertyString(const char* szName) const {
-    return HasGenericProperty<std::string>(mStringProperties, szName);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Has a configuration property
-bool ExportProperties::HasPropertyMatrix(const char* szName) const {
-    return HasGenericProperty<aiMatrix4x4>(mMatrixProperties, szName);
-}
-
-
-#endif // !ASSIMP_BUILD_NO_EXPORT

+ 0 - 107
thirdparty/assimp/code/Common/FileLogStream.h

@@ -1,107 +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 FileLofStream.h
-*/
-#ifndef ASSIMP_FILELOGSTREAM_H_INC
-#define ASSIMP_FILELOGSTREAM_H_INC
-
-#include <assimp/LogStream.hpp>
-#include <assimp/IOStream.hpp>
-#include <assimp/DefaultIOSystem.h>
-
-namespace Assimp    {
-
-// ----------------------------------------------------------------------------------
-/** @class  FileLogStream
- *  @brief  Logstream to write into a file.
- */
-class FileLogStream :
-    public LogStream
-{
-public:
-    FileLogStream( const char* file, IOSystem* io = NULL );
-    ~FileLogStream();
-    void write( const char* message );
-
-private:
-    IOStream *m_pStream;
-};
-
-// ----------------------------------------------------------------------------------
-//  Constructor
-inline FileLogStream::FileLogStream( const char* file, IOSystem* io ) :
-    m_pStream(NULL)
-{
-    if ( !file || 0 == *file )
-        return;
-
-    // If no IOSystem is specified: take a default one
-    if (!io)
-    {
-        DefaultIOSystem FileSystem;
-        m_pStream = FileSystem.Open( file, "wt");
-    }
-    else m_pStream = io->Open( file, "wt" );
-}
-
-// ----------------------------------------------------------------------------------
-//  Destructor
-inline FileLogStream::~FileLogStream()
-{
-    // The virtual d'tor should destroy the underlying file
-    delete m_pStream;
-}
-
-// ----------------------------------------------------------------------------------
-//  Write method
-inline void FileLogStream::write( const char* message )
-{
-    if (m_pStream != NULL)
-    {
-        m_pStream->Write(message, sizeof(char), ::strlen(message));
-        m_pStream->Flush();
-    }
-}
-
-// ----------------------------------------------------------------------------------
-} // !Namespace Assimp
-
-#endif // !! ASSIMP_FILELOGSTREAM_H_INC

+ 0 - 345
thirdparty/assimp/code/Common/FileSystemFilter.h

@@ -1,345 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2008, 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 FileSystemFilter.h
- *  Implements a filter system to filter calls to Exists() and Open()
- *  in order to improve the success rate of file opening ...
- */
-#pragma once
-#ifndef AI_FILESYSTEMFILTER_H_INC
-#define AI_FILESYSTEMFILTER_H_INC
-
-#include <assimp/IOSystem.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/fast_atof.h>
-#include <assimp/ParsingUtils.h>
-
-namespace Assimp    {
-
-inline bool IsHex(char s) {
-    return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F');
-}
-
-// ---------------------------------------------------------------------------
-/** File system filter
- */
-class FileSystemFilter : public IOSystem
-{
-public:
-    /** Constructor. */
-    FileSystemFilter(const std::string& file, IOSystem* old)
-    : mWrapped  (old)
-    , mSrc_file(file)
-    , mSep(mWrapped->getOsSeparator()) {
-        ai_assert(nullptr != mWrapped);
-
-        // Determine base directory
-        mBase = mSrc_file;
-        std::string::size_type ss2;
-        if (std::string::npos != (ss2 = mBase.find_last_of("\\/")))  {
-            mBase.erase(ss2,mBase.length()-ss2);
-        } else {
-            mBase = "";
-        }
-
-        // make sure the directory is terminated properly
-        char s;
-
-        if ( mBase.empty() ) {
-            mBase = ".";
-            mBase += getOsSeparator();
-        } else if ((s = *(mBase.end()-1)) != '\\' && s != '/') {
-            mBase += getOsSeparator();
-        }
-
-        DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'");
-    }
-
-    /** Destructor. */
-    ~FileSystemFilter() {
-        // empty
-    }
-
-    // -------------------------------------------------------------------
-    /** Tests for the existence of a file at the given path. */
-    bool Exists( const char* pFile) const {
-        ai_assert( nullptr != mWrapped );
-        
-        std::string tmp = pFile;
-
-        // Currently this IOSystem is also used to open THE ONE FILE.
-        if (tmp != mSrc_file)    {
-            BuildPath(tmp);
-            Cleanup(tmp);
-        }
-
-        return mWrapped->Exists(tmp);
-    }
-
-    // -------------------------------------------------------------------
-    /** Returns the directory separator. */
-    char getOsSeparator() const {
-        return mSep;
-    }
-
-    // -------------------------------------------------------------------
-    /** Open a new file with a given path. */
-    IOStream* Open( const char* pFile, const char* pMode = "rb") {
-        ai_assert( nullptr != mWrapped );
-        if ( nullptr == pFile || nullptr == pMode ) {
-            return nullptr;
-        }
-        
-        ai_assert( nullptr != pFile );
-        ai_assert( nullptr != pMode );
-
-        // First try the unchanged path
-        IOStream* s = mWrapped->Open(pFile,pMode);
-
-        if (nullptr == s) {
-            std::string tmp = pFile;
-
-            // Try to convert between absolute and relative paths
-            BuildPath(tmp);
-            s = mWrapped->Open(tmp,pMode);
-
-            if (nullptr == s) {
-                // Finally, look for typical issues with paths
-                // and try to correct them. This is our last
-                // resort.
-                tmp = pFile;
-                Cleanup(tmp);
-                BuildPath(tmp);
-                s = mWrapped->Open(tmp,pMode);
-            }
-        }
-
-        return s;
-    }
-
-    // -------------------------------------------------------------------
-    /** Closes the given file and releases all resources associated with it. */
-    void Close( IOStream* pFile) {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->Close(pFile);
-    }
-
-    // -------------------------------------------------------------------
-    /** Compare two paths */
-    bool ComparePaths (const char* one, const char* second) const {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->ComparePaths (one,second);
-    }
-
-    // -------------------------------------------------------------------
-    /** Pushes a new directory onto the directory stack. */
-    bool PushDirectory(const std::string &path ) {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->PushDirectory(path);
-    }
-
-    // -------------------------------------------------------------------
-    /** Returns the top directory from the stack. */
-    const std::string &CurrentDirectory() const {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->CurrentDirectory();
-    }
-
-    // -------------------------------------------------------------------
-    /** Returns the number of directories stored on the stack. */
-    size_t StackSize() const {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->StackSize();
-    }
-
-    // -------------------------------------------------------------------
-    /** Pops the top directory from the stack. */
-    bool PopDirectory() {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->PopDirectory();
-    }
-
-    // -------------------------------------------------------------------
-    /** Creates an new directory at the given path. */
-    bool CreateDirectory(const std::string &path) {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->CreateDirectory(path);
-    }
-
-    // -------------------------------------------------------------------
-    /** Will change the current directory to the given path. */
-    bool ChangeDirectory(const std::string &path) {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->ChangeDirectory(path);
-    }
-
-    // -------------------------------------------------------------------
-    /** Delete file. */
-    bool DeleteFile(const std::string &file) {
-        ai_assert( nullptr != mWrapped );
-        return mWrapped->DeleteFile(file);
-    }
-
-private:
-    // -------------------------------------------------------------------
-    /** Build a valid path from a given relative or absolute path.
-     */
-    void BuildPath (std::string& in) const {
-        ai_assert( nullptr != mWrapped );
-        // if we can already access the file, great.
-        if (in.length() < 3 || mWrapped->Exists(in)) {
-            return;
-        }
-
-        // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows).
-        if (in[1] != ':') {
-
-            // append base path and try
-            const std::string tmp = mBase + in;
-            if (mWrapped->Exists(tmp)) {
-                in = tmp;
-                return;
-            }
-        }
-
-        // Chop of the file name and look in the model directory, if
-        // this fails try all sub paths of the given path, i.e.
-        // if the given path is foo/bar/something.lwo, try
-        // <base>/something.lwo
-        // <base>/bar/something.lwo
-        // <base>/foo/bar/something.lwo
-        std::string::size_type pos = in.rfind('/');
-        if (std::string::npos == pos) {
-            pos = in.rfind('\\');
-        }
-
-        if (std::string::npos != pos)   {
-            std::string tmp;
-            std::string::size_type last_dirsep = std::string::npos;
-
-            while(true) {
-                tmp = mBase;
-                tmp += mSep;
-
-                std::string::size_type dirsep = in.rfind('/', last_dirsep);
-                if (std::string::npos == dirsep) {
-                    dirsep = in.rfind('\\', last_dirsep);
-                }
-
-                if (std::string::npos == dirsep || dirsep == 0) {
-                    // we did try this already.
-                    break;
-                }
-
-                last_dirsep = dirsep-1;
-
-                tmp += in.substr(dirsep+1, in.length()-pos);
-                if (mWrapped->Exists(tmp)) {
-                    in = tmp;
-                    return;
-                }
-            }
-        }
-
-        // hopefully the underlying file system has another few tricks to access this file ...
-    }
-
-    // -------------------------------------------------------------------
-    /** Cleanup the given path
-     */
-    void Cleanup (std::string& in) const {
-        if(in.empty()) {
-            return;
-        }
-
-        // Remove a very common issue when we're parsing file names: spaces at the
-        // beginning of the path.
-        char last = 0;
-        std::string::iterator it = in.begin();
-        while (IsSpaceOrNewLine( *it ))++it;
-        if (it != in.begin()) {
-            in.erase(in.begin(),it+1);
-        }
-
-        const char separator = getOsSeparator();
-        for (it = in.begin(); it != in.end(); ++it) {
-            // Exclude :// and \\, which remain untouched.
-            // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
-            if ( !strncmp(&*it, "://", 3 )) {
-                it += 3;
-                continue;
-            }
-            if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) {
-                it += 2;
-                continue;
-            }
-
-            // Cleanup path delimiters
-            if (*it == '/' || (*it) == '\\') {
-                *it = separator;
-
-                // And we're removing double delimiters, frequent issue with
-                // incorrectly composited paths ...
-                if (last == *it) {
-                    it = in.erase(it);
-                    --it;
-                }
-            } else if (*it == '%' && in.end() - it > 2) {
-                // Hex sequence in URIs
-                if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) {
-                    *it = HexOctetToDecimal(&*it);
-                    it = in.erase(it+1,it+2);
-                    --it;
-                }
-            }
-
-            last = *it;
-        }
-    }
-
-private:
-    IOSystem *mWrapped;
-    std::string mSrc_file, mBase;
-    char mSep;
-};
-
-} //!ns Assimp
-
-#endif //AI_DEFAULTIOSYSTEM_H_INC

+ 0 - 102
thirdparty/assimp/code/Common/IFF.h

@@ -1,102 +0,0 @@
-// Definitions for the Interchange File Format (IFF)
-// Alexander Gessler, 2006
-// Adapted to Assimp August 2008
-
-#ifndef AI_IFF_H_INCLUDED
-#define AI_IFF_H_INCLUDED
-
-#include <assimp/ByteSwapper.h>
-
-namespace Assimp    {
-namespace IFF       {
-
-/////////////////////////////////////////////////////////////////////////////////
-//! Describes an IFF chunk header
-/////////////////////////////////////////////////////////////////////////////////
-struct ChunkHeader
-{
-    //! Type of the chunk header - FourCC
-    uint32_t type;
-
-    //! Length of the chunk data, in bytes
-    uint32_t length;
-};
-
-
-/////////////////////////////////////////////////////////////////////////////////
-//! Describes an IFF sub chunk header
-/////////////////////////////////////////////////////////////////////////////////
-struct SubChunkHeader
-{
-    //! Type of the chunk header - FourCC
-    uint32_t type;
-
-    //! Length of the chunk data, in bytes
-    uint16_t length;
-};
-
-
-#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
-    ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
-
-
-#define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M')
-
-
-/////////////////////////////////////////////////////////////////////////////////
-//! Load a chunk header
-//! @param outFile Pointer to the file data - points to the chunk data afterwards
-//! @return Copy of the chunk header
-/////////////////////////////////////////////////////////////////////////////////
-inline ChunkHeader LoadChunk(uint8_t*& outFile)
-{
-    ChunkHeader head;
-    ::memcpy(&head.type, outFile, 4);
-    outFile += 4;
-    ::memcpy(&head.length, outFile, 4);
-    outFile += 4;
-    AI_LSWAP4(head.length);
-    AI_LSWAP4(head.type);
-    return head;
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-//! Load a sub chunk header
-//! @param outFile Pointer to the file data - points to the chunk data afterwards
-//! @return Copy of the sub chunk header
-/////////////////////////////////////////////////////////////////////////////////
-inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
-{
-    SubChunkHeader head;
-    ::memcpy(&head.type, outFile, 4);
-    outFile += 4;
-    ::memcpy(&head.length, outFile, 2);
-    outFile += 2;
-    AI_LSWAP2(head.length);
-    AI_LSWAP4(head.type);
-    return head;
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-//! Read the file header and return the type of the file and its size
-//! @param outFile Pointer to the file data. The buffer must at
-//!   least be 12 bytes large.
-//! @param fileType Receives the type of the file
-//! @return 0 if everything was OK, otherwise an error message
-/////////////////////////////////////////////////////////////////////////////////
-inline const char* ReadHeader(uint8_t* outFile, uint32_t& fileType)
-{
-    ChunkHeader head = LoadChunk(outFile);
-    if(AI_IFF_FOURCC_FORM != head.type)
-    {
-        return "The file is not an IFF file: FORM chunk is missing";
-    }
-    ::memcpy(&fileType, outFile, 4);
-    AI_LSWAP4(fileType);
-    return 0;
-}
-
-
-}}
-
-#endif // !! AI_IFF_H_INCLUDED

+ 0 - 1174
thirdparty/assimp/code/Common/Importer.cpp

@@ -1,1174 +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  Importer.cpp
- *  @brief Implementation of the CPP-API class #Importer
- */
-
-#include <assimp/version.h>
-#include <assimp/config.h>
-#include <assimp/importerdesc.h>
-
-// ------------------------------------------------------------------------------------------------
-/* Uncomment this line to prevent Assimp from catching unknown exceptions.
- *
- * Note that any Exception except DeadlyImportError may lead to
- * undefined behaviour -> loaders could remain in an unusable state and
- * further imports with the same Importer instance could fail/crash/burn ...
- */
-// ------------------------------------------------------------------------------------------------
-#ifndef ASSIMP_BUILD_DEBUG
-#   define ASSIMP_CATCH_GLOBAL_EXCEPTIONS
-#endif
-
-// ------------------------------------------------------------------------------------------------
-// Internal headers
-// ------------------------------------------------------------------------------------------------
-#include "Common/Importer.h"
-#include "Common/BaseProcess.h"
-#include "Common/DefaultProgressHandler.h"
-#include "PostProcessing/ProcessHelper.h"
-#include "Common/ScenePreprocessor.h"
-#include "Common/ScenePrivate.h"
-
-#include <assimp/BaseImporter.h>
-#include <assimp/GenericProperty.h>
-#include <assimp/MemoryIOWrapper.h>
-#include <assimp/Profiler.h>
-#include <assimp/TinyFormatter.h>
-#include <assimp/Exceptional.h>
-#include <assimp/Profiler.h>
-#include <set>
-#include <memory>
-#include <cctype>
-
-#include <assimp/DefaultIOStream.h>
-#include <assimp/DefaultIOSystem.h>
-
-#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-#   include "PostProcessing/ValidateDataStructure.h"
-#endif
-
-using namespace Assimp::Profiling;
-using namespace Assimp::Formatter;
-
-namespace Assimp {
-    // ImporterRegistry.cpp
-    void GetImporterInstanceList(std::vector< BaseImporter* >& out);
-	void DeleteImporterInstanceList(std::vector< BaseImporter* >& out);
-
-    // PostStepRegistry.cpp
-    void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
-}
-
-using namespace Assimp;
-using namespace Assimp::Intern;
-
-// ------------------------------------------------------------------------------------------------
-// Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides
-// new and delete (and their array counterparts) of public API classes (e.g. Logger) to
-// utilize our DLL heap.
-// See http://www.gotw.ca/publications/mill15.htm
-// ------------------------------------------------------------------------------------------------
-void* AllocateFromAssimpHeap::operator new ( size_t num_bytes)  {
-    return ::operator new(num_bytes);
-}
-
-void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw()  {
-    try {
-        return AllocateFromAssimpHeap::operator new( num_bytes );
-    }
-    catch( ... )    {
-        return NULL;
-    }
-}
-
-void AllocateFromAssimpHeap::operator delete ( void* data)  {
-    return ::operator delete(data);
-}
-
-void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes)    {
-    return ::operator new[](num_bytes);
-}
-
-void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() {
-    try {
-        return AllocateFromAssimpHeap::operator new[]( num_bytes );
-    }
-    catch( ... )    {
-        return NULL;
-    }
-}
-
-void AllocateFromAssimpHeap::operator delete[] ( void* data)    {
-    return ::operator delete[](data);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Importer constructor.
-Importer::Importer()
- : pimpl( new ImporterPimpl ) {
-    pimpl->mScene = NULL;
-    pimpl->mErrorString = "";
-
-    // Allocate a default IO handler
-    pimpl->mIOHandler = new DefaultIOSystem;
-    pimpl->mIsDefaultHandler = true;
-    pimpl->bExtraVerbose     = false; // disable extra verbose mode by default
-
-    pimpl->mProgressHandler = new DefaultProgressHandler();
-    pimpl->mIsDefaultProgressHandler = true;
-
-    GetImporterInstanceList(pimpl->mImporter);
-    GetPostProcessingStepInstanceList(pimpl->mPostProcessingSteps);
-
-    // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list.
-    pimpl->mPPShared = new SharedPostProcessInfo();
-    for (std::vector<BaseProcess*>::iterator it =  pimpl->mPostProcessingSteps.begin();
-        it != pimpl->mPostProcessingSteps.end();
-        ++it)   {
-
-        (*it)->SetSharedData(pimpl->mPPShared);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor of Importer
-Importer::~Importer()
-{
-    // Delete all import plugins
-	DeleteImporterInstanceList(pimpl->mImporter);
-
-    // Delete all post-processing plug-ins
-    for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)
-        delete pimpl->mPostProcessingSteps[a];
-
-    // Delete the assigned IO and progress handler
-    delete pimpl->mIOHandler;
-    delete pimpl->mProgressHandler;
-
-    // Kill imported scene. Destructor's should do that recursively
-    delete pimpl->mScene;
-
-    // Delete shared post-processing data
-    delete pimpl->mPPShared;
-
-    // and finally the pimpl itself
-    delete pimpl;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Register a custom post-processing step
-aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
-{
-    ai_assert(NULL != pImp);
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-        pimpl->mPostProcessingSteps.push_back(pImp);
-        ASSIMP_LOG_INFO("Registering custom post-processing step");
-
-    ASSIMP_END_EXCEPTION_REGION(aiReturn);
-    return AI_SUCCESS;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Register a custom loader plugin
-aiReturn Importer::RegisterLoader(BaseImporter* pImp)
-{
-    ai_assert(NULL != pImp);
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // --------------------------------------------------------------------
-    // Check whether we would have two loaders for the same file extension
-    // This is absolutely OK, but we should warn the developer of the new
-    // loader that his code will probably never be called if the first
-    // loader is a bit too lazy in his file checking.
-    // --------------------------------------------------------------------
-    std::set<std::string> st;
-    std::string baked;
-    pImp->GetExtensionList(st);
-
-    for(std::set<std::string>::const_iterator it = st.begin(); it != st.end(); ++it) {
-
-#ifdef ASSIMP_BUILD_DEBUG
-        if (IsExtensionSupported(*it)) {
-            ASSIMP_LOG_WARN_F("The file extension ", *it, " is already in use");
-        }
-#endif
-        baked += *it;
-    }
-
-    // add the loader
-    pimpl->mImporter.push_back(pImp);
-    ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked);
-    ASSIMP_END_EXCEPTION_REGION(aiReturn);
-    return AI_SUCCESS;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Unregister a custom loader plugin
-aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
-{
-    if(!pImp) {
-        // unregistering a NULL importer is no problem for us ... really!
-        return AI_SUCCESS;
-    }
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    std::vector<BaseImporter*>::iterator it = std::find(pimpl->mImporter.begin(),
-        pimpl->mImporter.end(),pImp);
-
-    if (it != pimpl->mImporter.end())   {
-        pimpl->mImporter.erase(it);
-        ASSIMP_LOG_INFO("Unregistering custom importer: ");
-        return AI_SUCCESS;
-    }
-    ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ...");
-    ASSIMP_END_EXCEPTION_REGION(aiReturn);
-    return AI_FAILURE;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Unregister a custom loader plugin
-aiReturn Importer::UnregisterPPStep(BaseProcess* pImp)
-{
-    if(!pImp) {
-        // unregistering a NULL ppstep is no problem for us ... really!
-        return AI_SUCCESS;
-    }
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    std::vector<BaseProcess*>::iterator it = std::find(pimpl->mPostProcessingSteps.begin(),
-        pimpl->mPostProcessingSteps.end(),pImp);
-
-    if (it != pimpl->mPostProcessingSteps.end())    {
-        pimpl->mPostProcessingSteps.erase(it);
-        ASSIMP_LOG_INFO("Unregistering custom post-processing step");
-        return AI_SUCCESS;
-    }
-    ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you ..");
-    ASSIMP_END_EXCEPTION_REGION(aiReturn);
-    return AI_FAILURE;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Supplies a custom IO handler to the importer to open and access files.
-void Importer::SetIOHandler( IOSystem* pIOHandler)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    // If the new handler is zero, allocate a default IO implementation.
-    if (!pIOHandler)
-    {
-        // Release pointer in the possession of the caller
-        pimpl->mIOHandler = new DefaultIOSystem();
-        pimpl->mIsDefaultHandler = true;
-    }
-    // Otherwise register the custom handler
-    else if (pimpl->mIOHandler != pIOHandler)
-    {
-        delete pimpl->mIOHandler;
-        pimpl->mIOHandler = pIOHandler;
-        pimpl->mIsDefaultHandler = false;
-    }
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the currently set IO handler
-IOSystem* Importer::GetIOHandler() const {
-    return pimpl->mIOHandler;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Check whether a custom IO handler is currently set
-bool Importer::IsDefaultIOHandler() const {
-    return pimpl->mIsDefaultHandler;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Supplies a custom progress handler to get regular callbacks during importing
-void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    // If the new handler is zero, allocate a default implementation.
-    if (!pHandler)
-    {
-        // Release pointer in the possession of the caller
-        pimpl->mProgressHandler = new DefaultProgressHandler();
-        pimpl->mIsDefaultProgressHandler = true;
-    }
-    // Otherwise register the custom handler
-    else if (pimpl->mProgressHandler != pHandler)
-    {
-        delete pimpl->mProgressHandler;
-        pimpl->mProgressHandler = pHandler;
-        pimpl->mIsDefaultProgressHandler = false;
-    }
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the currently set progress handler
-ProgressHandler* Importer::GetProgressHandler() const {
-    return pimpl->mProgressHandler;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Check whether a custom progress handler is currently set
-bool Importer::IsDefaultProgressHandler() const {
-    return pimpl->mIsDefaultProgressHandler;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Validate post process step flags
-bool _ValidateFlags(unsigned int pFlags)
-{
-    if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals)   {
-        ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
-        return false;
-    }
-    if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices)    {
-        ASSIMP_LOG_ERROR("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible");
-        return false;
-    }
-    return true;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Free the current scene
-void Importer::FreeScene( )
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    delete pimpl->mScene;
-    pimpl->mScene = NULL;
-
-    pimpl->mErrorString = "";
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the current error string, if any
-const char* Importer::GetErrorString() const
-{
-     /* Must remain valid as long as ReadFile() or FreeFile() are not called */
-    return pimpl->mErrorString.c_str();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Enable extra-verbose mode
-void Importer::SetExtraVerbose(bool bDo)
-{
-    pimpl->bExtraVerbose = bDo;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the current scene
-const aiScene* Importer::GetScene() const
-{
-    return pimpl->mScene;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Orphan the current scene and return it.
-aiScene* Importer::GetOrphanedScene()
-{
-    aiScene* s = pimpl->mScene;
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    pimpl->mScene = NULL;
-
-    pimpl->mErrorString = ""; /* reset error string */
-    ASSIMP_END_EXCEPTION_REGION(aiScene*);
-    return s;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Validate post-processing flags
-bool Importer::ValidateFlags(unsigned int pFlags) const
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    // run basic checks for mutually exclusive flags
-    if(!_ValidateFlags(pFlags)) {
-        return false;
-    }
-
-    // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ...
-#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-    if (pFlags & aiProcess_ValidateDataStructure) {
-        return false;
-    }
-#endif
-    pFlags &= ~aiProcess_ValidateDataStructure;
-
-    // Now iterate through all bits which are set in the flags and check whether we find at least
-    // one pp plugin which handles it.
-    for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) {
-
-        if (pFlags & mask) {
-
-            bool have = false;
-            for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)   {
-                if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) {
-
-                    have = true;
-                    break;
-                }
-            }
-            if (!have) {
-                return false;
-            }
-        }
-    }
-    ASSIMP_END_EXCEPTION_REGION(bool);
-    return true;
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
-    size_t pLength,
-    unsigned int pFlags,
-    const char* pHint /*= ""*/)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    if (!pHint) {
-        pHint = "";
-    }
-
-    if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) {
-        pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
-        return NULL;
-    }
-
-    // prevent deletion of the previous IOHandler
-    IOSystem* io = pimpl->mIOHandler;
-    pimpl->mIOHandler = NULL;
-
-    SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io));
-
-    // read the file and recover the previous IOSystem
-    static const size_t BufSize(Importer::MaxLenHint + 28);
-    char fbuff[BufSize];
-    ai_snprintf(fbuff, BufSize, "%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint);
-
-    ReadFile(fbuff,pFlags);
-    SetIOHandler(io);
-
-    ASSIMP_END_EXCEPTION_REGION(const aiScene*);
-    return pimpl->mScene;
-}
-
-// ------------------------------------------------------------------------------------------------
-void WriteLogOpening(const std::string& file)
-{
-    ASSIMP_LOG_INFO_F("Load ", file);
-
-    // print a full version dump. This is nice because we don't
-    // need to ask the authors of incoming bug reports for
-    // the library version they're using - a log dump is
-    // sufficient.
-    const unsigned int flags( aiGetCompileFlags() );
-    std::stringstream stream;
-    stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " "
-#if defined(ASSIMP_BUILD_ARCHITECTURE)
-        << ASSIMP_BUILD_ARCHITECTURE
-#elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__)
-        << "x86"
-#elif defined(_M_X64) || defined(__x86_64__)
-        << "amd64"
-#elif defined(_M_IA64) || defined(__ia64__)
-        << "itanium"
-#elif defined(__ppc__) || defined(__powerpc__)
-        << "ppc32"
-#elif defined(__powerpc64__)
-        << "ppc64"
-#elif defined(__arm__)
-        << "arm"
-#else
-        << "<unknown architecture>"
-#endif
-        << " "
-#if defined(ASSIMP_BUILD_COMPILER)
-        << ( ASSIMP_BUILD_COMPILER )
-#elif defined(_MSC_VER)
-        << "msvc"
-#elif defined(__GNUC__)
-        << "gcc"
-#else
-        << "<unknown compiler>"
-#endif
-
-#ifdef ASSIMP_BUILD_DEBUG
-        << " debug"
-#endif
-
-        << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "")
-        << (flags & ASSIMP_CFLAGS_SHARED  ? " shared" : "")
-        << (flags & ASSIMP_CFLAGS_SINGLETHREADED  ? " singlethreaded" : "");
-
-        ASSIMP_LOG_DEBUG(stream.str());
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the given file and returns its contents if successful.
-const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    const std::string pFile(_pFile);
-
-    // ----------------------------------------------------------------------
-    // Put a large try block around everything to catch all std::exception's
-    // that might be thrown by STL containers or by new().
-    // ImportErrorException's are throw by ourselves and caught elsewhere.
-    //-----------------------------------------------------------------------
-
-    WriteLogOpening(pFile);
-
-#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
-    try
-#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
-    {
-        // Check whether this Importer instance has already loaded
-        // a scene. In this case we need to delete the old one
-        if (pimpl->mScene)  {
-
-            ASSIMP_LOG_DEBUG("(Deleting previous scene)");
-            FreeScene();
-        }
-
-        // First check if the file is accessible at all
-        if( !pimpl->mIOHandler->Exists( pFile)) {
-
-            pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
-            ASSIMP_LOG_ERROR(pimpl->mErrorString);
-            return NULL;
-        }
-
-        std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
-        if (profiler) {
-            profiler->BeginRegion("total");
-        }
-
-        // Find an worker class which can handle the file
-        BaseImporter* imp = NULL;
-        SetPropertyInteger("importerIndex", -1);
-        for( unsigned int a = 0; a < pimpl->mImporter.size(); a++)  {
-
-            if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
-                imp = pimpl->mImporter[a];
-                SetPropertyInteger("importerIndex", a);
-                break;
-            }
-        }
-
-        if (!imp)   {
-            // not so bad yet ... try format auto detection.
-            const std::string::size_type s = pFile.find_last_of('.');
-            if (s != std::string::npos) {
-                ASSIMP_LOG_INFO("File extension not known, trying signature-based detection");
-                for( unsigned int a = 0; a < pimpl->mImporter.size(); a++)  {
-                    if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
-                        imp = pimpl->mImporter[a];
-                        SetPropertyInteger("importerIndex", a);
-                        break;
-                    }
-                }
-            }
-            // Put a proper error message if no suitable importer was found
-            if( !imp)   {
-                pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
-                ASSIMP_LOG_ERROR(pimpl->mErrorString);
-                return NULL;
-            }
-        }
-
-        // Get file size for progress handler
-        IOStream * fileIO = pimpl->mIOHandler->Open( pFile );
-        uint32_t fileSize = 0;
-        if (fileIO)
-        {
-            fileSize = static_cast<uint32_t>(fileIO->FileSize());
-            pimpl->mIOHandler->Close( fileIO );
-        }
-
-        // Dispatch the reading to the worker class for this format
-        const aiImporterDesc *desc( imp->GetInfo() );
-        std::string ext( "unknown" );
-        if ( NULL != desc ) {
-            ext = desc->mName;
-        }
-        ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." );
-        pimpl->mProgressHandler->UpdateFileRead( 0, fileSize );
-
-        if (profiler) {
-            profiler->BeginRegion("import");
-        }
-
-        pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler);
-        pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize );
-
-        if (profiler) {
-            profiler->EndRegion("import");
-        }
-
-        SetPropertyString("sourceFilePath", pFile);
-
-        // If successful, apply all active post processing steps to the imported data
-        if( pimpl->mScene)  {
-
-#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-            // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called.
-            if (pFlags & aiProcess_ValidateDataStructure)
-            {
-                ValidateDSProcess ds;
-                ds.ExecuteOnScene (this);
-                if (!pimpl->mScene) {
-                    return NULL;
-                }
-            }
-#endif // no validation
-
-            // Preprocess the scene and prepare it for post-processing
-            if (profiler) {
-                profiler->BeginRegion("preprocess");
-            }
-
-            ScenePreprocessor pre(pimpl->mScene);
-            pre.ProcessScene();
-
-            if (profiler) {
-                profiler->EndRegion("preprocess");
-            }
-
-            // Ensure that the validation process won't be called twice
-            ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure));
-        }
-        // if failed, extract the error string
-        else if( !pimpl->mScene) {
-            pimpl->mErrorString = imp->GetErrorText();
-        }
-
-        // clear any data allocated by post-process steps
-        pimpl->mPPShared->Clean();
-
-        if (profiler) {
-            profiler->EndRegion("total");
-        }
-    }
-#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
-    catch (std::exception &e)
-    {
-#if (defined _MSC_VER) &&   (defined _CPPRTTI)
-        // if we have RTTI get the full name of the exception that occurred
-        pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
-#else
-        pimpl->mErrorString = std::string("std::exception: ") + e.what();
-#endif
-
-        ASSIMP_LOG_ERROR(pimpl->mErrorString);
-        delete pimpl->mScene; pimpl->mScene = NULL;
-    }
-#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
-
-    // either successful or failure - the pointer expresses it anyways
-    ASSIMP_END_EXCEPTION_REGION(const aiScene*);
-    return pimpl->mScene;
-}
-
-
-// ------------------------------------------------------------------------------------------------
-// Apply post-processing to the currently bound scene
-const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    // Return immediately if no scene is active
-    if (!pimpl->mScene) {
-        return NULL;
-    }
-
-    // If no flags are given, return the current scene with no further action
-    if (!pFlags) {
-        return pimpl->mScene;
-    }
-
-    // In debug builds: run basic flag validation
-    ai_assert(_ValidateFlags(pFlags));
-    ASSIMP_LOG_INFO("Entering post processing pipeline");
-
-#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-    // The ValidateDS process plays an exceptional role. It isn't contained in the global
-    // list of post-processing steps, so we need to call it manually.
-    if (pFlags & aiProcess_ValidateDataStructure)
-    {
-        ValidateDSProcess ds;
-        ds.ExecuteOnScene (this);
-        if (!pimpl->mScene) {
-            return NULL;
-        }
-    }
-#endif // no validation
-#ifdef ASSIMP_BUILD_DEBUG
-    if (pimpl->bExtraVerbose)
-    {
-#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-        ASSIMP_LOG_ERROR("Verbose Import is not available due to build settings");
-#endif  // no validation
-        pFlags |= aiProcess_ValidateDataStructure;
-    }
-#else
-    if (pimpl->bExtraVerbose) {
-        ASSIMP_LOG_WARN("Not a debug build, ignoring extra verbose setting");
-    }
-#endif // ! DEBUG
-
-    std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
-    for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)   {
-
-        BaseProcess* process = pimpl->mPostProcessingSteps[a];
-        pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
-        if( process->IsActive( pFlags)) {
-
-            if (profiler) {
-                profiler->BeginRegion("postprocess");
-            }
-
-            process->ExecuteOnScene ( this );
-
-            if (profiler) {
-                profiler->EndRegion("postprocess");
-            }
-        }
-        if( !pimpl->mScene) {
-            break;
-        }
-#ifdef ASSIMP_BUILD_DEBUG
-
-#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-        continue;
-#endif  // no validation
-
-        // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
-        if (pimpl->bExtraVerbose)   {
-            ASSIMP_LOG_DEBUG("Verbose Import: re-validating data structures");
-
-            ValidateDSProcess ds;
-            ds.ExecuteOnScene (this);
-            if( !pimpl->mScene) {
-                ASSIMP_LOG_ERROR("Verbose Import: failed to re-validate data structures");
-                break;
-            }
-        }
-#endif // ! DEBUG
-    }
-    pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()), 
-        static_cast<int>(pimpl->mPostProcessingSteps.size()) );
-
-    // update private scene flags
-    if( pimpl->mScene )
-      ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
-
-    // clear any data allocated by post-process steps
-    pimpl->mPPShared->Clean();
-    ASSIMP_LOG_INFO("Leaving post processing pipeline");
-
-    ASSIMP_END_EXCEPTION_REGION(const aiScene*);
-    return pimpl->mScene;
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) {
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // Return immediately if no scene is active
-    if ( NULL == pimpl->mScene ) {
-        return NULL;
-    }
-
-    // If no flags are given, return the current scene with no further action
-    if ( NULL == rootProcess ) {
-        return pimpl->mScene;
-    }
-
-    // In debug builds: run basic flag validation
-    ASSIMP_LOG_INFO( "Entering customized post processing pipeline" );
-
-#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-    // The ValidateDS process plays an exceptional role. It isn't contained in the global
-    // list of post-processing steps, so we need to call it manually.
-    if ( requestValidation )
-    {
-        ValidateDSProcess ds;
-        ds.ExecuteOnScene( this );
-        if ( !pimpl->mScene ) {
-            return NULL;
-        }
-    }
-#endif // no validation
-#ifdef ASSIMP_BUILD_DEBUG
-    if ( pimpl->bExtraVerbose )
-    {
-#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-        ASSIMP_LOG_ERROR( "Verbose Import is not available due to build settings" );
-#endif  // no validation
-    }
-#else
-    if ( pimpl->bExtraVerbose ) {
-        ASSIMP_LOG_WARN( "Not a debug build, ignoring extra verbose setting" );
-    }
-#endif // ! DEBUG
-
-    std::unique_ptr<Profiler> profiler( GetPropertyInteger( AI_CONFIG_GLOB_MEASURE_TIME, 0 ) ? new Profiler() : NULL );
-
-    if ( profiler ) {
-        profiler->BeginRegion( "postprocess" );
-    }
-
-    rootProcess->ExecuteOnScene( this );
-
-    if ( profiler ) {
-        profiler->EndRegion( "postprocess" );
-    }
-
-    // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
-    if ( pimpl->bExtraVerbose || requestValidation  ) {
-        ASSIMP_LOG_DEBUG( "Verbose Import: revalidating data structures" );
-
-        ValidateDSProcess ds;
-        ds.ExecuteOnScene( this );
-        if ( !pimpl->mScene ) {
-            ASSIMP_LOG_ERROR( "Verbose Import: failed to revalidate data structures" );
-        }
-    }
-
-    // clear any data allocated by post-process steps
-    pimpl->mPPShared->Clean();
-    ASSIMP_LOG_INFO( "Leaving customized post processing pipeline" );
-
-    ASSIMP_END_EXCEPTION_REGION( const aiScene* );
-
-    return pimpl->mScene;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Helper function to check whether an extension is supported by ASSIMP
-bool Importer::IsExtensionSupported(const char* szExtension) const
-{
-    return nullptr != GetImporter(szExtension);
-}
-
-// ------------------------------------------------------------------------------------------------
-size_t Importer::GetImporterCount() const
-{
-    return pimpl->mImporter.size();
-}
-
-// ------------------------------------------------------------------------------------------------
-const aiImporterDesc* Importer::GetImporterInfo(size_t index) const
-{
-    if (index >= pimpl->mImporter.size()) {
-        return NULL;
-    }
-    return pimpl->mImporter[index]->GetInfo();
-}
-
-
-// ------------------------------------------------------------------------------------------------
-BaseImporter* Importer::GetImporter (size_t index) const
-{
-    if (index >= pimpl->mImporter.size()) {
-        return NULL;
-    }
-    return pimpl->mImporter[index];
-}
-
-// ------------------------------------------------------------------------------------------------
-// Find a loader plugin for a given file extension
-BaseImporter* Importer::GetImporter (const char* szExtension) const
-{
-    return GetImporter(GetImporterIndex(szExtension));
-}
-
-// ------------------------------------------------------------------------------------------------
-// Find a loader plugin for a given file extension
-size_t Importer::GetImporterIndex (const char* szExtension) const {
-    ai_assert(nullptr != szExtension);
-
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-
-    // skip over wildcard and dot characters at string head --
-    for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension );
-
-    std::string ext(szExtension);
-    if (ext.empty()) {
-        return static_cast<size_t>(-1);
-    }
-    std::transform( ext.begin(), ext.end(), ext.begin(), ToLower<char> );
-
-    std::set<std::string> str;
-    for (std::vector<BaseImporter*>::const_iterator i =  pimpl->mImporter.begin();i != pimpl->mImporter.end();++i)  {
-        str.clear();
-
-        (*i)->GetExtensionList(str);
-        for (std::set<std::string>::const_iterator it = str.begin(); it != str.end(); ++it) {
-            if (ext == *it) {
-                return std::distance(static_cast< std::vector<BaseImporter*>::const_iterator >(pimpl->mImporter.begin()), i);
-            }
-        }
-    }
-    ASSIMP_END_EXCEPTION_REGION(size_t);
-    return static_cast<size_t>(-1);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Helper function to build a list of all file extensions supported by ASSIMP
-void Importer::GetExtensionList(aiString& szOut) const
-{
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-    std::set<std::string> str;
-    for (std::vector<BaseImporter*>::const_iterator i =  pimpl->mImporter.begin();i != pimpl->mImporter.end();++i)  {
-        (*i)->GetExtensionList(str);
-    }
-
-	// List can be empty
-	if( !str.empty() ) {
-		for (std::set<std::string>::const_iterator it = str.begin();; ) {
-			szOut.Append("*.");
-			szOut.Append((*it).c_str());
-
-			if (++it == str.end()) {
-				break;
-			}
-			szOut.Append(";");
-		}
-	}
-    ASSIMP_END_EXCEPTION_REGION(void);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool Importer::SetPropertyInteger(const char* szName, int iValue)
-{
-    bool existing;
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-        existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue);
-    ASSIMP_END_EXCEPTION_REGION(bool);
-    return existing;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool Importer::SetPropertyFloat(const char* szName, ai_real iValue)
-{
-    bool existing;
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-        existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue);
-    ASSIMP_END_EXCEPTION_REGION(bool);
-    return existing;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool Importer::SetPropertyString(const char* szName, const std::string& value)
-{
-    bool existing;
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-        existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value);
-    ASSIMP_END_EXCEPTION_REGION(bool);
-    return existing;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Set a configuration property
-bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
-{
-    bool existing;
-    ASSIMP_BEGIN_EXCEPTION_REGION();
-        existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value);
-    ASSIMP_END_EXCEPTION_REGION(bool);
-    return existing;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a configuration property
-int Importer::GetPropertyInteger(const char* szName,
-    int iErrorReturn /*= 0xffffffff*/) const
-{
-    return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a configuration property
-ai_real Importer::GetPropertyFloat(const char* szName,
-    ai_real iErrorReturn /*= 10e10*/) const
-{
-    return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a configuration property
-const std::string Importer::GetPropertyString(const char* szName,
-    const std::string& iErrorReturn /*= ""*/) const
-{
-    return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get a configuration property
-const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName,
-    const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const
-{
-    return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the memory requirements of a single node
-inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
-{
-    iScene += sizeof(aiNode);
-    iScene += sizeof(unsigned int) * pcNode->mNumMeshes;
-    iScene += sizeof(void*) * pcNode->mNumChildren;
-
-    for (unsigned int i = 0; i < pcNode->mNumChildren;++i) {
-        AddNodeWeight(iScene,pcNode->mChildren[i]);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get the memory requirements of the scene
-void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
-{
-    in = aiMemoryInfo();
-    aiScene* mScene = pimpl->mScene;
-
-    // return if we have no scene loaded
-    if (!pimpl->mScene)
-        return;
-
-
-    in.total = sizeof(aiScene);
-
-    // add all meshes
-    for (unsigned int i = 0; i < mScene->mNumMeshes;++i)
-    {
-        in.meshes += sizeof(aiMesh);
-        if (mScene->mMeshes[i]->HasPositions()) {
-            in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
-        }
-
-        if (mScene->mMeshes[i]->HasNormals()) {
-            in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
-        }
-
-        if (mScene->mMeshes[i]->HasTangentsAndBitangents()) {
-            in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2;
-        }
-
-        for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) {
-            if (mScene->mMeshes[i]->HasVertexColors(a)) {
-                in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices;
-            }
-            else break;
-        }
-        for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
-            if (mScene->mMeshes[i]->HasTextureCoords(a)) {
-                in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
-            }
-            else break;
-        }
-        if (mScene->mMeshes[i]->HasBones()) {
-            in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
-            for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) {
-                in.meshes += sizeof(aiBone);
-                in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight);
-            }
-        }
-        in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces;
-    }
-    in.total += in.meshes;
-
-    // add all embedded textures
-    for (unsigned int i = 0; i < mScene->mNumTextures;++i) {
-        const aiTexture* pc = mScene->mTextures[i];
-        in.textures += sizeof(aiTexture);
-        if (pc->mHeight) {
-            in.textures += 4 * pc->mHeight * pc->mWidth;
-        }
-        else in.textures += pc->mWidth;
-    }
-    in.total += in.textures;
-
-    // add all animations
-    for (unsigned int i = 0; i < mScene->mNumAnimations;++i) {
-        const aiAnimation* pc = mScene->mAnimations[i];
-        in.animations += sizeof(aiAnimation);
-
-        // add all bone anims
-        for (unsigned int a = 0; a < pc->mNumChannels; ++a) {
-            const aiNodeAnim* pc2 = pc->mChannels[i];
-            in.animations += sizeof(aiNodeAnim);
-            in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey);
-            in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey);
-            in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey);
-        }
-    }
-    in.total += in.animations;
-
-    // add all cameras and all lights
-    in.total += in.cameras = sizeof(aiCamera) *  mScene->mNumCameras;
-    in.total += in.lights  = sizeof(aiLight)  *  mScene->mNumLights;
-
-    // add all nodes
-    AddNodeWeight(in.nodes,mScene->mRootNode);
-    in.total += in.nodes;
-
-    // add all materials
-    for (unsigned int i = 0; i < mScene->mNumMaterials;++i) {
-        const aiMaterial* pc = mScene->mMaterials[i];
-        in.materials += sizeof(aiMaterial);
-        in.materials += pc->mNumAllocated * sizeof(void*);
-
-        for (unsigned int a = 0; a < pc->mNumProperties;++a) {
-            in.materials += pc->mProperties[a]->mDataLength;
-        }
-    }
-    in.total += in.materials;
-}

+ 0 - 247
thirdparty/assimp/code/Common/Importer.h

@@ -1,247 +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 Importer.h mostly internal stuff for use by #Assimp::Importer */
-#pragma once
-#ifndef INCLUDED_AI_IMPORTER_H
-#define INCLUDED_AI_IMPORTER_H
-
-#include <map>
-#include <vector>
-#include <string>
-#include <assimp/matrix4x4.h>
-
-struct aiScene;
-
-namespace Assimp    {
-    class ProgressHandler;
-    class IOSystem;
-    class BaseImporter;
-    class BaseProcess;
-    class SharedPostProcessInfo;
-
-
-//! @cond never
-// ---------------------------------------------------------------------------
-/** @brief Internal PIMPL implementation for Assimp::Importer
- *
- *  Using this idiom here allows us to drop the dependency from
- *  std::vector and std::map in the public headers. Furthermore we are dropping
- *  any STL interface problems caused by mismatching STL settings. All
- *  size calculation are now done by us, not the app heap. */
-class ImporterPimpl {
-public:
-    // Data type to store the key hash
-    typedef unsigned int KeyType;
-
-    // typedefs for our four configuration maps.
-    // We don't need more, so there is no need for a generic solution
-    typedef std::map<KeyType, int> IntPropertyMap;
-    typedef std::map<KeyType, ai_real> FloatPropertyMap;
-    typedef std::map<KeyType, std::string> StringPropertyMap;
-    typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
-
-    /** IO handler to use for all file accesses. */
-    IOSystem* mIOHandler;
-    bool mIsDefaultHandler;
-
-    /** Progress handler for feedback. */
-    ProgressHandler* mProgressHandler;
-    bool mIsDefaultProgressHandler;
-
-    /** Format-specific importer worker objects - one for each format we can read.*/
-    std::vector< BaseImporter* > mImporter;
-
-    /** Post processing steps we can apply at the imported data. */
-    std::vector< BaseProcess* > mPostProcessingSteps;
-
-    /** The imported data, if ReadFile() was successful, NULL otherwise. */
-    aiScene* mScene;
-
-    /** The error description, if there was one. */
-    std::string mErrorString;
-
-    /** List of integer properties */
-    IntPropertyMap mIntProperties;
-
-    /** List of floating-point properties */
-    FloatPropertyMap mFloatProperties;
-
-    /** List of string properties */
-    StringPropertyMap mStringProperties;
-
-    /** List of Matrix properties */
-    MatrixPropertyMap mMatrixProperties;
-
-    /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step
-     *  to be executed before and after every single post-process step */
-    bool bExtraVerbose;
-
-    /** Used by post-process steps to share data */
-    SharedPostProcessInfo* mPPShared;
-
-    /// The default class constructor.
-    ImporterPimpl() AI_NO_EXCEPT;
-};
-
-inline
-ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT
-: mIOHandler( nullptr )
-, mIsDefaultHandler( false )
-, mProgressHandler( nullptr )
-, mIsDefaultProgressHandler( false )
-, mImporter()
-, mPostProcessingSteps()
-, mScene( nullptr )
-, mErrorString()
-, mIntProperties()
-, mFloatProperties()
-, mStringProperties()
-, mMatrixProperties()
-, bExtraVerbose( false )
-, mPPShared( nullptr ) {
-    // empty
-}
-//! @endcond
-
-
-struct BatchData;
-
-// ---------------------------------------------------------------------------
-/** FOR IMPORTER PLUGINS ONLY: A helper class to the pleasure of importers
- *  that need to load many external meshes recursively.
- *
- *  The class uses several threads to load these meshes (or at least it
- *  could, this has not yet been implemented at the moment).
- *
- *  @note The class may not be used by more than one thread*/
-class ASSIMP_API BatchLoader
-{
-    // friend of Importer
-
-public:
-    //! @cond never
-    // -------------------------------------------------------------------
-    /** Wraps a full list of configuration properties for an importer.
-     *  Properties can be set using SetGenericProperty */
-    struct PropertyMap
-    {
-        ImporterPimpl::IntPropertyMap     ints;
-        ImporterPimpl::FloatPropertyMap   floats;
-        ImporterPimpl::StringPropertyMap  strings;
-        ImporterPimpl::MatrixPropertyMap  matrices;
-
-        bool operator == (const PropertyMap& prop) const {
-            // fixme: really isocpp? gcc complains
-            return ints == prop.ints && floats == prop.floats && strings == prop.strings && matrices == prop.matrices;
-        }
-
-        bool empty () const {
-            return ints.empty() && floats.empty() && strings.empty() && matrices.empty();
-        }
-    };
-    //! @endcond
-
-public:
-    // -------------------------------------------------------------------
-    /** Construct a batch loader from a given IO system to be used
-     *  to access external files 
-     */
-    explicit BatchLoader(IOSystem* pIO, bool validate = false );
-
-    // -------------------------------------------------------------------
-    /** The class destructor.
-     */
-    ~BatchLoader();
-
-    // -------------------------------------------------------------------
-    /** Sets the validation step. True for enable validation during postprocess.
-     *  @param  enable  True for validation.
-     */
-    void setValidation( bool enabled );
-    
-    // -------------------------------------------------------------------
-    /** Returns the current validation step.
-     *  @return The current validation step.
-     */
-    bool getValidation() const;
-    
-    // -------------------------------------------------------------------
-    /** Add a new file to the list of files to be loaded.
-     *  @param file File to be loaded
-     *  @param steps Post-processing steps to be executed on the file
-     *  @param map Optional configuration properties
-     *  @return 'Load request channel' - an unique ID that can later
-     *    be used to access the imported file data.
-     *  @see GetImport */
-    unsigned int AddLoadRequest (
-        const std::string& file,
-        unsigned int steps = 0,
-        const PropertyMap* map = NULL
-        );
-
-    // -------------------------------------------------------------------
-    /** Get an imported scene.
-     *  This polls the import from the internal request list.
-     *  If an import is requested several times, this function
-     *  can be called several times, too.
-     *
-     *  @param which LRWC returned by AddLoadRequest().
-     *  @return NULL if there is no scene with this file name
-     *  in the queue of the scene hasn't been loaded yet. */
-    aiScene* GetImport(
-        unsigned int which
-        );
-
-    // -------------------------------------------------------------------
-    /** Waits until all scenes have been loaded. This returns
-     *  immediately if no scenes are queued.*/
-    void LoadAll();
-
-private:
-    // No need to have that in the public API ...
-    BatchData *m_data;
-};
-
-} // Namespace Assimp
-
-#endif // INCLUDED_AI_IMPORTER_H

+ 0 - 377
thirdparty/assimp/code/Common/ImporterRegistry.cpp

@@ -1,377 +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 ImporterRegistry.cpp
-
-Central registry for all importers available. Do not edit this file
-directly (unless you are adding new loaders), instead use the
-corresponding preprocessor flag to selectively disable formats.
-*/
-
-#include <vector>
-#include <assimp/BaseImporter.h>
-
-// ------------------------------------------------------------------------------------------------
-// Importers
-// (include_new_importers_here)
-// ------------------------------------------------------------------------------------------------
-#ifndef ASSIMP_BUILD_NO_X_IMPORTER
-#   include "X/XFileImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-#   include "AMF/AMFImporter.hpp"
-#endif
-#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
-#   include "3DS/3DSLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
-#   include "MD3/MD3Loader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
-#   include "MDL/MDLLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
-#   include "MD2/MD2Loader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
-#   include "Ply/PlyLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
-#   include "ASE/ASELoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
-#   include "Obj/ObjFileImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
-#   include "HMP/HMPLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
-#   include "SMD/SMDLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
-#   include "MDC/MDCLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
-#   include "MD5/MD5Loader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
-#   include "STL/STLLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
-#   include "LWO/LWOLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
-#   include "DXF/DXFLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
-#   include "NFF/NFFLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
-#   include "Raw/RawLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
-#   include "SIB/SIBImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
-#   include "OFF/OFFLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
-#   include "AC/ACLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
-#   include "BVH/BVHLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
-#   include "Irr/IRRMeshLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
-#   include "Irr/IRRLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
-#   include "Q3D/Q3DLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
-#   include "B3D/B3DImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
-#   include "Collada/ColladaLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
-#   include "Terragen/TerragenLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
-#   include "CSM/CSMLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
-#   include "Unreal/UnrealLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
-#   include "LWS/LWSLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
-#   include "Ogre/OgreImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
-#   include "OpenGEX/OpenGEXImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
-#   include "MS3D/MS3DLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
-#   include "COB/COBLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
-#   include "Blender/BlenderLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
-#   include "Q3BSP/Q3BSPFileImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
-#   include "NDO/NDOLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
-#   include "Importer/IFC/IFCLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
-#   include "XGL/XGLLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
-#   include "FBX/FBXImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
-#   include "Assbin/AssbinLoader.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
-#   include "glTF/glTFImporter.h"
-#   include "glTF2/glTF2Importer.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
-#   include "C4D/C4DImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
-#   include "3MF/D3MFImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
-#   include "X3D/X3DImporter.hpp"
-#endif
-#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
-#   include "MMD/MMDImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
-#   include "M3D/M3DImporter.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
-#   include "Importer/StepFile/StepFileImporter.h"
-#endif
-
-namespace Assimp {
-
-// ------------------------------------------------------------------------------------------------
-void GetImporterInstanceList(std::vector< BaseImporter* >& out)
-{
-    // ----------------------------------------------------------------------------
-    // Add an instance of each worker class here
-    // (register_new_importers_here)
-    // ----------------------------------------------------------------------------
-    out.reserve(64);
-#if (!defined ASSIMP_BUILD_NO_X_IMPORTER)
-    out.push_back( new XFileImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER)
-    out.push_back( new ObjFileImporter());
-#endif
-#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-	out.push_back( new AMFImporter() );
-#endif
-#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
-    out.push_back( new Discreet3DSImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_M3D_IMPORTER)
-    out.push_back( new M3DImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER)
-    out.push_back( new MD3Importer());
-#endif
-#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER)
-    out.push_back( new MD2Importer());
-#endif
-#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER)
-    out.push_back( new PLYImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER)
-    out.push_back( new MDLImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER)
-  #if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
-    out.push_back( new ASEImporter());
-#  endif
-#endif
-#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER)
-    out.push_back( new HMPImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER)
-    out.push_back( new SMDImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER)
-    out.push_back( new MDCImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER)
-    out.push_back( new MD5Importer());
-#endif
-#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER)
-    out.push_back( new STLImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER)
-    out.push_back( new LWOImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER)
-    out.push_back( new DXFImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER)
-    out.push_back( new NFFImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER)
-    out.push_back( new RAWImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER)
-    out.push_back( new SIBImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER)
-    out.push_back( new OFFImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER)
-    out.push_back( new AC3DImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER)
-    out.push_back( new BVHLoader());
-#endif
-#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER)
-    out.push_back( new IRRMeshImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER)
-    out.push_back( new IRRImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER)
-    out.push_back( new Q3DImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER)
-    out.push_back( new B3DImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER)
-    out.push_back( new ColladaLoader());
-#endif
-#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER)
-    out.push_back( new TerragenImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER)
-    out.push_back( new CSMImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER)
-    out.push_back( new UnrealImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
-    out.push_back( new LWSImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER)
-    out.push_back( new Ogre::OgreImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER )
-    out.push_back( new OpenGEX::OpenGEXImporter() );
-#endif
-#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER)
-    out.push_back( new MS3DImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER)
-    out.push_back( new COBImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER)
-    out.push_back( new BlenderImporter());
-#endif
-#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER)
-    out.push_back( new Q3BSPFileImporter() );
-#endif
-#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER)
-    out.push_back( new NDOImporter() );
-#endif
-#if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER)
-    out.push_back( new IFCImporter() );
-#endif
-#if ( !defined ASSIMP_BUILD_NO_XGL_IMPORTER )
-    out.push_back( new XGLImporter() );
-#endif
-#if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER )
-    out.push_back( new FBXImporter() );
-#endif
-#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER )
-    out.push_back( new AssbinImporter() );
-#endif
-#if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER )
-    out.push_back( new glTFImporter() );
-    out.push_back( new glTF2Importer() );
-#endif
-#if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER )
-    out.push_back( new C4DImporter() );
-#endif
-#if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER )
-    out.push_back( new D3MFImporter() );
-#endif
-#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
-    out.push_back( new X3DImporter() );
-#endif
-#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
-    out.push_back( new MMDImporter() );
-#endif
-#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
-    out.push_back(new StepFile::StepFileImporter());
-#endif
-}
-
-/** will delete all registered importers. */
-void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){
-	for(size_t i= 0; i<deleteList.size();++i){
-		delete deleteList[i];
-		deleteList[i]=nullptr;
-	}//for
-}
-
-} // namespace Assimp

+ 0 - 229
thirdparty/assimp/code/Common/PolyTools.h

@@ -1,229 +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 PolyTools.h, various utilities for our dealings with arbitrary polygons */
-
-#ifndef AI_POLYTOOLS_H_INCLUDED
-#define AI_POLYTOOLS_H_INCLUDED
-
-#include <assimp/material.h>
-#include <assimp/ai_assert.h>
-
-namespace Assimp {
-
-// -------------------------------------------------------------------------------
-/** Compute the signed area of a triangle.
- *  The function accepts an unconstrained template parameter for use with
- *  both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
-template <typename T>
-inline double GetArea2D(const T& v1, const T& v2, const T& v3)
-{
-    return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y));
-}
-
-// -------------------------------------------------------------------------------
-/** Test if a given point p2 is on the left side of the line formed by p0-p1.
- *  The function accepts an unconstrained template parameter for use with
- *  both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
-template <typename T>
-inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2)
-{
-    return GetArea2D(p0,p2,p1) > 0;
-}
-
-// -------------------------------------------------------------------------------
-/** Test if a given point is inside a given triangle in R2.
- * The function accepts an unconstrained template parameter for use with
- *  both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
-template <typename T>
-inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp)
-{
-    // Point in triangle test using baryzentric coordinates
-    const aiVector2D v0 = p1 - p0;
-    const aiVector2D v1 = p2 - p0;
-    const aiVector2D v2 = pp - p0;
-
-    double dot00 = v0 * v0;
-    double dot01 = v0 * v1;
-    double dot02 = v0 * v2;
-    double dot11 = v1 * v1;
-    double dot12 = v1 * v2;
-
-    const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
-    dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom;
-    dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom;
-
-    return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1);
-}
-
-
-// -------------------------------------------------------------------------------
-/** Check whether the winding order of a given polygon is counter-clockwise.
- *  The function accepts an unconstrained template parameter, but is intended
- *  to be used only with aiVector2D and aiVector3D (z axis is ignored, only
- *  x and y are taken into account).
- * @note Code taken from http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/applet1.html and translated to C++
- */
-template <typename T>
-inline bool IsCCW(T* in, size_t npoints) {
-    double aa, bb, cc, b, c, theta;
-    double convex_turn;
-    double convex_sum = 0;
-
-    ai_assert(npoints >= 3);
-
-    for (size_t i = 0; i < npoints - 2; i++) {
-        aa = ((in[i+2].x - in[i].x) * (in[i+2].x - in[i].x)) +
-            ((-in[i+2].y + in[i].y) * (-in[i+2].y + in[i].y));
-
-        bb = ((in[i+1].x - in[i].x) * (in[i+1].x - in[i].x)) +
-            ((-in[i+1].y + in[i].y) * (-in[i+1].y + in[i].y));
-
-        cc = ((in[i+2].x - in[i+1].x) *
-            (in[i+2].x - in[i+1].x)) +
-            ((-in[i+2].y + in[i+1].y) *
-            (-in[i+2].y + in[i+1].y));
-
-        b = std::sqrt(bb);
-        c = std::sqrt(cc);
-        theta = std::acos((bb + cc - aa) / (2 * b * c));
-
-        if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) {
-            //  if (convex(in[i].x, in[i].y,
-            //      in[i+1].x, in[i+1].y,
-            //      in[i+2].x, in[i+2].y)) {
-            convex_turn = AI_MATH_PI_F - theta;
-            convex_sum += convex_turn;
-        }
-        else {
-            convex_sum -= AI_MATH_PI_F - theta;
-        }
-    }
-    aa = ((in[1].x - in[npoints-2].x) *
-        (in[1].x - in[npoints-2].x)) +
-        ((-in[1].y + in[npoints-2].y) *
-        (-in[1].y + in[npoints-2].y));
-
-    bb = ((in[0].x - in[npoints-2].x) *
-        (in[0].x - in[npoints-2].x)) +
-        ((-in[0].y + in[npoints-2].y) *
-        (-in[0].y + in[npoints-2].y));
-
-    cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) +
-        ((-in[1].y + in[0].y) * (-in[1].y + in[0].y));
-
-    b = std::sqrt(bb);
-    c = std::sqrt(cc);
-    theta = std::acos((bb + cc - aa) / (2 * b * c));
-
-    //if (convex(in[npoints-2].x, in[npoints-2].y,
-    //  in[0].x, in[0].y,
-    //  in[1].x, in[1].y)) {
-    if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) {
-        convex_turn = AI_MATH_PI_F - theta;
-        convex_sum += convex_turn;
-    }
-    else {
-        convex_sum -= AI_MATH_PI_F - theta;
-    }
-
-    return convex_sum >= (2 * AI_MATH_PI_F);
-}
-
-
-// -------------------------------------------------------------------------------
-/** Compute the normal of an arbitrary polygon in R3.
- *
- *  The code is based on Newell's formula, that is a polygons normal is the ratio
- *  of its area when projected onto the three coordinate axes.
- *
- *  @param out Receives the output normal
- *  @param num Number of input vertices
- *  @param x X data source. x[ofs_x*n] is the n'th element.
- *  @param y Y data source. y[ofs_y*n] is the y'th element
- *  @param z Z data source. z[ofs_z*n] is the z'th element
- *
- *  @note The data arrays must have storage for at least num+2 elements. Using
- *  this method is much faster than the 'other' NewellNormal()
- */
-template <int ofs_x, int ofs_y, int ofs_z, typename TReal>
-inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, TReal* z)
-{
-    // Duplicate the first two vertices at the end
-    x[(num+0)*ofs_x] = x[0];
-    x[(num+1)*ofs_x] = x[ofs_x];
-
-    y[(num+0)*ofs_y] = y[0];
-    y[(num+1)*ofs_y] = y[ofs_y];
-
-    z[(num+0)*ofs_z] = z[0];
-    z[(num+1)*ofs_z] = z[ofs_z];
-
-    TReal sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0;
-
-    TReal *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2;
-    TReal *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2;
-    TReal *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2;
-
-    for (int tmp=0; tmp < num; tmp++) {
-        sum_xy += (*xptr) * ( (*yhigh) - (*ylow) );
-        sum_yz += (*yptr) * ( (*zhigh) - (*zlow) );
-        sum_zx += (*zptr) * ( (*xhigh) - (*xlow) );
-
-        xptr  += ofs_x;
-        xlow  += ofs_x;
-        xhigh += ofs_x;
-
-        yptr  += ofs_y;
-        ylow  += ofs_y;
-        yhigh += ofs_y;
-
-        zptr  += ofs_z;
-        zlow  += ofs_z;
-        zhigh += ofs_z;
-    }
-    out = aiVector3t<TReal>(sum_yz,sum_zx,sum_xy);
-}
-
-} // ! Assimp
-
-#endif

+ 0 - 265
thirdparty/assimp/code/Common/PostStepRegistry.cpp

@@ -1,265 +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 ImporterRegistry.cpp
-
-Central registry for all postprocessing steps available. Do not edit this file
-directly (unless you are adding new steps), instead use the
-corresponding preprocessor flag to selectively disable steps.
-*/
-
-#include "PostProcessing/ProcessHelper.h"
-
-#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS
-#   include "PostProcessing/CalcTangentsProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
-#   include "PostProcessing/JoinVerticesProcess.h"
-#endif
-#if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
-#   include "PostProcessing/ConvertToLHProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
-#   include "PostProcessing/TriangulateProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS
-#   include "PostProcessing/DropFaceNormalsProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS
-#   include "PostProcessing/GenFaceNormalsProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS
-#   include "PostProcessing/GenVertexNormalsProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS
-#   include "PostProcessing/RemoveVCProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS
-#   include "PostProcessing/SplitLargeMeshes.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS
-#   include "PostProcessing/PretransformVertices.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS
-#   include "PostProcessing/LimitBoneWeightsProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-#   include "PostProcessing/ValidateDataStructure.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS
-#   include "PostProcessing/ImproveCacheLocality.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS
-#   include "PostProcessing/FixNormalsStep.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS
-#   include "PostProcessing/RemoveRedundantMaterials.h"
-#endif
-#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS)
-#   include "PostProcessing/EmbedTexturesProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
-#   include "PostProcessing/FindInvalidDataProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS
-#   include "PostProcessing/FindDegenerates.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS
-#   include "PostProcessing/SortByPTypeProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
-#   include "PostProcessing/ComputeUVMappingProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
-#   include "PostProcessing/TextureTransform.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS
-#   include "PostProcessing/FindInstancesProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
-#   include "PostProcessing/OptimizeMeshes.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
-#   include "PostProcessing/OptimizeGraph.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
-#   include "Common/SplitByBoneCountProcess.h"
-#endif
-#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
-#   include "PostProcessing/DeboneProcess.h"
-#endif
-#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
-#   include "PostProcessing/ScaleProcess.h"
-#endif
-#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
-#   include "PostProcessing/ArmaturePopulate.h"
-#endif
-#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
-#   include "PostProcessing/GenBoundingBoxesProcess.h"
-#endif
-
-
-
-namespace Assimp {
-
-// ------------------------------------------------------------------------------------------------
-void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
-{
-    // ----------------------------------------------------------------------------
-    // Add an instance of each post processing step here in the order
-    // of sequence it is executed. Steps that are added here are not
-    // validated - as RegisterPPStep() does - all dependencies must be given.
-    // ----------------------------------------------------------------------------
-    out.reserve(31);
-#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS)
-    out.push_back( new MakeLeftHandedProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS)
-    out.push_back( new FlipUVsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
-    out.push_back( new FlipWindingOrderProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS)
-    out.push_back( new RemoveVCProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
-    out.push_back( new RemoveRedundantMatsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS)
-    out.push_back( new EmbedTexturesProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS)
-    out.push_back( new FindInstancesProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
-    out.push_back( new OptimizeGraphProcess());
-#endif
-#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
-    out.push_back( new ComputeUVMappingProcess());
-#endif
-#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
-    out.push_back( new TextureTransformStep());
-#endif
-#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
-    out.push_back( new ScaleProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
-    out.push_back( new ArmaturePopulate());
-#endif
-#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
-    out.push_back( new PretransformVertices());
-#endif
-#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS)
-    out.push_back( new TriangulateProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS)
-    //find degenerates should run after triangulation (to sort out small
-    //generated triangles) but before sort by p types (in case there are lines
-    //and points generated and inserted into a mesh)
-    out.push_back( new FindDegeneratesProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS)
-    out.push_back( new SortByPTypeProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS)
-    out.push_back( new FindInvalidDataProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS)
-    out.push_back( new OptimizeMeshesProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS)
-    out.push_back( new FixInfacingNormalsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS)
-    out.push_back( new SplitByBoneCountProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
-    out.push_back( new SplitLargeMeshesProcess_Triangle());
-#endif
-#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
-    out.push_back( new DropFaceNormalsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
-    out.push_back( new GenFaceNormalsProcess());
-#endif
-    // .........................................................................
-    // DON'T change the order of these five ..
-    // XXX this is actually a design weakness that dates back to the time
-    // when Importer would maintain the postprocessing step list exclusively.
-    // Now that others access it too, we need a better solution.
-    out.push_back( new ComputeSpatialSortProcess());
-    // .........................................................................
-
-#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS)
-    out.push_back( new GenVertexNormalsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS)
-    out.push_back( new CalcTangentsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS)
-    out.push_back( new JoinVerticesProcess());
-#endif
-
-    // .........................................................................
-    out.push_back( new DestroySpatialSortProcess());
-    // .........................................................................
-
-#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
-    out.push_back( new SplitLargeMeshesProcess_Vertex());
-#endif
-#if (!defined ASSIMP_BUILD_NO_DEBONE_PROCESS)
-    out.push_back( new DeboneProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
-    out.push_back( new LimitBoneWeightsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS)
-    out.push_back( new ImproveCacheLocalityProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
-    out.push_back(new GenBoundingBoxesProcess);
-#endif
-}
-
-}

+ 0 - 113
thirdparty/assimp/code/Common/RemoveComments.cpp

@@ -1,113 +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  RemoveComments.cpp
- *  @brief Defines the CommentRemover utility class
- */
-
-#include <assimp/RemoveComments.h>
-#include <assimp/ParsingUtils.h>
-
-namespace Assimp    {
-
-// ------------------------------------------------------------------------------------------------
-// Remove line comments from a file
-void CommentRemover::RemoveLineComments(const char* szComment,
-    char* szBuffer, char chReplacement /* = ' ' */)
-{
-    // validate parameters
-    ai_assert(NULL != szComment && NULL != szBuffer && *szComment);
-
-    const size_t len = strlen(szComment);
-    while (*szBuffer)   {
-
-        // skip over quotes
-        if (*szBuffer == '\"' || *szBuffer == '\'')
-            while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'');
-
-        if (!strncmp(szBuffer,szComment,len)) {
-            while (!IsLineEnd(*szBuffer))
-                *szBuffer++ = chReplacement;
-
-            if (!*szBuffer) {
-                break;
-            }
-        }
-        ++szBuffer;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Remove multi-line comments from a file
-void CommentRemover::RemoveMultiLineComments(const char* szCommentStart,
-    const char* szCommentEnd,char* szBuffer,
-    char chReplacement)
-{
-    // validate parameters
-    ai_assert(NULL != szCommentStart && NULL != szCommentEnd &&
-        NULL != szBuffer && *szCommentStart && *szCommentEnd);
-
-    const size_t len  = strlen(szCommentEnd);
-    const size_t len2 = strlen(szCommentStart);
-
-    while (*szBuffer)   {
-        // skip over quotes
-        if (*szBuffer == '\"' || *szBuffer == '\'')
-            while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'');
-
-        if (!strncmp(szBuffer,szCommentStart,len2))  {
-            while (*szBuffer) {
-                if (!::strncmp(szBuffer,szCommentEnd,len)) {
-                    for (unsigned int i = 0; i < len;++i)
-                        *szBuffer++ = chReplacement;
-
-                    break;
-                }
-            *szBuffer++ = chReplacement;
-            }
-            continue;
-        }
-        ++szBuffer;
-    }
-}
-
-} // !! Assimp

+ 0 - 168
thirdparty/assimp/code/Common/SGSpatialSort.cpp

@@ -1,168 +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 Implementation of the helper class to quickly find
-vertices close to a given position. Special implementation for
-the 3ds loader handling smooth groups correctly  */
-
-#include <assimp/SGSpatialSort.h>
-
-using namespace Assimp;
-
-// ------------------------------------------------------------------------------------------------
-SGSpatialSort::SGSpatialSort()
-{
-    // define the reference plane. We choose some arbitrary vector away from all basic axises
-    // in the hope that no model spreads all its vertices along this plane.
-    mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
-    mPlaneNormal.Normalize();
-}
-// ------------------------------------------------------------------------------------------------
-// Destructor
-SGSpatialSort::~SGSpatialSort()
-{
-    // nothing to do here, everything destructs automatically
-}
-// ------------------------------------------------------------------------------------------------
-void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index,
-    unsigned int smoothingGroup)
-{
-    // store position by index and distance
-    float distance = vPosition * mPlaneNormal;
-    mPositions.push_back( Entry( index, vPosition,
-        distance, smoothingGroup));
-}
-// ------------------------------------------------------------------------------------------------
-void SGSpatialSort::Prepare()
-{
-    // now sort the array ascending by distance.
-    std::sort( this->mPositions.begin(), this->mPositions.end());
-}
-// ------------------------------------------------------------------------------------------------
-// Returns an iterator for all positions close to the given position.
-void SGSpatialSort::FindPositions( const aiVector3D& pPosition,
-    uint32_t pSG,
-    float pRadius,
-    std::vector<unsigned int>& poResults,
-    bool exactMatch /*= false*/) const
-{
-    float dist = pPosition * mPlaneNormal;
-    float minDist = dist - pRadius, maxDist = dist + pRadius;
-
-    // clear the array
-    poResults.clear();
-
-    // quick check for positions outside the range
-    if( mPositions.empty() )
-        return;
-    if( maxDist < mPositions.front().mDistance)
-        return;
-    if( minDist > mPositions.back().mDistance)
-        return;
-
-    // do a binary search for the minimal distance to start the iteration there
-    unsigned int index = (unsigned int)mPositions.size() / 2;
-    unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
-    while( binaryStepSize > 1)
-    {
-        if( mPositions[index].mDistance < minDist)
-            index += binaryStepSize;
-        else
-            index -= binaryStepSize;
-
-        binaryStepSize /= 2;
-    }
-
-    // depending on the direction of the last step we need to single step a bit back or forth
-    // to find the actual beginning element of the range
-    while( index > 0 && mPositions[index].mDistance > minDist)
-        index--;
-    while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
-        index++;
-
-    // Mow start iterating from there until the first position lays outside of the distance range.
-    // Add all positions inside the distance range within the given radius to the result aray
-
-    float squareEpsilon = pRadius * pRadius;
-    std::vector<Entry>::const_iterator it  = mPositions.begin() + index;
-    std::vector<Entry>::const_iterator end = mPositions.end();
-
-    if (exactMatch)
-    {
-        while( it->mDistance < maxDist)
-        {
-            if((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG)
-            {
-                poResults.push_back( it->mIndex);
-            }
-            ++it;
-            if( end == it )break;
-        }
-    }
-    else
-    {
-        // if the given smoothing group is 0, we'll return all surrounding vertices
-        if (!pSG)
-        {
-            while( it->mDistance < maxDist)
-            {
-                if((it->mPosition - pPosition).SquareLength() < squareEpsilon)
-                    poResults.push_back( it->mIndex);
-                ++it;
-                if( end == it)break;
-            }
-        }
-        else while( it->mDistance < maxDist)
-        {
-            if((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
-                (it->mSmoothGroups & pSG || !it->mSmoothGroups))
-            {
-                poResults.push_back( it->mIndex);
-            }
-            ++it;
-            if( end == it)break;
-        }
-    }
-}
-
-

+ 0 - 1350
thirdparty/assimp/code/Common/SceneCombiner.cpp

@@ -1,1350 +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.
-
-----------------------------------------------------------------------
-*/
-
-// TODO: refactor entire file to get rid of the "flat-copy" first approach
-// to copying structures. This easily breaks in the most unintuitive way
-// possible as new fields are added to assimp structures.
-
-// ----------------------------------------------------------------------------
-/** 
-  * @file Implements Assimp::SceneCombiner. This is a smart utility
-  *       class that combines multiple scenes, meshes, ... into one. Currently
-  *       these utilities are used by the IRR and LWS loaders and the
-  *       OptimizeGraph step.
-  */
-// ----------------------------------------------------------------------------
-#include <assimp/SceneCombiner.h>
-#include <assimp/StringUtils.h>
-#include <assimp/fast_atof.h>
-#include <assimp/metadata.h>
-#include <assimp/Hash.h>
-#include "time.h"
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/scene.h>
-#include <assimp/mesh.h>
-#include <stdio.h>
-#include "ScenePrivate.h"
-
-namespace Assimp {
-
-// ------------------------------------------------------------------------------------------------
-// Add a prefix to a string
-inline
-void PrefixString(aiString& string,const char* prefix, unsigned int len) {
-    // If the string is already prefixed, we won't prefix it a second time
-    if (string.length >= 1 && string.data[0] == '$')
-        return;
-
-    if (len+string.length>=MAXLEN-1) {
-        ASSIMP_LOG_DEBUG("Can't add an unique prefix because the string is too long");
-        ai_assert(false);
-        return;
-    }
-
-    // Add the prefix
-    ::memmove(string.data+len,string.data,string.length+1);
-    ::memcpy (string.data, prefix, len);
-
-    // And update the string's length
-    string.length += len;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Add node identifiers to a hashing set
-void SceneCombiner::AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes) {
-    // Add node name to hashing set if it is non-empty - empty nodes are allowed
-    // and they can't have any anims assigned so its absolutely safe to duplicate them.
-    if (node->mName.length) {
-        hashes.insert( SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)) );
-    }
-
-    // Process all children recursively
-    for (unsigned int i = 0; i < node->mNumChildren;++i)
-        AddNodeHashes(node->mChildren[i],hashes);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Add a name prefix to all nodes in a hierarchy
-void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len) {
-    ai_assert(NULL != prefix);
-    PrefixString(node->mName,prefix,len);
-
-    // Process all children recursively
-    for ( unsigned int i = 0; i < node->mNumChildren; ++i ) {
-        AddNodePrefixes( node->mChildren[ i ], prefix, len );
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Search for matching names
-bool SceneCombiner::FindNameMatch(const aiString& name, std::vector<SceneHelper>& input, unsigned int cur) {
-    const unsigned int hash = SuperFastHash(name.data, static_cast<uint32_t>(name.length));
-
-    // Check whether we find a positive match in one of the given sets
-    for (unsigned int i = 0; i < input.size(); ++i) {
-        if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Add a name prefix to all nodes in a hierarchy if a hash match is found
-void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, unsigned int len,
-        std::vector<SceneHelper>& input, unsigned int cur) {
-    ai_assert(NULL != prefix);
-    const unsigned int hash = SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length));
-
-    // Check whether we find a positive match in one of the given sets
-    for (unsigned int i = 0; i < input.size(); ++i) {
-        if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
-            PrefixString(node->mName,prefix,len);
-            break;
-        }
-    }
-
-    // Process all children recursively
-    for (unsigned int i = 0; i < node->mNumChildren;++i)
-        AddNodePrefixesChecked(node->mChildren[i],prefix,len,input,cur);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Add an offset to all mesh indices in a node graph
-void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset) {
-    for (unsigned int i = 0; i < node->mNumMeshes;++i)
-        node->mMeshes[i] += offset;
-
-    for ( unsigned int i = 0; i < node->mNumChildren; ++i ) {
-        OffsetNodeMeshIndices( node->mChildren[ i ], offset );
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Merges two scenes. Currently only used by the LWS loader.
-void SceneCombiner::MergeScenes(aiScene** _dest,std::vector<aiScene*>& src, unsigned int flags) {
-    if ( nullptr == _dest ) {
-        return;
-    }
-
-    // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it
-    if (src.empty()) {
-        if (*_dest) {
-            (*_dest)->~aiScene();
-            SceneCombiner::CopySceneFlat(_dest,src[0]);
-        }
-        else *_dest = src[0];
-        return;
-    }
-    if (*_dest)(*_dest)->~aiScene();
-    else *_dest = new aiScene();
-
-    // Create a dummy scene to serve as master for the others
-    aiScene* master = new aiScene();
-    master->mRootNode = new aiNode();
-    master->mRootNode->mName.Set("<MergeRoot>");
-
-    std::vector<AttachmentInfo> srcList (src.size());
-    for (unsigned int i = 0; i < srcList.size();++i)    {
-        srcList[i] = AttachmentInfo(src[i],master->mRootNode);
-    }
-
-    // 'master' will be deleted afterwards
-    MergeScenes (_dest, master, srcList, flags);
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::AttachToGraph (aiNode* attach, std::vector<NodeAttachmentInfo>& srcList) {
-    unsigned int cnt;
-    for ( cnt = 0; cnt < attach->mNumChildren; ++cnt ) {
-        AttachToGraph( attach->mChildren[ cnt ], srcList );
-    }
-
-    cnt = 0;
-    for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin();
-         it != srcList.end(); ++it)
-    {
-        if ((*it).attachToNode == attach && !(*it).resolved)
-            ++cnt;
-    }
-
-    if (cnt)    {
-        aiNode** n = new aiNode*[cnt+attach->mNumChildren];
-        if (attach->mNumChildren)   {
-            ::memcpy(n,attach->mChildren,sizeof(void*)*attach->mNumChildren);
-            delete[] attach->mChildren;
-        }
-        attach->mChildren = n;
-
-        n += attach->mNumChildren;
-        attach->mNumChildren += cnt;
-
-        for (unsigned int i = 0; i < srcList.size();++i)    {
-            NodeAttachmentInfo& att = srcList[i];
-            if (att.attachToNode == attach && !att.resolved)    {
-                *n = att.node;
-                (**n).mParent = attach;
-                ++n;
-
-                // mark this attachment as resolved
-                att.resolved = true;
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::AttachToGraph ( aiScene* master, std::vector<NodeAttachmentInfo>& src) {
-    ai_assert(NULL != master);
-    AttachToGraph(master->mRootNode,src);
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master, std::vector<AttachmentInfo>& srcList, unsigned int flags) {
-    if ( nullptr == _dest ) {
-        return;
-    }
-
-    // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it
-    if (srcList.empty())    {
-        if (*_dest) {
-            SceneCombiner::CopySceneFlat(_dest,master);
-        }
-        else *_dest = master;
-        return;
-    }
-    if (*_dest) {
-        (*_dest)->~aiScene();
-        new (*_dest) aiScene();
-    }
-    else *_dest = new aiScene();
-
-    aiScene* dest = *_dest;
-
-    std::vector<SceneHelper> src (srcList.size()+1);
-    src[0].scene = master;
-    for (unsigned int i = 0; i < srcList.size();++i)    {
-        src[i+1] = SceneHelper( srcList[i].scene );
-    }
-
-    // this helper array specifies which scenes are duplicates of others
-    std::vector<unsigned int> duplicates(src.size(),UINT_MAX);
-
-    // this helper array is used as lookup table several times
-    std::vector<unsigned int> offset(src.size());
-
-    // Find duplicate scenes
-    for (unsigned int i = 0; i < src.size();++i) {
-        if (duplicates[i] != i && duplicates[i] != UINT_MAX) {
-            continue;
-        }
-
-        duplicates[i] = i;
-        for ( unsigned int a = i+1; a < src.size(); ++a)    {
-            if (src[i].scene == src[a].scene) {
-                duplicates[a] = i;
-            }
-        }
-    }
-
-    // Generate unique names for all named stuff?
-    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES)
-    {
-#if 0
-        // Construct a proper random number generator
-        boost::mt19937 rng(  );
-        boost::uniform_int<> dist(1u,1 << 24u);
-        boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rndGen(rng, dist);
-#endif
-        for (unsigned int i = 1; i < src.size();++i)
-        {
-            //if (i != duplicates[i])
-            //{
-            //  // duplicate scenes share the same UID
-            //  ::strcpy( src[i].id, src[duplicates[i]].id );
-            //  src[i].idlen = src[duplicates[i]].idlen;
-
-            //  continue;
-            //}
-
-            src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_",i);
-
-            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
-
-                // Compute hashes for all identifiers in this scene and store them
-                // in a sorted table (for convenience I'm using std::set). We hash
-                // just the node and animation channel names, all identifiers except
-                // the material names should be caught by doing this.
-                AddNodeHashes(src[i]->mRootNode,src[i].hashes);
-
-                for (unsigned int a = 0; a < src[i]->mNumAnimations;++a) {
-                    aiAnimation* anim = src[i]->mAnimations[a];
-                    src[i].hashes.insert(SuperFastHash(anim->mName.data,static_cast<uint32_t>(anim->mName.length)));
-                }
-            }
-        }
-    }
-
-    unsigned int cnt;
-
-    // First find out how large the respective output arrays must be
-    for ( unsigned int n = 0; n < src.size();++n )
-    {
-        SceneHelper* cur = &src[n];
-
-        if (n == duplicates[n] || flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)   {
-            dest->mNumTextures   += (*cur)->mNumTextures;
-            dest->mNumMaterials  += (*cur)->mNumMaterials;
-            dest->mNumMeshes     += (*cur)->mNumMeshes;
-        }
-
-        dest->mNumLights     += (*cur)->mNumLights;
-        dest->mNumCameras    += (*cur)->mNumCameras;
-        dest->mNumAnimations += (*cur)->mNumAnimations;
-
-        // Combine the flags of all scenes
-        // We need to process them flag-by-flag here to get correct results
-        // dest->mFlags ; //|= (*cur)->mFlags;
-        if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
-            dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
-        }
-    }
-
-    // generate the output texture list + an offset table for all texture indices
-    if (dest->mNumTextures)
-    {
-        aiTexture** pip = dest->mTextures = new aiTexture*[dest->mNumMaterials];
-        cnt = 0;
-        for ( unsigned int n = 0; n < src.size();++n )
-        {
-            SceneHelper* cur = &src[n];
-            for (unsigned int i = 0; i < (*cur)->mNumTextures;++i)
-            {
-                if (n != duplicates[n])
-                {
-                    if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
-                        Copy(pip,(*cur)->mTextures[i]);
-
-                    else continue;
-                }
-                else *pip = (*cur)->mTextures[i];
-                ++pip;
-            }
-
-            offset[n] = cnt;
-            cnt = (unsigned int)(pip - dest->mTextures);
-        }
-    }
-
-    // generate the output material list + an offset table for all material indices
-    if (dest->mNumMaterials)
-    {
-        aiMaterial** pip = dest->mMaterials = new aiMaterial*[dest->mNumMaterials];
-        cnt = 0;
-        for ( unsigned int n = 0; n < src.size();++n )  {
-            SceneHelper* cur = &src[n];
-            for (unsigned int i = 0; i < (*cur)->mNumMaterials;++i)
-            {
-                if (n != duplicates[n])
-                {
-                    if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
-                        Copy(pip,(*cur)->mMaterials[i]);
-
-                    else continue;
-                }
-                else *pip = (*cur)->mMaterials[i];
-
-                if ((*cur)->mNumTextures != dest->mNumTextures)     {
-                    // We need to update all texture indices of the mesh. So we need to search for
-                    // a material property called '$tex.file'
-
-                    for (unsigned int a = 0; a < (*pip)->mNumProperties;++a)
-                    {
-                        aiMaterialProperty* prop = (*pip)->mProperties[a];
-                        if (!strncmp(prop->mKey.data,"$tex.file",9))
-                        {
-                            // Check whether this texture is an embedded texture.
-                            // In this case the property looks like this: *<n>,
-                            // where n is the index of the texture.
-                            aiString& s = *((aiString*)prop->mData);
-                            if ('*' == s.data[0])   {
-                                // Offset the index and write it back ..
-                                const unsigned int idx = strtoul10(&s.data[1]) + offset[n];
-                                ASSIMP_itoa10(&s.data[1],sizeof(s.data)-1,idx);
-                            }
-                        }
-
-                        // Need to generate new, unique material names?
-                        else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES)
-                        {
-                            aiString* pcSrc = (aiString*) prop->mData;
-                            PrefixString(*pcSrc, (*cur).id, (*cur).idlen);
-                        }
-                    }
-                }
-                ++pip;
-            }
-
-            offset[n] = cnt;
-            cnt = (unsigned int)(pip - dest->mMaterials);
-        }
-    }
-
-    // generate the output mesh list + again an offset table for all mesh indices
-    if (dest->mNumMeshes)
-    {
-        aiMesh** pip = dest->mMeshes = new aiMesh*[dest->mNumMeshes];
-        cnt = 0;
-        for ( unsigned int n = 0; n < src.size();++n )
-        {
-            SceneHelper* cur = &src[n];
-            for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i)
-            {
-                if (n != duplicates[n]) {
-                    if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
-                        Copy(pip, (*cur)->mMeshes[i]);
-
-                    else continue;
-                }
-                else *pip = (*cur)->mMeshes[i];
-
-                // update the material index of the mesh
-                (*pip)->mMaterialIndex +=  offset[n];
-                ++pip;
-            }
-
-            // reuse the offset array - store now the mesh offset in it
-            offset[n] = cnt;
-            cnt = (unsigned int)(pip - dest->mMeshes);
-        }
-    }
-
-    std::vector <NodeAttachmentInfo> nodes;
-    nodes.reserve(srcList.size());
-
-    // ----------------------------------------------------------------------------
-    // Now generate the output node graph. We need to make those
-    // names in the graph that are referenced by anims or lights
-    // or cameras unique. So we add a prefix to them ... $<rand>_
-    // We could also use a counter, but using a random value allows us to
-    // use just one prefix if we are joining multiple scene hierarchies recursively.
-    // Chances are quite good we don't collide, so we try that ...
-    // ----------------------------------------------------------------------------
-
-    // Allocate space for light sources, cameras and animations
-    aiLight** ppLights = dest->mLights = (dest->mNumLights
-        ? new aiLight*[dest->mNumLights] : NULL);
-
-    aiCamera** ppCameras = dest->mCameras = (dest->mNumCameras
-        ? new aiCamera*[dest->mNumCameras] : NULL);
-
-    aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations
-        ? new aiAnimation*[dest->mNumAnimations] : NULL);
-
-    for ( int n = static_cast<int>(src.size()-1); n >= 0 ;--n ) /* !!! important !!! */
-    {
-        SceneHelper* cur = &src[n];
-        aiNode* node;
-
-        // To offset or not to offset, this is the question
-        if (n != (int)duplicates[n])
-        {
-            // Get full scene-graph copy
-            Copy( &node, (*cur)->mRootNode );
-            OffsetNodeMeshIndices(node,offset[duplicates[n]]);
-
-            if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
-                // (note:) they are already 'offseted' by offset[duplicates[n]]
-                OffsetNodeMeshIndices(node,offset[n] - offset[duplicates[n]]);
-            }
-        }
-        else // if (n == duplicates[n])
-        {
-            node = (*cur)->mRootNode;
-            OffsetNodeMeshIndices(node,offset[n]);
-        }
-        if (n) // src[0] is the master node
-            nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n ));
-
-        // add name prefixes?
-        if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
-
-            // or the whole scenegraph
-            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
-                AddNodePrefixesChecked(node,(*cur).id,(*cur).idlen,src,n);
-            }
-            else AddNodePrefixes(node,(*cur).id,(*cur).idlen);
-
-            // meshes
-            for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i)    {
-                aiMesh* mesh = (*cur)->mMeshes[i];
-
-                // rename all bones
-                for (unsigned int a = 0; a < mesh->mNumBones;++a)   {
-                    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
-                        if (!FindNameMatch(mesh->mBones[a]->mName,src,n))
-                            continue;
-                    }
-                    PrefixString(mesh->mBones[a]->mName,(*cur).id,(*cur).idlen);
-                }
-            }
-        }
-
-        // --------------------------------------------------------------------
-        // Copy light sources
-        for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights)
-        {
-            if (n != (int)duplicates[n]) // duplicate scene?
-            {
-                Copy(ppLights, (*cur)->mLights[i]);
-            }
-            else *ppLights = (*cur)->mLights[i];
-
-
-            // Add name prefixes?
-            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
-                if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
-                    if (!FindNameMatch((*ppLights)->mName,src,n))
-                        continue;
-                }
-
-                PrefixString((*ppLights)->mName,(*cur).id,(*cur).idlen);
-            }
-        }
-
-        // --------------------------------------------------------------------
-        // Copy cameras
-        for (unsigned int i = 0; i < (*cur)->mNumCameras;++i,++ppCameras)   {
-            if (n != (int)duplicates[n]) // duplicate scene?
-            {
-                Copy(ppCameras, (*cur)->mCameras[i]);
-            }
-            else *ppCameras = (*cur)->mCameras[i];
-
-            // Add name prefixes?
-            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
-                if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
-                    if (!FindNameMatch((*ppCameras)->mName,src,n))
-                        continue;
-                }
-
-                PrefixString((*ppCameras)->mName,(*cur).id,(*cur).idlen);
-            }
-        }
-
-        // --------------------------------------------------------------------
-        // Copy animations
-        for (unsigned int i = 0; i < (*cur)->mNumAnimations;++i,++ppAnims)  {
-            if (n != (int)duplicates[n]) // duplicate scene?
-            {
-                Copy(ppAnims, (*cur)->mAnimations[i]);
-            }
-            else *ppAnims = (*cur)->mAnimations[i];
-
-            // Add name prefixes?
-            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
-                if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
-                    if (!FindNameMatch((*ppAnims)->mName,src,n))
-                        continue;
-                }
-
-                PrefixString((*ppAnims)->mName,(*cur).id,(*cur).idlen);
-
-                // don't forget to update all node animation channels
-                for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) {
-                    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
-                        if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName,src,n))
-                            continue;
-                    }
-
-                    PrefixString((*ppAnims)->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen);
-                }
-            }
-        }
-    }
-
-    // Now build the output graph
-    AttachToGraph ( master, nodes);
-    dest->mRootNode = master->mRootNode;
-
-    // Check whether we succeeded at building the output graph
-    for (std::vector <NodeAttachmentInfo> ::iterator it = nodes.begin();
-        it != nodes.end(); ++it)
-    {
-        if (!(*it).resolved) {
-            if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) {
-                // search for this attachment point in all other imported scenes, too.
-                for ( unsigned int n = 0; n < src.size();++n ) {
-                    if (n != (*it).src_idx) {
-                        AttachToGraph(src[n].scene,nodes);
-                        if ((*it).resolved)
-                            break;
-                    }
-                }
-            }
-            if (!(*it).resolved) {
-                ASSIMP_LOG_ERROR_F( "SceneCombiner: Failed to resolve attachment ", (*it).node->mName.data,
-                    " ", (*it).attachToNode->mName.data );
-            }
-        }
-    }
-
-    // now delete all input scenes. Make sure duplicate scenes aren't
-    // deleted more than one time
-    for ( unsigned int n = 0; n < src.size();++n )  {
-        if (n != duplicates[n]) // duplicate scene?
-            continue;
-
-        aiScene* deleteMe = src[n].scene;
-
-        // We need to delete the arrays before the destructor is called -
-        // we are reusing the array members
-        delete[] deleteMe->mMeshes;     deleteMe->mMeshes     = NULL;
-        delete[] deleteMe->mCameras;    deleteMe->mCameras    = NULL;
-        delete[] deleteMe->mLights;     deleteMe->mLights     = NULL;
-        delete[] deleteMe->mMaterials;  deleteMe->mMaterials  = NULL;
-        delete[] deleteMe->mAnimations; deleteMe->mAnimations = NULL;
-
-        deleteMe->mRootNode = NULL;
-
-        // Now we can safely delete the scene
-        delete deleteMe;
-    }
-
-    // Check flags
-    if (!dest->mNumMeshes || !dest->mNumMaterials) {
-        dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
-    }
-
-    // We're finished
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build a list of unique bones
-void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash>& asBones,
-    std::vector<aiMesh*>::const_iterator it,
-    std::vector<aiMesh*>::const_iterator end)
-{
-    unsigned int iOffset = 0;
-    for (; it != end;++it)  {
-        for (unsigned int l = 0; l < (*it)->mNumBones;++l)  {
-            aiBone* p = (*it)->mBones[l];
-            uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length);
-
-            std::list<BoneWithHash>::iterator it2  = asBones.begin();
-            std::list<BoneWithHash>::iterator end2 = asBones.end();
-
-            for (;it2 != end2;++it2)    {
-                if ((*it2).first == itml)   {
-                    (*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
-                    break;
-                }
-            }
-            if (end2 == it2)    {
-                // need to begin a new bone entry
-                asBones.push_back(BoneWithHash());
-                BoneWithHash& btz = asBones.back();
-
-                // setup members
-                btz.first = itml;
-                btz.second = &p->mName;
-                btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset));
-            }
-        }
-        iOffset += (*it)->mNumVertices;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Merge a list of bones
-void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it,
-    std::vector<aiMesh*>::const_iterator end)
-{
-    if ( nullptr == out || out->mNumBones == 0 ) {
-        return;
-    }
-
-    // find we need to build an unique list of all bones.
-    // we work with hashes to make the comparisons MUCH faster,
-    // at least if we have many bones.
-    std::list<BoneWithHash> asBones;
-    BuildUniqueBoneList( asBones, it, end );
-
-    // now create the output bones
-    out->mNumBones = 0;
-    out->mBones = new aiBone*[asBones.size()];
-
-    for (std::list<BoneWithHash>::const_iterator boneIt = asBones.begin(),boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt )  {
-        // Allocate a bone and setup it's name
-        aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
-        pc->mName = aiString( *( boneIt->second ));
-
-        std::vector< BoneSrcIndex >::const_iterator wend = boneIt->pSrcBones.end();
-
-        // Loop through all bones to be joined for this bone
-        for (std::vector< BoneSrcIndex >::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit)  {
-            pc->mNumWeights += (*wmit).first->mNumWeights;
-
-            // NOTE: different offset matrices for bones with equal names
-            // are - at the moment - not handled correctly.
-            if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) {
-                ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment");
-                continue;
-            }
-            pc->mOffsetMatrix = wmit->first->mOffsetMatrix;
-        }
-
-        // Allocate the vertex weight array
-        aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
-
-        // And copy the final weights - adjust the vertex IDs by the
-        // face index offset of the corresponding mesh.
-        for (std::vector< BoneSrcIndex >::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) {
-            if (wmit == wend) {
-                break;
-            }
-
-            aiBone* pip = (*wmit).first;
-            for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) {
-                const aiVertexWeight& vfi = pip->mWeights[mp];
-                avw->mWeight = vfi.mWeight;
-                avw->mVertexId = vfi.mVertexId + (*wmit).second;
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Merge a list of meshes
-void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/,
-    std::vector<aiMesh*>::const_iterator begin,
-    std::vector<aiMesh*>::const_iterator end)
-{
-    if ( nullptr == _out ) {
-        return;
-    }
-
-    if (begin == end)   {
-        *_out = NULL; // no meshes ...
-        return;
-    }
-
-    // Allocate the output mesh
-    aiMesh* out = *_out = new aiMesh();
-    out->mMaterialIndex = (*begin)->mMaterialIndex;
-
-    std::string name;
-    // Find out how much output storage we'll need
-    for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it) {
-        const char *meshName( (*it)->mName.C_Str() );
-        name += std::string( meshName );
-        if ( it != end - 1 ) {
-            name += ".";
-        }
-        out->mNumVertices   += (*it)->mNumVertices;
-        out->mNumFaces      += (*it)->mNumFaces;
-        out->mNumBones      += (*it)->mNumBones;
-
-        // combine primitive type flags
-        out->mPrimitiveTypes |= (*it)->mPrimitiveTypes;
-    }
-    out->mName.Set( name.c_str() );
-
-    if (out->mNumVertices) {
-        aiVector3D* pv2;
-
-        // copy vertex positions
-        if ((**begin).HasPositions())   {
-
-            pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
-            for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it)  {
-                if ((*it)->mVertices)   {
-                    ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
-                }
-                else ASSIMP_LOG_WARN("JoinMeshes: Positions expected but input mesh contains no positions");
-                pv2 += (*it)->mNumVertices;
-            }
-        }
-        // copy normals
-        if ((**begin).HasNormals()) {
-
-            pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
-            for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)   {
-                if ((*it)->mNormals)    {
-                    ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D));
-                } else {
-                    ASSIMP_LOG_WARN( "JoinMeshes: Normals expected but input mesh contains no normals" );
-                }
-                pv2 += (*it)->mNumVertices;
-            }
-        }
-        // copy tangents and bi-tangents
-        if ((**begin).HasTangentsAndBitangents())   {
-
-            pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
-            aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
-
-            for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
-                if ((*it)->mTangents)   {
-                    ::memcpy(pv2, (*it)->mTangents,  (*it)->mNumVertices*sizeof(aiVector3D));
-                    ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D));
-                } else {
-                    ASSIMP_LOG_WARN( "JoinMeshes: Tangents expected but input mesh contains no tangents" );
-                }
-                pv2  += (*it)->mNumVertices;
-                pv2b += (*it)->mNumVertices;
-            }
-        }
-        // copy texture coordinates
-        unsigned int n = 0;
-        while ((**begin).HasTextureCoords(n)) {
-            out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n];
-
-            pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
-            for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)   {
-                if ((*it)->mTextureCoords[n])   {
-                    ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D));
-                } else {
-                    ASSIMP_LOG_WARN( "JoinMeshes: UVs expected but input mesh contains no UVs" );
-                }
-                pv2 += (*it)->mNumVertices;
-            }
-            ++n;
-        }
-        // copy vertex colors
-        n = 0;
-        while ((**begin).HasVertexColors(n))    {
-            aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
-            for ( std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it )   {
-                if ((*it)->mColors[n])  {
-                    ::memcpy( pVec2, (*it)->mColors[ n ], (*it)->mNumVertices * sizeof( aiColor4D ) ) ;
-                } else {
-                    ASSIMP_LOG_WARN( "JoinMeshes: VCs expected but input mesh contains no VCs" );
-                }
-                pVec2 += (*it)->mNumVertices;
-            }
-            ++n;
-        }
-    }
-
-    if (out->mNumFaces) // just for safety
-    {
-        // copy faces
-        out->mFaces = new aiFace[out->mNumFaces];
-        aiFace* pf2 = out->mFaces;
-
-        unsigned int ofs = 0;
-        for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)   {
-            for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2)    {
-                aiFace& face = (*it)->mFaces[m];
-                pf2->mNumIndices = face.mNumIndices;
-                pf2->mIndices = face.mIndices;
-
-                if (ofs)    {
-                    // add the offset to the vertex
-                    for (unsigned int q = 0; q < face.mNumIndices; ++q)
-                        face.mIndices[q] += ofs;
-                }
-                face.mIndices = NULL;
-            }
-            ofs += (*it)->mNumVertices;
-        }
-    }
-
-    // bones - as this is quite lengthy, I moved the code to a separate function
-    if (out->mNumBones)
-        MergeBones(out,begin,end);
-
-    // delete all source meshes
-    for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)
-        delete *it;
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::MergeMaterials(aiMaterial** dest,
-        std::vector<aiMaterial*>::const_iterator begin,
-        std::vector<aiMaterial*>::const_iterator end)
-{
-    if ( nullptr == dest ) {
-        return;
-    }
-
-    if (begin == end)   {
-        *dest = NULL; // no materials ...
-        return;
-    }
-
-    // Allocate the output material
-    aiMaterial* out = *dest = new aiMaterial();
-
-    // Get the maximal number of properties
-    unsigned int size = 0;
-    for (std::vector<aiMaterial*>::const_iterator it = begin; it != end; ++it) {
-        size += (*it)->mNumProperties;
-    }
-
-    out->Clear();
-    delete[] out->mProperties;
-
-    out->mNumAllocated = size;
-    out->mNumProperties = 0;
-    out->mProperties = new aiMaterialProperty*[out->mNumAllocated];
-
-    for (std::vector<aiMaterial*>::const_iterator it = begin; it != end; ++it) {
-        for(unsigned int i = 0; i < (*it)->mNumProperties; ++i) {
-            aiMaterialProperty* sprop = (*it)->mProperties[i];
-
-            // Test if we already have a matching property
-            const aiMaterialProperty* prop_exist;
-            if(aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) {
-                // If not, we add it to the new material
-                aiMaterialProperty* prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty();
-
-                prop->mDataLength = sprop->mDataLength;
-                prop->mData = new char[prop->mDataLength];
-                ::memcpy(prop->mData, sprop->mData, prop->mDataLength);
-
-                prop->mIndex    = sprop->mIndex;
-                prop->mSemantic = sprop->mSemantic;
-                prop->mKey      = sprop->mKey;
-                prop->mType     = sprop->mType;
-
-                out->mNumProperties++;
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-template <typename Type>
-inline
-void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) {
-    if (!num) {
-        dest = NULL;
-        return;
-    }
-    dest = new Type*[num];
-    for (ai_uint i = 0; i < num;++i) {
-        SceneCombiner::Copy(&dest[i],src[i]);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-template <typename Type>
-inline
-void GetArrayCopy(Type*& dest, ai_uint num ) {
-    if ( !dest ) {
-        return;
-    }
-    Type* old = dest;
-
-    dest = new Type[num];
-    ::memcpy(dest, old, sizeof(Type) * num);
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    // reuse the old scene or allocate a new?
-    if (*_dest) {
-        (*_dest)->~aiScene();
-        new (*_dest) aiScene();
-    } else {
-        *_dest = new aiScene();
-    }
-
-    ::memcpy(*_dest,src,sizeof(aiScene));
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    if (allocate) {
-        *_dest = new aiScene();
-    }
-    aiScene* dest = *_dest;
-    ai_assert(nullptr != dest);
-
-    // copy metadata
-    if ( nullptr != src->mMetaData ) {
-        dest->mMetaData = new aiMetadata( *src->mMetaData );
-    }
-
-    // copy animations
-    dest->mNumAnimations = src->mNumAnimations;
-    CopyPtrArray(dest->mAnimations,src->mAnimations,
-        dest->mNumAnimations);
-
-    // copy textures
-    dest->mNumTextures = src->mNumTextures;
-    CopyPtrArray(dest->mTextures,src->mTextures,
-        dest->mNumTextures);
-
-    // copy materials
-    dest->mNumMaterials = src->mNumMaterials;
-    CopyPtrArray(dest->mMaterials,src->mMaterials,
-        dest->mNumMaterials);
-
-    // copy lights
-    dest->mNumLights = src->mNumLights;
-    CopyPtrArray(dest->mLights,src->mLights,
-        dest->mNumLights);
-
-    // copy cameras
-    dest->mNumCameras = src->mNumCameras;
-    CopyPtrArray(dest->mCameras,src->mCameras,
-        dest->mNumCameras);
-
-    // copy meshes
-    dest->mNumMeshes = src->mNumMeshes;
-    CopyPtrArray(dest->mMeshes,src->mMeshes,
-        dest->mNumMeshes);
-
-    // now - copy the root node of the scene (deep copy, too)
-    Copy( &dest->mRootNode, src->mRootNode);
-
-    // and keep the flags ...
-    dest->mFlags = src->mFlags;
-
-    // source private data might be NULL if the scene is user-allocated (i.e. for use with the export API)
-    if (dest->mPrivate != NULL) {
-        ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiMesh* dest = *_dest = new aiMesh();
-
-    // get a flat copy
-    ::memcpy(dest,src,sizeof(aiMesh));
-
-    // and reallocate all arrays
-    GetArrayCopy( dest->mVertices,   dest->mNumVertices );
-    GetArrayCopy( dest->mNormals ,   dest->mNumVertices );
-    GetArrayCopy( dest->mTangents,   dest->mNumVertices );
-    GetArrayCopy( dest->mBitangents, dest->mNumVertices );
-
-    unsigned int n = 0;
-    while (dest->HasTextureCoords(n))
-        GetArrayCopy( dest->mTextureCoords[n++],   dest->mNumVertices );
-
-    n = 0;
-    while (dest->HasVertexColors(n))
-        GetArrayCopy( dest->mColors[n++],   dest->mNumVertices );
-
-    // make a deep copy of all bones
-    CopyPtrArray(dest->mBones,dest->mBones,dest->mNumBones);
-
-    // make a deep copy of all faces
-    GetArrayCopy(dest->mFaces,dest->mNumFaces);
-    for (unsigned int i = 0; i < dest->mNumFaces;++i) {
-        aiFace& f = dest->mFaces[i];
-        GetArrayCopy(f.mIndices,f.mNumIndices);
-    }
-
-    // make a deep copy of all blend shapes
-    CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes);
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy(aiAnimMesh** _dest, const aiAnimMesh* src) {
-    if (nullptr == _dest || nullptr == src) {
-        return;
-    }
-
-    aiAnimMesh* dest = *_dest = new aiAnimMesh();
-
-    // get a flat copy
-    ::memcpy(dest, src, sizeof(aiAnimMesh));
-
-    // and reallocate all arrays
-    GetArrayCopy(dest->mVertices, dest->mNumVertices);
-    GetArrayCopy(dest->mNormals, dest->mNumVertices);
-    GetArrayCopy(dest->mTangents, dest->mNumVertices);
-    GetArrayCopy(dest->mBitangents, dest->mNumVertices);
-
-    unsigned int n = 0;
-    while (dest->HasTextureCoords(n))
-        GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
-
-    n = 0;
-    while (dest->HasVertexColors(n))
-        GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiMaterial* dest = (aiMaterial*) ( *_dest = new aiMaterial() );
-
-    dest->Clear();
-    delete[] dest->mProperties;
-
-    dest->mNumAllocated  =  src->mNumAllocated;
-    dest->mNumProperties =  src->mNumProperties;
-    dest->mProperties    =  new aiMaterialProperty* [dest->mNumAllocated];
-
-    for (unsigned int i = 0; i < dest->mNumProperties;++i)
-    {
-        aiMaterialProperty* prop  = dest->mProperties[i] = new aiMaterialProperty();
-        aiMaterialProperty* sprop = src->mProperties[i];
-
-        prop->mDataLength = sprop->mDataLength;
-        prop->mData = new char[prop->mDataLength];
-        ::memcpy(prop->mData,sprop->mData,prop->mDataLength);
-
-        prop->mIndex    = sprop->mIndex;
-        prop->mSemantic = sprop->mSemantic;
-        prop->mKey      = sprop->mKey;
-        prop->mType     = sprop->mType;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy(aiTexture** _dest, const aiTexture* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiTexture* dest = *_dest = new aiTexture();
-
-    // get a flat copy
-    ::memcpy(dest,src,sizeof(aiTexture));
-
-    // and reallocate all arrays. We must do it manually here
-    const char* old = (const char*)dest->pcData;
-    if (old)
-    {
-        unsigned int cpy;
-        if (!dest->mHeight)cpy = dest->mWidth;
-        else cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel);
-
-        if (!cpy)
-        {
-            dest->pcData = NULL;
-            return;
-        }
-        // the cast is legal, the aiTexel c'tor does nothing important
-        dest->pcData = (aiTexel*) new char[cpy];
-        ::memcpy(dest->pcData, old, cpy);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiAnimation* dest = *_dest = new aiAnimation();
-
-    // get a flat copy
-    ::memcpy(dest,src,sizeof(aiAnimation));
-
-    // and reallocate all arrays
-    CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels );
-    CopyPtrArray( dest->mMorphMeshChannels, src->mMorphMeshChannels, dest->mNumMorphMeshChannels );
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy(aiNodeAnim** _dest, const aiNodeAnim* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiNodeAnim* dest = *_dest = new aiNodeAnim();
-
-    // get a flat copy
-    ::memcpy(dest,src,sizeof(aiNodeAnim));
-
-    // and reallocate all arrays
-    GetArrayCopy( dest->mPositionKeys, dest->mNumPositionKeys );
-    GetArrayCopy( dest->mScalingKeys,  dest->mNumScalingKeys );
-    GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys );
-}
-
-void SceneCombiner::Copy(aiMeshMorphAnim** _dest, const aiMeshMorphAnim* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiMeshMorphAnim* dest = *_dest = new aiMeshMorphAnim();
-
-    // get a flat copy
-    ::memcpy(dest,src,sizeof(aiMeshMorphAnim));
-
-    // and reallocate all arrays
-    GetArrayCopy( dest->mKeys, dest->mNumKeys );
-    for (ai_uint i = 0; i < dest->mNumKeys;++i) {
-        dest->mKeys[i].mValues = new unsigned int[dest->mKeys[i].mNumValuesAndWeights];
-        dest->mKeys[i].mWeights = new double[dest->mKeys[i].mNumValuesAndWeights];
-        ::memcpy(dest->mKeys[i].mValues, src->mKeys[i].mValues, dest->mKeys[i].mNumValuesAndWeights * sizeof(unsigned int));
-        ::memcpy(dest->mKeys[i].mWeights, src->mKeys[i].mWeights, dest->mKeys[i].mNumValuesAndWeights * sizeof(double));
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy( aiCamera** _dest,const  aiCamera* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiCamera* dest = *_dest = new aiCamera();
-
-    // get a flat copy, that's already OK
-    ::memcpy(dest,src,sizeof(aiCamera));
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy(aiLight** _dest, const aiLight* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiLight* dest = *_dest = new aiLight();
-
-    // get a flat copy, that's already OK
-    ::memcpy(dest,src,sizeof(aiLight));
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy(aiBone** _dest, const aiBone* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    aiBone* dest = *_dest = new aiBone();
-
-    // get a flat copy
-    ::memcpy(dest,src,sizeof(aiBone));
-
-    // and reallocate all arrays
-    GetArrayCopy( dest->mWeights, dest->mNumWeights );
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy     (aiNode** _dest, const aiNode* src)
-{
-    ai_assert(NULL != _dest && NULL != src);
-
-    aiNode* dest = *_dest = new aiNode();
-
-    // get a flat copy
-    ::memcpy(dest,src,sizeof(aiNode));
-
-    if (src->mMetaData) {
-        Copy(&dest->mMetaData, src->mMetaData);
-    }
-
-    // and reallocate all arrays
-    GetArrayCopy( dest->mMeshes, dest->mNumMeshes );
-    CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren);
-
-	// need to set the mParent fields to the created aiNode.
-	for( unsigned int i = 0; i < dest->mNumChildren; i ++ ) {
-		dest->mChildren[i]->mParent = dest;
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) {
-    if ( nullptr == _dest || nullptr == src ) {
-        return;
-    }
-
-    if ( 0 == src->mNumProperties ) {
-        return;
-    }
-
-    aiMetadata* dest = *_dest = aiMetadata::Alloc( src->mNumProperties );
-    std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys);
-
-    dest->mValues = new aiMetadataEntry[src->mNumProperties];
-    for (unsigned int i = 0; i < src->mNumProperties; ++i) {
-        aiMetadataEntry& in = src->mValues[i];
-        aiMetadataEntry& out = dest->mValues[i];
-        out.mType = in.mType;
-        switch (dest->mValues[i].mType) {
-            case AI_BOOL:
-                out.mData = new bool(*static_cast<bool*>(in.mData));
-                break;
-            case AI_INT32:
-                out.mData = new int32_t(*static_cast<int32_t*>(in.mData));
-                break;
-            case AI_UINT64:
-                out.mData = new uint64_t(*static_cast<uint64_t*>(in.mData));
-                break;
-            case AI_FLOAT:
-                out.mData = new float(*static_cast<float*>(in.mData));
-                break;
-            case AI_DOUBLE:
-                out.mData = new double(*static_cast<double*>(in.mData));
-                break;
-            case AI_AISTRING:
-                out.mData = new aiString(*static_cast<aiString*>(in.mData));
-                break;
-            case AI_AIVECTOR3D:
-                out.mData = new aiVector3D(*static_cast<aiVector3D*>(in.mData));
-                break;
-            default:
-                ai_assert(false);
-                break;
-        }
-    }
-}
-
-} // Namespace Assimp
-

+ 0 - 261
thirdparty/assimp/code/Common/ScenePreprocessor.cpp

@@ -1,261 +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.
-
-----------------------------------------------------------------------
-*/
-
-#include "ScenePreprocessor.h"
-#include <assimp/ai_assert.h>
-#include <assimp/scene.h>
-#include <assimp/DefaultLogger.hpp>
-
-
-using namespace Assimp;
-
-// ---------------------------------------------------------------------------------------------
-void ScenePreprocessor::ProcessScene ()
-{
-    ai_assert(scene != NULL);
-
-    // Process all meshes
-    for (unsigned int i = 0; i < scene->mNumMeshes;++i)
-        ProcessMesh(scene->mMeshes[i]);
-
-    // - nothing to do for nodes for the moment
-    // - nothing to do for textures for the moment
-    // - nothing to do for lights for the moment
-    // - nothing to do for cameras for the moment
-
-    // Process all animations
-    for (unsigned int i = 0; i < scene->mNumAnimations;++i)
-        ProcessAnimation(scene->mAnimations[i]);
-
-    // Generate a default material if none was specified
-    if (!scene->mNumMaterials && scene->mNumMeshes) {
-        scene->mMaterials      = new aiMaterial*[2];
-        aiMaterial* helper;
-
-        aiString name;
-
-        scene->mMaterials[scene->mNumMaterials] = helper = new aiMaterial();
-        aiColor3D clr(0.6f,0.6f,0.6f);
-        helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
-
-        // setup the default name to make this material identifiable
-        name.Set(AI_DEFAULT_MATERIAL_NAME);
-        helper->AddProperty(&name,AI_MATKEY_NAME);
-
-        ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME  "\'");
-
-        for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
-            scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials;
-        }
-
-        scene->mNumMaterials++;
-    }
-}
-
-// ---------------------------------------------------------------------------------------------
-void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
-{
-    // If aiMesh::mNumUVComponents is *not* set assign the default value of 2
-    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i)   {
-        if (!mesh->mTextureCoords[i]) {
-            mesh->mNumUVComponents[i] = 0;
-        } else {
-            if (!mesh->mNumUVComponents[i])
-                mesh->mNumUVComponents[i] = 2;
-
-            aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices;
-
-            // Ensure unused components are zeroed. This will make 1D texture channels work
-            // as if they were 2D channels .. just in case an application doesn't handle
-            // this case
-            if (2 == mesh->mNumUVComponents[i]) {
-                for (; p != end; ++p)
-                    p->z = 0.f;
-            }
-            else if (1 == mesh->mNumUVComponents[i]) {
-                for (; p != end; ++p)
-                    p->z = p->y = 0.f;
-            }
-            else if (3 == mesh->mNumUVComponents[i]) {
-                // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element
-                for (; p != end; ++p) {
-                    if (p->z != 0)
-                        break;
-                }
-                if (p == end) {
-                    ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D.");
-                    mesh->mNumUVComponents[i] = 2;
-                }
-            }
-        }
-    }
-
-    // If the information which primitive types are there in the
-    // mesh is currently not available, compute it.
-    if (!mesh->mPrimitiveTypes) {
-        for (unsigned int a = 0; a < mesh->mNumFaces; ++a)  {
-            aiFace& face = mesh->mFaces[a];
-            switch (face.mNumIndices)
-            {
-            case 3u:
-                mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
-                break;
-
-            case 2u:
-                mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
-                break;
-
-            case 1u:
-                mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
-                break;
-
-            default:
-                mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
-                break;
-            }
-        }
-    }
-
-    // If tangents and normals are given but no bitangents compute them
-    if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents)    {
-
-        mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
-        for (unsigned int i = 0; i < mesh->mNumVertices;++i)    {
-            mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i];
-        }
-    }
-}
-
-// ---------------------------------------------------------------------------------------------
-void ScenePreprocessor::ProcessAnimation (aiAnimation* anim)
-{
-    double first = 10e10, last = -10e10;
-    for (unsigned int i = 0; i < anim->mNumChannels;++i)    {
-        aiNodeAnim* channel = anim->mChannels[i];
-
-        /*  If the exact duration of the animation is not given
-         *  compute it now.
-         */
-        if (anim->mDuration == -1.) {
-
-            // Position keys
-            for (unsigned int j = 0; j < channel->mNumPositionKeys;++j) {
-                aiVectorKey& key = channel->mPositionKeys[j];
-                first = std::min (first, key.mTime);
-                last  = std::max (last,  key.mTime);
-            }
-
-            // Scaling keys
-            for (unsigned int j = 0; j < channel->mNumScalingKeys;++j )  {
-                aiVectorKey& key = channel->mScalingKeys[j];
-                first = std::min (first, key.mTime);
-                last  = std::max (last,  key.mTime);
-            }
-
-            // Rotation keys
-            for (unsigned int j = 0; j < channel->mNumRotationKeys;++j ) {
-                aiQuatKey& key = channel->mRotationKeys[ j ];
-                first = std::min (first, key.mTime);
-                last  = std::max (last,  key.mTime);
-            }
-        }
-
-        /*  Check whether the animation channel has no rotation
-         *  or position tracks. In this case we generate a dummy
-         *  track from the information we have in the transformation
-         *  matrix of the corresponding node.
-         */
-        if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys)  {
-            // Find the node that belongs to this animation
-            aiNode* node = scene->mRootNode->FindNode(channel->mNodeName);
-            if (node) // ValidateDS will complain later if 'node' is NULL
-            {
-                // Decompose the transformation matrix of the node
-                aiVector3D scaling, position;
-                aiQuaternion rotation;
-
-                node->mTransformation.Decompose(scaling, rotation,position);
-
-                // No rotation keys? Generate a dummy track
-                if (!channel->mNumRotationKeys) {
-                    channel->mNumRotationKeys = 1;
-                    channel->mRotationKeys = new aiQuatKey[1];
-                    aiQuatKey& q = channel->mRotationKeys[0];
-
-                    q.mTime  = 0.;
-                    q.mValue = rotation;
-
-                    ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy rotation track has been generated");
-                }
-
-                // No scaling keys? Generate a dummy track
-                if (!channel->mNumScalingKeys)  {
-                    channel->mNumScalingKeys = 1;
-                    channel->mScalingKeys = new aiVectorKey[1];
-                    aiVectorKey& q = channel->mScalingKeys[0];
-
-                    q.mTime  = 0.;
-                    q.mValue = scaling;
-
-                    ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy scaling track has been generated");
-                }
-
-                // No position keys? Generate a dummy track
-                if (!channel->mNumPositionKeys) {
-                    channel->mNumPositionKeys = 1;
-                    channel->mPositionKeys = new aiVectorKey[1];
-                    aiVectorKey& q = channel->mPositionKeys[0];
-
-                    q.mTime  = 0.;
-                    q.mValue = position;
-
-                    ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy position track has been generated");
-                }
-            }
-        }
-    }
-
-    if (anim->mDuration == -1.)     {
-        ASSIMP_LOG_DEBUG("ScenePreprocessor: Setting animation duration");
-        anim->mDuration = last - std::min( first, 0. );
-    }
-}

+ 0 - 125
thirdparty/assimp/code/Common/ScenePreprocessor.h

@@ -1,125 +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 a post processing step to search all meshes for
-  degenerated faces */
-#ifndef AI_SCENE_PREPROCESSOR_H_INC
-#define AI_SCENE_PREPROCESSOR_H_INC
-
-#include <assimp/defs.h>
-#include <stddef.h>
-
-struct aiScene;
-struct aiAnimation;
-struct aiMesh;
-
-class ScenePreprocessorTest;
-namespace Assimp    {
-
-// ----------------------------------------------------------------------------------
-/** ScenePreprocessor: Preprocess a scene before any post-processing
- *  steps are executed.
- *
- *  The step computes data that needn't necessarily be provided by the
- *  importer, such as aiMesh::mPrimitiveTypes.
-*/
-// ----------------------------------------------------------------------------------
-class ASSIMP_API ScenePreprocessor
-{
-    // Make ourselves a friend of the corresponding test unit.
-    friend class ::ScenePreprocessorTest;
-public:
-
-    // ----------------------------------------------------------------
-    /** Default c'tpr. Use SetScene() to assign a scene to the object.
-     */
-    ScenePreprocessor()
-        :   scene   (NULL)
-    {}
-
-    /** Constructs the object and assigns a specific scene to it
-     */
-    ScenePreprocessor(aiScene* _scene)
-        :   scene   (_scene)
-    {}
-
-    // ----------------------------------------------------------------
-    /** Assign a (new) scene to the object.
-     *
-     *  One 'SceneProcessor' can be used for multiple scenes.
-     *  Call ProcessScene to have the scene preprocessed.
-     *  @param sc Scene to be processed.
-     */
-    void SetScene (aiScene* sc) {
-        scene = sc;
-    }
-
-    // ----------------------------------------------------------------
-    /** Preprocess the current scene
-     */
-    void ProcessScene ();
-
-protected:
-
-    // ----------------------------------------------------------------
-    /** Preprocess an animation in the scene
-     *  @param anim Anim to be preprocessed.
-     */
-    void ProcessAnimation (aiAnimation* anim);
-
-
-    // ----------------------------------------------------------------
-    /** Preprocess a mesh in the scene
-     *  @param mesh Mesh to be preprocessed.
-     */
-    void ProcessMesh (aiMesh* mesh);
-
-protected:
-
-    //! Scene we're currently working on
-    aiScene* scene;
-};
-
-
-} // ! end namespace Assimp
-
-#endif // include guard

+ 0 - 105
thirdparty/assimp/code/Common/ScenePrivate.h

@@ -1,105 +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 Stuff to deal with aiScene::mPrivate
- */
-#pragma once
-#ifndef AI_SCENEPRIVATE_H_INCLUDED
-#define AI_SCENEPRIVATE_H_INCLUDED
-
-#include <assimp/ai_assert.h>
-#include <assimp/scene.h>
-
-namespace Assimp {
-
-// Forward declarations
-class Importer;
-
-struct ScenePrivateData {
-    //  The struct constructor.
-    ScenePrivateData() AI_NO_EXCEPT;
-
-    // Importer that originally loaded the scene though the C-API
-    // If set, this object is owned by this private data instance.
-    Assimp::Importer* mOrigImporter;
-
-    // List of post-processing steps already applied to the scene.
-    unsigned int mPPStepsApplied;
-
-    // true if the scene is a copy made with aiCopyScene()
-    // or the corresponding C++ API. This means that user code
-    // may have made modifications to it, so mPPStepsApplied
-    // and mOrigImporter are no longer safe to rely on and only
-    // serve informative purposes.
-    bool mIsCopy;
-};
-
-inline
-ScenePrivateData::ScenePrivateData() AI_NO_EXCEPT
-: mOrigImporter( nullptr )
-, mPPStepsApplied( 0 )
-, mIsCopy( false ) {
-    // empty
-}
-
-// Access private data stored in the scene
-inline
-ScenePrivateData* ScenePriv(aiScene* in) {
-    ai_assert( nullptr != in );
-    if ( nullptr == in ) {
-        return nullptr;
-    }
-    return static_cast<ScenePrivateData*>(in->mPrivate);
-}
-
-inline
-const ScenePrivateData* ScenePriv(const aiScene* in) {
-    ai_assert( nullptr != in );
-    if ( nullptr == in ) {
-        return nullptr;
-    }
-    return static_cast<const ScenePrivateData*>(in->mPrivate);
-}
-
-} // Namespace Assimp
-
-#endif // AI_SCENEPRIVATE_H_INCLUDED

+ 0 - 270
thirdparty/assimp/code/Common/SkeletonMeshBuilder.cpp

@@ -1,270 +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  SkeletonMeshBuilder.cpp
- *  @brief Implementation of a little class to construct a dummy mesh for a skeleton
- */
-
-#include <assimp/scene.h>
-#include <assimp/SkeletonMeshBuilder.h>
-
-using namespace Assimp;
-
-// ------------------------------------------------------------------------------------------------
-// The constructor processes the given scene and adds a mesh there.
-SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly)
-{
-    // nothing to do if there's mesh data already present at the scene
-    if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
-        return;
-
-    if (!root)
-        root = pScene->mRootNode;
-
-    mKnobsOnly = bKnobsOnly;
-
-    // build some faces around each node
-    CreateGeometry( root );
-
-    // create a mesh to hold all the generated faces
-    pScene->mNumMeshes = 1;
-    pScene->mMeshes = new aiMesh*[1];
-    pScene->mMeshes[0] = CreateMesh();
-    // and install it at the root node
-    root->mNumMeshes = 1;
-    root->mMeshes = new unsigned int[1];
-    root->mMeshes[0] = 0;
-
-    // create a dummy material for the mesh
-    if(pScene->mNumMaterials==0){
-		pScene->mNumMaterials = 1;
-		pScene->mMaterials = new aiMaterial*[1];
-		pScene->mMaterials[0] = CreateMaterial();
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Recursively builds a simple mesh representation for the given node
-void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
-{
-    // add a joint entry for the node.
-    const unsigned int vertexStartIndex = static_cast<unsigned int>(mVertices.size());
-
-    // now build the geometry.
-    if( pNode->mNumChildren > 0 && !mKnobsOnly)
-    {
-        // If the node has children, we build little pointers to each of them
-        for( unsigned int a = 0; a < pNode->mNumChildren; a++)
-        {
-            // find a suitable coordinate system
-            const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation;
-            aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4);
-            ai_real distanceToChild = childpos.Length();
-            if( distanceToChild < 0.0001)
-                continue;
-            aiVector3D up = aiVector3D( childpos).Normalize();
-
-            aiVector3D orth( 1.0, 0.0, 0.0);
-            if( std::fabs( orth * up) > 0.99)
-                orth.Set( 0.0, 1.0, 0.0);
-
-            aiVector3D front = (up ^ orth).Normalize();
-            aiVector3D side = (front ^ up).Normalize();
-
-            unsigned int localVertexStart = static_cast<unsigned int>(mVertices.size());
-            mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
-            mVertices.push_back( childpos);
-            mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
-            mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
-            mVertices.push_back( childpos);
-            mVertices.push_back( front * distanceToChild * (ai_real)0.1);
-            mVertices.push_back( front * distanceToChild * (ai_real)0.1);
-            mVertices.push_back( childpos);
-            mVertices.push_back( side * distanceToChild * (ai_real)0.1);
-            mVertices.push_back( side * distanceToChild * (ai_real)0.1);
-            mVertices.push_back( childpos);
-            mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
-
-            mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2));
-            mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5));
-            mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
-            mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
-        }
-    }
-    else
-    {
-        // if the node has no children, it's an end node. Put a little knob there instead
-        aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
-        ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 );
-
-        mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
-        mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
-        mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
-        mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
-
-        mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
-        mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
-        mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
-        mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
-        mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
-        mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
-        mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
-
-        mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2));
-        mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5));
-        mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8));
-        mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11));
-        mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14));
-        mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17));
-        mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20));
-        mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23));
-    }
-
-    unsigned int numVertices = static_cast<unsigned int>(mVertices.size() - vertexStartIndex);
-    if( numVertices > 0)
-    {
-        // create a bone affecting all the newly created vertices
-        aiBone* bone = new aiBone;
-        mBones.push_back( bone);
-        bone->mName = pNode->mName;
-
-        // calculate the bone offset matrix by concatenating the inverse transformations of all parents
-        bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse();
-        for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent)
-            bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix;
-
-        // add all the vertices to the bone's influences
-        bone->mNumWeights = numVertices;
-        bone->mWeights = new aiVertexWeight[numVertices];
-        for( unsigned int a = 0; a < numVertices; a++)
-            bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0);
-
-        // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
-        // them to the array, but I'm tired now and I'm annoyed.
-        aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse();
-        for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++)
-            mVertices[a] = boneToMeshTransform * mVertices[a];
-    }
-
-    // and finally recurse into the children list
-    for( unsigned int a = 0; a < pNode->mNumChildren; a++)
-        CreateGeometry( pNode->mChildren[a]);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Creates the mesh from the internally accumulated stuff and returns it.
-aiMesh* SkeletonMeshBuilder::CreateMesh()
-{
-    aiMesh* mesh = new aiMesh();
-
-    // add points
-    mesh->mNumVertices = static_cast<unsigned int>(mVertices.size());
-    mesh->mVertices = new aiVector3D[mesh->mNumVertices];
-    std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
-
-    mesh->mNormals = new aiVector3D[mesh->mNumVertices];
-
-    // add faces
-    mesh->mNumFaces = static_cast<unsigned int>(mFaces.size());
-    mesh->mFaces = new aiFace[mesh->mNumFaces];
-    for( unsigned int a = 0; a < mesh->mNumFaces; a++)
-    {
-        const Face& inface = mFaces[a];
-        aiFace& outface = mesh->mFaces[a];
-        outface.mNumIndices = 3;
-        outface.mIndices = new unsigned int[3];
-        outface.mIndices[0] = inface.mIndices[0];
-        outface.mIndices[1] = inface.mIndices[1];
-        outface.mIndices[2] = inface.mIndices[2];
-
-        // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize
-        // the skeleton, so it's good if there's a visual difference to the rest of the geometry
-        aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^
-            (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]]));
-
-        if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/
-            nor = aiVector3D(1.0,0.0,0.0);
-
-        for (unsigned int n = 0; n < 3; ++n)
-            mesh->mNormals[inface.mIndices[n]] = nor;
-    }
-
-    // add the bones
-    mesh->mNumBones = static_cast<unsigned int>(mBones.size());
-    mesh->mBones = new aiBone*[mesh->mNumBones];
-    std::copy( mBones.begin(), mBones.end(), mesh->mBones);
-
-    // default
-    mesh->mMaterialIndex = 0;
-
-    return mesh;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Creates a dummy material and returns it.
-aiMaterial* SkeletonMeshBuilder::CreateMaterial()
-{
-    aiMaterial* matHelper = new aiMaterial;
-
-    // Name
-    aiString matName( std::string( "SkeletonMaterial"));
-    matHelper->AddProperty( &matName, AI_MATKEY_NAME);
-
-    // Prevent backface culling
-    const int no_cull = 1;
-    matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED);
-
-    return matHelper;
-}

+ 0 - 342
thirdparty/assimp/code/Common/SpatialSort.cpp

@@ -1,342 +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 Implementation of the helper class to quickly find vertices close to a given position */
-
-#include <assimp/SpatialSort.h>
-#include <assimp/ai_assert.h>
-
-using namespace Assimp;
-
-// CHAR_BIT seems to be defined under MVSC, but not under GCC. Pray that the correct value is 8.
-#ifndef CHAR_BIT
-#   define CHAR_BIT 8
-#endif
-
-// ------------------------------------------------------------------------------------------------
-// Constructs a spatially sorted representation from the given position array.
-SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions,
-    unsigned int pElementOffset)
-
-    // define the reference plane. We choose some arbitrary vector away from all basic axises
-    // in the hope that no model spreads all its vertices along this plane.
-    : mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
-{
-    mPlaneNormal.Normalize();
-    Fill(pPositions,pNumPositions,pElementOffset);
-}
-
-// ------------------------------------------------------------------------------------------------
-SpatialSort :: SpatialSort()
-: mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
-{
-    mPlaneNormal.Normalize();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor
-SpatialSort::~SpatialSort()
-{
-    // nothing to do here, everything destructs automatically
-}
-
-// ------------------------------------------------------------------------------------------------
-void SpatialSort::Fill( const aiVector3D* pPositions, unsigned int pNumPositions,
-    unsigned int pElementOffset,
-    bool pFinalize /*= true */)
-{
-    mPositions.clear();
-    Append(pPositions,pNumPositions,pElementOffset,pFinalize);
-}
-
-// ------------------------------------------------------------------------------------------------
-void SpatialSort :: Finalize()
-{
-    std::sort( mPositions.begin(), mPositions.end());
-}
-
-// ------------------------------------------------------------------------------------------------
-void SpatialSort::Append( const aiVector3D* pPositions, unsigned int pNumPositions,
-    unsigned int pElementOffset,
-    bool pFinalize /*= true */)
-{
-    // store references to all given positions along with their distance to the reference plane
-    const size_t initial = mPositions.size();
-    mPositions.reserve(initial + (pFinalize?pNumPositions:pNumPositions*2));
-    for( unsigned int a = 0; a < pNumPositions; a++)
-    {
-        const char* tempPointer = reinterpret_cast<const char*> (pPositions);
-        const aiVector3D* vec   = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
-
-        // store position by index and distance
-        ai_real distance = *vec * mPlaneNormal;
-        mPositions.push_back( Entry( static_cast<unsigned int>(a+initial), *vec, distance));
-    }
-
-    if (pFinalize) {
-        // now sort the array ascending by distance.
-        Finalize();
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns an iterator for all positions close to the given position.
-void SpatialSort::FindPositions( const aiVector3D& pPosition,
-    ai_real pRadius, std::vector<unsigned int>& poResults) const
-{
-    const ai_real dist = pPosition * mPlaneNormal;
-    const ai_real minDist = dist - pRadius, maxDist = dist + pRadius;
-
-    // clear the array
-    poResults.clear();
-
-    // quick check for positions outside the range
-    if( mPositions.size() == 0)
-        return;
-    if( maxDist < mPositions.front().mDistance)
-        return;
-    if( minDist > mPositions.back().mDistance)
-        return;
-
-    // do a binary search for the minimal distance to start the iteration there
-    unsigned int index = (unsigned int)mPositions.size() / 2;
-    unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
-    while( binaryStepSize > 1)
-    {
-        if( mPositions[index].mDistance < minDist)
-            index += binaryStepSize;
-        else
-            index -= binaryStepSize;
-
-        binaryStepSize /= 2;
-    }
-
-    // depending on the direction of the last step we need to single step a bit back or forth
-    // to find the actual beginning element of the range
-    while( index > 0 && mPositions[index].mDistance > minDist)
-        index--;
-    while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
-        index++;
-
-    // Mow start iterating from there until the first position lays outside of the distance range.
-    // Add all positions inside the distance range within the given radius to the result aray
-    std::vector<Entry>::const_iterator it = mPositions.begin() + index;
-    const ai_real pSquared = pRadius*pRadius;
-    while( it->mDistance < maxDist)
-    {
-        if( (it->mPosition - pPosition).SquareLength() < pSquared)
-            poResults.push_back( it->mIndex);
-        ++it;
-        if( it == mPositions.end())
-            break;
-    }
-
-    // that's it
-}
-
-namespace {
-
-    // Binary, signed-integer representation of a single-precision floating-point value.
-    // IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are
-    //  ordered the same way when their bits are reinterpreted as sign-magnitude integers."
-    // This allows us to convert all floating-point numbers to signed integers of arbitrary size
-    //  and then use them to work with ULPs (Units in the Last Place, for high-precision
-    //  computations) or to compare them (integer comparisons are faster than floating-point
-    //  comparisons on many platforms).
-    typedef ai_int BinFloat;
-
-    // --------------------------------------------------------------------------------------------
-    // Converts the bit pattern of a floating-point number to its signed integer representation.
-    BinFloat ToBinary( const ai_real & pValue) {
-
-        // If this assertion fails, signed int is not big enough to store a float on your platform.
-        //  Please correct the declaration of BinFloat a few lines above - but do it in a portable,
-        //  #ifdef'd manner!
-        static_assert( sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)");
-
-        #if defined( _MSC_VER)
-            // If this assertion fails, Visual C++ has finally moved to ILP64. This means that this
-            //  code has just become legacy code! Find out the current value of _MSC_VER and modify
-            //  the #if above so it evaluates false on the current and all upcoming VC versions (or
-            //  on the current platform, if LP64 or LLP64 are still used on other platforms).
-            static_assert( sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)");
-
-            // This works best on Visual C++, but other compilers have their problems with it.
-            const BinFloat binValue = reinterpret_cast<BinFloat const &>(pValue);
-        #else
-            // On many compilers, reinterpreting a float address as an integer causes aliasing
-            // problems. This is an ugly but more or less safe way of doing it.
-            union {
-                ai_real     asFloat;
-                BinFloat    asBin;
-            } conversion;
-            conversion.asBin    = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float)
-            conversion.asFloat  = pValue;
-            const BinFloat binValue = conversion.asBin;
-        #endif
-
-        // floating-point numbers are of sign-magnitude format, so find out what signed number
-        //  representation we must convert negative values to.
-        // See http://en.wikipedia.org/wiki/Signed_number_representations.
-
-        // Two's complement?
-        if( (-42 == (~42 + 1)) && (binValue & 0x80000000))
-            return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue;
-        // One's complement?
-        else if ( (-42 == ~42) && (binValue & 0x80000000))
-            return BinFloat(-0) - binValue;
-        // Sign-magnitude?
-        else if( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary
-            return binValue;
-        else
-            return binValue;
-    }
-
-} // namespace
-
-// ------------------------------------------------------------------------------------------------
-// Fills an array with indices of all positions identical to the given position. In opposite to
-// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units.
-void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition,
-    std::vector<unsigned int>& poResults) const
-{
-    // Epsilons have a huge disadvantage: they are of constant precision, while floating-point
-    //  values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but
-    //  if you apply it to 0.001, it is enormous.
-
-    // The best way to overcome this is the unit in the last place (ULP). A precision of 2 ULPs
-    //  tells us that a float does not differ more than 2 bits from the "real" value. ULPs are of
-    //  logarithmic precision - around 1, they are 1*(2^24) and around 10000, they are 0.00125.
-
-    // For standard C math, we can assume a precision of 0.5 ULPs according to IEEE 754. The
-    //  incoming vertex positions might have already been transformed, probably using rather
-    //  inaccurate SSE instructions, so we assume a tolerance of 4 ULPs to safely identify
-    //  identical vertex positions.
-    static const int toleranceInULPs = 4;
-    // An interesting point is that the inaccuracy grows linear with the number of operations:
-    //  multiplying to numbers, each inaccurate to four ULPs, results in an inaccuracy of four ULPs
-    //  plus 0.5 ULPs for the multiplication.
-    // To compute the distance to the plane, a dot product is needed - that is a multiplication and
-    //  an addition on each number.
-    static const int distanceToleranceInULPs = toleranceInULPs + 1;
-    // The squared distance between two 3D vectors is computed the same way, but with an additional
-    //  subtraction.
-    static const int distance3DToleranceInULPs = distanceToleranceInULPs + 1;
-
-    // Convert the plane distance to its signed integer representation so the ULPs tolerance can be
-    //  applied. For some reason, VC won't optimize two calls of the bit pattern conversion.
-    const BinFloat minDistBinary = ToBinary( pPosition * mPlaneNormal) - distanceToleranceInULPs;
-    const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs;
-
-    // clear the array in this strange fashion because a simple clear() would also deallocate
-    // the array which we want to avoid
-    poResults.resize( 0 );
-
-    // do a binary search for the minimal distance to start the iteration there
-    unsigned int index = (unsigned int)mPositions.size() / 2;
-    unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
-    while( binaryStepSize > 1)
-    {
-        // Ugly, but conditional jumps are faster with integers than with floats
-        if( minDistBinary > ToBinary(mPositions[index].mDistance))
-            index += binaryStepSize;
-        else
-            index -= binaryStepSize;
-
-        binaryStepSize /= 2;
-    }
-
-    // depending on the direction of the last step we need to single step a bit back or forth
-    // to find the actual beginning element of the range
-    while( index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance) )
-        index--;
-    while( index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance))
-        index++;
-
-    // Now start iterating from there until the first position lays outside of the distance range.
-    // Add all positions inside the distance range within the tolerance to the result array
-    std::vector<Entry>::const_iterator it = mPositions.begin() + index;
-    while( ToBinary(it->mDistance) < maxDistBinary)
-    {
-        if( distance3DToleranceInULPs >= ToBinary((it->mPosition - pPosition).SquareLength()))
-            poResults.push_back(it->mIndex);
-        ++it;
-        if( it == mPositions.end())
-            break;
-    }
-
-    // that's it
-}
-
-// ------------------------------------------------------------------------------------------------
-unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int>& fill, ai_real pRadius) const
-{
-    fill.resize(mPositions.size(),UINT_MAX);
-    ai_real dist, maxDist;
-
-    unsigned int t=0;
-    const ai_real pSquared = pRadius*pRadius;
-    for (size_t i = 0; i < mPositions.size();) {
-        dist = mPositions[i].mPosition * mPlaneNormal;
-        maxDist = dist + pRadius;
-
-        fill[mPositions[i].mIndex] = t;
-        const aiVector3D& oldpos = mPositions[i].mPosition;
-        for (++i; i < fill.size() && mPositions[i].mDistance < maxDist
-            && (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i)
-        {
-            fill[mPositions[i].mIndex] = t;
-        }
-        ++t;
-    }
-
-#ifdef ASSIMP_BUILD_DEBUG
-
-    // debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1
-    for (size_t i = 0; i < fill.size(); ++i) {
-        ai_assert(fill[i]<mPositions.size());
-    }
-
-#endif
-    return t;
-}

+ 0 - 407
thirdparty/assimp/code/Common/SplitByBoneCountProcess.cpp

@@ -1,407 +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 SplitByBoneCountProcess.cpp
-/// Implementation of the SplitByBoneCount postprocessing step
-
-// internal headers of the post-processing framework
-#include "SplitByBoneCountProcess.h"
-#include <assimp/postprocess.h>
-#include <assimp/DefaultLogger.hpp>
-
-#include <limits>
-#include <assimp/TinyFormatter.h>
-
-using namespace Assimp;
-using namespace Assimp::Formatter;
-
-// ------------------------------------------------------------------------------------------------
-// Constructor
-SplitByBoneCountProcess::SplitByBoneCountProcess()
-{
-    // set default, might be overridden by importer config
-    mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor
-SplitByBoneCountProcess::~SplitByBoneCountProcess()
-{
-    // nothing to do here
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns whether the processing step is present in the given flag.
-bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
-{
-    return !!(pFlags & aiProcess_SplitByBoneCount);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Updates internal properties
-void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
-{
-    mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Executes the post processing step on the given imported data.
-void SplitByBoneCountProcess::Execute( aiScene* pScene)
-{
-    ASSIMP_LOG_DEBUG("SplitByBoneCountProcess begin");
-
-    // early out
-    bool isNecessary = false;
-    for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
-        if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
-            isNecessary = true;
-
-    if( !isNecessary )
-    {
-        ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess early-out: no meshes with more than " << mMaxBoneCount << " bones." );
-        return;
-    }
-
-    // we need to do something. Let's go.
-    mSubMeshIndices.clear();
-    mSubMeshIndices.resize( pScene->mNumMeshes);
-
-    // build a new array of meshes for the scene
-    std::vector<aiMesh*> meshes;
-
-    for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
-    {
-        aiMesh* srcMesh = pScene->mMeshes[a];
-
-        std::vector<aiMesh*> newMeshes;
-        SplitMesh( pScene->mMeshes[a], newMeshes);
-
-        // mesh was split
-        if( !newMeshes.empty() )
-        {
-            // store new meshes and indices of the new meshes
-            for( unsigned int b = 0; b < newMeshes.size(); ++b)
-            {
-                mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
-                meshes.push_back( newMeshes[b]);
-            }
-
-            // and destroy the source mesh. It should be completely contained inside the new submeshes
-            delete srcMesh;
-        }
-        else
-        {
-            // Mesh is kept unchanged - store it's new place in the mesh array
-            mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
-            meshes.push_back( srcMesh);
-        }
-    }
-
-    // rebuild the scene's mesh array
-    pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
-    delete [] pScene->mMeshes;
-    pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
-    std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
-
-    // recurse through all nodes and translate the node's mesh indices to fit the new mesh array
-    UpdateNode( pScene->mRootNode);
-
-    ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess end: split " << mSubMeshIndices.size() << " meshes into " << meshes.size() << " submeshes." );
-}
-
-// ------------------------------------------------------------------------------------------------
-// Splits the given mesh by bone count.
-void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
-{
-    // skip if not necessary
-    if( pMesh->mNumBones <= mMaxBoneCount )
-        return;
-
-    // necessary optimisation: build a list of all affecting bones for each vertex
-    // TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
-    typedef std::pair<unsigned int, float> BoneWeight;
-    std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
-    for( unsigned int a = 0; a < pMesh->mNumBones; ++a)
-    {
-        const aiBone* bone = pMesh->mBones[a];
-        for( unsigned int b = 0; b < bone->mNumWeights; ++b)
-            vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight));
-    }
-
-    unsigned int numFacesHandled = 0;
-    std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
-    while( numFacesHandled < pMesh->mNumFaces )
-    {
-        // which bones are used in the current submesh
-        unsigned int numBones = 0;
-        std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
-        // indices of the faces which are going to go into this submesh
-        std::vector<unsigned int> subMeshFaces;
-        subMeshFaces.reserve( pMesh->mNumFaces);
-        // accumulated vertex count of all the faces in this submesh
-        unsigned int numSubMeshVertices = 0;
-        // a small local array of new bones for the current face. State of all used bones for that face
-        // can only be updated AFTER the face is completely analysed. Thanks to imre for the fix.
-        std::vector<unsigned int> newBonesAtCurrentFace;
-
-        // add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
-        for( unsigned int a = 0; a < pMesh->mNumFaces; ++a)
-        {
-            // skip if the face is already stored in a submesh
-            if( isFaceHandled[a] )
-                continue;
-
-            const aiFace& face = pMesh->mFaces[a];
-            // check every vertex if its bones would still fit into the current submesh
-            for( unsigned int b = 0; b < face.mNumIndices; ++b )
-            {
-                const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
-                for( unsigned int c = 0; c < vb.size(); ++c)
-                {
-                    unsigned int boneIndex = vb[c].first;
-                    // if the bone is already used in this submesh, it's ok
-                    if( isBoneUsed[boneIndex] )
-                        continue;
-
-                    // if it's not used, yet, we would need to add it. Store its bone index
-                    if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() )
-                        newBonesAtCurrentFace.push_back( boneIndex);
-                }
-            }
-
-            // leave out the face if the new bones required for this face don't fit the bone count limit anymore
-            if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount )
-                continue;
-
-            // mark all new bones as necessary
-            while( !newBonesAtCurrentFace.empty() )
-            {
-                unsigned int newIndex = newBonesAtCurrentFace.back();
-                newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear()
-                if( isBoneUsed[newIndex] )
-                    continue;
-
-                isBoneUsed[newIndex] = true;
-                numBones++;
-            }
-
-            // store the face index and the vertex count
-            subMeshFaces.push_back( a);
-            numSubMeshVertices += face.mNumIndices;
-
-            // remember that this face is handled
-            isFaceHandled[a] = true;
-            numFacesHandled++;
-        }
-
-        // create a new mesh to hold this subset of the source mesh
-        aiMesh* newMesh = new aiMesh;
-        if( pMesh->mName.length > 0 )
-            newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size());
-        newMesh->mMaterialIndex = pMesh->mMaterialIndex;
-        newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
-        poNewMeshes.push_back( newMesh);
-
-        // create all the arrays for this mesh if the old mesh contained them
-        newMesh->mNumVertices = numSubMeshVertices;
-        newMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size());
-        newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
-        if( pMesh->HasNormals() )
-            newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
-        if( pMesh->HasTangentsAndBitangents() )
-        {
-            newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
-            newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
-        }
-        for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
-        {
-            if( pMesh->HasTextureCoords( a) )
-                newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
-            newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
-        }
-        for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
-        {
-            if( pMesh->HasVertexColors( a) )
-                newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
-        }
-
-        // and copy over the data, generating faces with linear indices along the way
-        newMesh->mFaces = new aiFace[subMeshFaces.size()];
-        unsigned int nvi = 0; // next vertex index
-        std::vector<unsigned int> previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh
-        for( unsigned int a = 0; a < subMeshFaces.size(); ++a )
-        {
-            const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
-            aiFace& dstFace = newMesh->mFaces[a];
-            dstFace.mNumIndices = srcFace.mNumIndices;
-            dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
-
-            // accumulate linearly all the vertices of the source face
-            for( unsigned int b = 0; b < dstFace.mNumIndices; ++b )
-            {
-                unsigned int srcIndex = srcFace.mIndices[b];
-                dstFace.mIndices[b] = nvi;
-                previousVertexIndices[nvi] = srcIndex;
-
-                newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
-                if( pMesh->HasNormals() )
-                    newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
-                if( pMesh->HasTangentsAndBitangents() )
-                {
-                    newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
-                    newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
-                }
-                for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
-                {
-                    if( pMesh->HasTextureCoords( c) )
-                        newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
-                }
-                for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
-                {
-                    if( pMesh->HasVertexColors( c) )
-                        newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
-                }
-
-                nvi++;
-            }
-        }
-
-        ai_assert( nvi == numSubMeshVertices );
-
-        // Create the bones for the new submesh: first create the bone array
-        newMesh->mNumBones = 0;
-        newMesh->mBones = new aiBone*[numBones];
-
-        std::vector<unsigned int> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<unsigned int>::max());
-        for( unsigned int a = 0; a < pMesh->mNumBones; ++a )
-        {
-            if( !isBoneUsed[a] )
-                continue;
-
-            // create the new bone
-            const aiBone* srcBone = pMesh->mBones[a];
-            aiBone* dstBone = new aiBone;
-            mappedBoneIndex[a] = newMesh->mNumBones;
-            newMesh->mBones[newMesh->mNumBones++] = dstBone;
-            dstBone->mName = srcBone->mName;
-            dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
-            dstBone->mNumWeights = 0;
-        }
-
-        ai_assert( newMesh->mNumBones == numBones );
-
-        // iterate over all new vertices and count which bones affected its old vertex in the source mesh
-        for( unsigned int a = 0; a < numSubMeshVertices; ++a )
-        {
-            unsigned int oldIndex = previousVertexIndices[a];
-            const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
-
-            for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b )
-            {
-                unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
-                if( newBoneIndex != std::numeric_limits<unsigned int>::max() )
-                    newMesh->mBones[newBoneIndex]->mNumWeights++;
-            }
-        }
-
-        // allocate all bone weight arrays accordingly
-        for( unsigned int a = 0; a < newMesh->mNumBones; ++a )
-        {
-            aiBone* bone = newMesh->mBones[a];
-            ai_assert( bone->mNumWeights > 0 );
-            bone->mWeights = new aiVertexWeight[bone->mNumWeights];
-            bone->mNumWeights = 0; // for counting up in the next step
-        }
-
-        // now copy all the bone vertex weights for all the vertices which made it into the new submesh
-        for( unsigned int a = 0; a < numSubMeshVertices; ++a)
-        {
-            // find the source vertex for it in the source mesh
-            unsigned int previousIndex = previousVertexIndices[a];
-            // these bones were affecting it
-            const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
-            // all of the bones affecting it should be present in the new submesh, or else
-            // the face it comprises shouldn't be present
-            for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b)
-            {
-                unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
-                ai_assert( newBoneIndex != std::numeric_limits<unsigned int>::max() );
-                aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
-                newMesh->mBones[newBoneIndex]->mNumWeights++;
-
-                dstWeight->mVertexId = a;
-                dstWeight->mWeight = bonesOnThisVertex[b].second;
-            }
-        }
-
-        // I have the strange feeling that this will break apart at some point in time...
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Recursively updates the node's mesh list to account for the changed mesh list
-void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
-{
-    // rebuild the node's mesh index list
-    if( pNode->mNumMeshes > 0 )
-    {
-        std::vector<unsigned int> newMeshList;
-        for( unsigned int a = 0; a < pNode->mNumMeshes; ++a)
-        {
-            unsigned int srcIndex = pNode->mMeshes[a];
-            const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex];
-            newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
-        }
-
-        delete [] pNode->mMeshes;
-        pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size());
-        pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
-        std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
-    }
-
-    // do that also recursively for all children
-    for( unsigned int a = 0; a < pNode->mNumChildren; ++a )
-    {
-        UpdateNode( pNode->mChildren[a]);
-    }
-}

+ 0 - 111
thirdparty/assimp/code/Common/SplitByBoneCountProcess.h

@@ -1,111 +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 SplitByBoneCountProcess.h
-/// Defines a post processing step to split meshes with many bones into submeshes
-#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC
-#define AI_SPLITBYBONECOUNTPROCESS_H_INC
-
-#include <vector>
-#include "BaseProcess.h"
-
-#include <assimp/mesh.h>
-#include <assimp/scene.h>
-
-namespace Assimp
-{
-
-
-/** Postprocessing filter to split meshes with many bones into submeshes
- * so that each submesh has a certain max bone count.
- *
- * Applied BEFORE the JoinVertices-Step occurs.
- * Returns NON-UNIQUE vertices, splits by bone count.
-*/
-class SplitByBoneCountProcess : public BaseProcess
-{
-public:
-
-    SplitByBoneCountProcess();
-    ~SplitByBoneCountProcess();
-
-public:
-    /** Returns whether the processing step is present in the given flag.
-    * @param pFlags The processing flags the importer was called with. A
-    *   bitwise combination of #aiPostProcessSteps.
-    * @return true if the process is present in this flag fields,
-    *   false if not.
-    */
-    bool IsActive( unsigned int pFlags) const;
-
-    /** Called prior to ExecuteOnScene().
-    * The function is a request to the process to update its configuration
-    * basing on the Importer's configuration property list.
-    */
-    virtual void SetupProperties(const Importer* pImp);
-
-protected:
-    /** 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);
-
-    /// Splits the given mesh by bone count.
-    /// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
-    /// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
-    void SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const;
-
-    /// Recursively updates the node's mesh list to account for the changed mesh list
-    void UpdateNode( aiNode* pNode) const;
-
-public:
-    /// Max bone count. Splitting occurs if a mesh has more than that number of bones.
-    size_t mMaxBoneCount;
-
-    /// Per mesh index: Array of indices of the new submeshes.
-    std::vector< std::vector<unsigned int> > mSubMeshIndices;
-};
-
-} // end of namespace Assimp
-
-#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

+ 0 - 507
thirdparty/assimp/code/Common/StandardShapes.cpp

@@ -1,507 +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   StandardShapes.cpp
- *  @brief  Implementation of the StandardShapes class
- *
- *  The primitive geometry data comes from
- *  http://geometrictools.com/Documentation/PlatonicSolids.pdf.
- */
-
-#include <assimp/StandardShapes.h>
-#include <assimp/StringComparison.h>
-#include <stddef.h>
-#include <assimp/Defines.h>
-#include <assimp/mesh.h>
-
-namespace Assimp    {
-
-
-# define ADD_TRIANGLE(n0,n1,n2) \
-    positions.push_back(n0); \
-    positions.push_back(n1); \
-    positions.push_back(n2);
-
-#   define ADD_PENTAGON(n0,n1,n2,n3,n4) \
-    if (polygons) \
-    { \
-        positions.push_back(n0); \
-        positions.push_back(n1); \
-        positions.push_back(n2); \
-        positions.push_back(n3); \
-        positions.push_back(n4); \
-    } \
-    else \
-    { \
-        ADD_TRIANGLE(n0, n1, n2) \
-        ADD_TRIANGLE(n0, n2, n3) \
-        ADD_TRIANGLE(n0, n3, n4) \
-    }
-
-#   define ADD_QUAD(n0,n1,n2,n3) \
-    if (polygons) \
-    { \
-        positions.push_back(n0); \
-        positions.push_back(n1); \
-        positions.push_back(n2); \
-        positions.push_back(n3); \
-    } \
-    else \
-    { \
-        ADD_TRIANGLE(n0, n1, n2) \
-        ADD_TRIANGLE(n0, n2, n3) \
-    }
-
-
-// ------------------------------------------------------------------------------------------------
-// Fast subdivision for a mesh whose verts have a magnitude of 1
-void Subdivide(std::vector<aiVector3D>& positions)
-{
-    // assume this to be constant - (fixme: must be 1.0? I think so)
-    const ai_real fl1 = positions[0].Length();
-
-    unsigned int origSize = (unsigned int)positions.size();
-    for (unsigned int i = 0 ; i < origSize ; i+=3)
-    {
-        aiVector3D& tv0 = positions[i];
-        aiVector3D& tv1 = positions[i+1];
-        aiVector3D& tv2 = positions[i+2];
-
-        aiVector3D a = tv0, b = tv1, c = tv2;
-        aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1;
-        aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1;
-        aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1;
-
-        tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original
-        ADD_TRIANGLE(v1, v2, a);
-        ADD_TRIANGLE(v2, v3, c);
-        ADD_TRIANGLE(v3, v1, b);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Construct a mesh from given vertex positions
-aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions,
-    unsigned int numIndices)
-{
-    if (positions.empty() || !numIndices) return NULL;
-
-    // Determine which kinds of primitives the mesh consists of
-    aiMesh* out = new aiMesh();
-    switch (numIndices) {
-        case 1:
-            out->mPrimitiveTypes = aiPrimitiveType_POINT;
-            break;
-        case 2:
-            out->mPrimitiveTypes = aiPrimitiveType_LINE;
-            break;
-        case 3:
-            out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
-            break;
-        default:
-            out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
-            break;
-    };
-
-    out->mNumFaces = (unsigned int)positions.size() / numIndices;
-    out->mFaces = new aiFace[out->mNumFaces];
-    for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i) {
-        aiFace& f = out->mFaces[i];
-        f.mNumIndices = numIndices;
-        f.mIndices = new unsigned int[numIndices];
-        for (unsigned int j = 0; i < numIndices; ++i, ++a) {
-            f.mIndices[j] = a;
-        }
-    }
-    out->mNumVertices = (unsigned int)positions.size();
-    out->mVertices = new aiVector3D[out->mNumVertices];
-    ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
-
-    return out;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Construct a mesh with a specific shape (callback)
-aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
-    std::vector<aiVector3D>&))
-{
-    std::vector<aiVector3D> temp;
-    unsigned num = (*GenerateFunc)(temp);
-    return MakeMesh(temp,num);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Construct a mesh with a specific shape (callback)
-aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
-    std::vector<aiVector3D>&, bool))
-{
-    std::vector<aiVector3D> temp;
-    unsigned num = (*GenerateFunc)(temp,true);
-    return MakeMesh(temp,num);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Construct a mesh with a specific shape (callback)
-aiMesh* StandardShapes::MakeMesh (unsigned int num,  void (*GenerateFunc)(
-    unsigned int,std::vector<aiVector3D>&))
-{
-    std::vector<aiVector3D> temp;
-    (*GenerateFunc)(num,temp);
-    return MakeMesh(temp,3);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build an incosahedron with points.magnitude == 1
-unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D>& positions)
-{
-    positions.reserve(positions.size()+60);
-
-    const ai_real t = ( ai_real( 1.0 )+ ai_real( 2.236067977 ) ) / ai_real( 2.0 );
-    const ai_real s = std::sqrt(ai_real(1.0) + t*t);
-
-    const aiVector3D v0  = aiVector3D(t,1.0, 0.0)/s;
-    const aiVector3D v1  = aiVector3D(-t,1.0, 0.0)/s;
-    const aiVector3D v2  = aiVector3D(t,-1.0, 0.0)/s;
-    const aiVector3D v3  = aiVector3D(-t,-1.0, 0.0)/s;
-    const aiVector3D v4  = aiVector3D(1.0, 0.0, t)/s;
-    const aiVector3D v5  = aiVector3D(1.0, 0.0,-t)/s;
-    const aiVector3D v6  = aiVector3D(-1.0, 0.0,t)/s;
-    const aiVector3D v7  = aiVector3D(-1.0, 0.0,-t)/s;
-    const aiVector3D v8  = aiVector3D(0.0, t, 1.0)/s;
-    const aiVector3D v9  = aiVector3D(0.0,-t, 1.0)/s;
-    const aiVector3D v10 = aiVector3D(0.0, t,-1.0)/s;
-    const aiVector3D v11 = aiVector3D(0.0,-t,-1.0)/s;
-
-    ADD_TRIANGLE(v0,v8,v4);
-    ADD_TRIANGLE(v0,v5,v10);
-    ADD_TRIANGLE(v2,v4,v9);
-    ADD_TRIANGLE(v2,v11,v5);
-
-    ADD_TRIANGLE(v1,v6,v8);
-    ADD_TRIANGLE(v1,v10,v7);
-    ADD_TRIANGLE(v3,v9,v6);
-    ADD_TRIANGLE(v3,v7,v11);
-
-    ADD_TRIANGLE(v0,v10,v8);
-    ADD_TRIANGLE(v1,v8,v10);
-    ADD_TRIANGLE(v2,v9,v11);
-    ADD_TRIANGLE(v3,v11,v9);
-
-    ADD_TRIANGLE(v4,v2,v0);
-    ADD_TRIANGLE(v5,v0,v2);
-    ADD_TRIANGLE(v6,v1,v3);
-    ADD_TRIANGLE(v7,v3,v1);
-
-    ADD_TRIANGLE(v8,v6,v4);
-    ADD_TRIANGLE(v9,v4,v6);
-    ADD_TRIANGLE(v10,v5,v7);
-    ADD_TRIANGLE(v11,v7,v5);
-    return 3;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build a dodecahedron with points.magnitude == 1
-unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D>& positions,
-    bool polygons /*= false*/)
-{
-    positions.reserve(positions.size()+108);
-
-    const ai_real a = ai_real( 1.0 ) / ai_real(1.7320508);
-    const ai_real b = std::sqrt(( ai_real( 3.0 )- ai_real( 2.23606797))/ ai_real( 6.0) );
-    const ai_real c = std::sqrt(( ai_real( 3.0 )+ ai_real( 2.23606797f))/ ai_real( 6.0) );
-
-    const aiVector3D v0  = aiVector3D(a,a,a);
-    const aiVector3D v1  = aiVector3D(a,a,-a);
-    const aiVector3D v2  = aiVector3D(a,-a,a);
-    const aiVector3D v3  = aiVector3D(a,-a,-a);
-    const aiVector3D v4  = aiVector3D(-a,a,a);
-    const aiVector3D v5  = aiVector3D(-a,a,-a);
-    const aiVector3D v6  = aiVector3D(-a,-a,a);
-    const aiVector3D v7  = aiVector3D(-a,-a,-a);
-    const aiVector3D v8  = aiVector3D(b,c,0.0);
-    const aiVector3D v9  = aiVector3D(-b,c,0.0);
-    const aiVector3D v10 = aiVector3D(b,-c,0.0);
-    const aiVector3D v11 = aiVector3D(-b,-c,0.0);
-    const aiVector3D v12 = aiVector3D(c, 0.0, b);
-    const aiVector3D v13 = aiVector3D(c, 0.0, -b);
-    const aiVector3D v14 = aiVector3D(-c, 0.0, b);
-    const aiVector3D v15 = aiVector3D(-c, 0.0, -b);
-    const aiVector3D v16 = aiVector3D(0.0, b, c);
-    const aiVector3D v17 = aiVector3D(0.0, -b, c);
-    const aiVector3D v18 = aiVector3D(0.0, b, -c);
-    const aiVector3D v19 = aiVector3D(0.0, -b, -c);
-
-    ADD_PENTAGON(v0, v8, v9, v4, v16);
-    ADD_PENTAGON(v0, v12, v13, v1, v8);
-    ADD_PENTAGON(v0, v16, v17, v2, v12);
-    ADD_PENTAGON(v8, v1, v18, v5, v9);
-    ADD_PENTAGON(v12, v2, v10, v3, v13);
-    ADD_PENTAGON(v16, v4, v14, v6, v17);
-    ADD_PENTAGON(v9, v5, v15, v14, v4);
-
-    ADD_PENTAGON(v6, v11, v10, v2, v17);
-    ADD_PENTAGON(v3, v19, v18, v1, v13);
-    ADD_PENTAGON(v7, v15, v5, v18, v19);
-    ADD_PENTAGON(v7, v11, v6, v14, v15);
-    ADD_PENTAGON(v7, v19, v3, v10, v11);
-    return (polygons ? 5 : 3);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build an octahedron with points.magnitude == 1
-unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D>& positions)
-{
-    positions.reserve(positions.size()+24);
-
-    const aiVector3D v0  = aiVector3D(1.0, 0.0, 0.0) ;
-    const aiVector3D v1  = aiVector3D(-1.0, 0.0, 0.0);
-    const aiVector3D v2  = aiVector3D(0.0, 1.0, 0.0);
-    const aiVector3D v3  = aiVector3D(0.0, -1.0, 0.0);
-    const aiVector3D v4  = aiVector3D(0.0, 0.0, 1.0);
-    const aiVector3D v5  = aiVector3D(0.0, 0.0, -1.0);
-
-    ADD_TRIANGLE(v4,v0,v2);
-    ADD_TRIANGLE(v4,v2,v1);
-    ADD_TRIANGLE(v4,v1,v3);
-    ADD_TRIANGLE(v4,v3,v0);
-
-    ADD_TRIANGLE(v5,v2,v0);
-    ADD_TRIANGLE(v5,v1,v2);
-    ADD_TRIANGLE(v5,v3,v1);
-    ADD_TRIANGLE(v5,v0,v3);
-    return 3;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build a tetrahedron with points.magnitude == 1
-unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D>& positions)
-{
-    positions.reserve(positions.size()+9);
-
-    const ai_real invThree = ai_real( 1.0 ) / ai_real( 3.0 );
-    const ai_real a = ai_real( 1.41421 ) * invThree;
-    const ai_real b = ai_real( 2.4494 ) * invThree;
-
-    const aiVector3D v0  = aiVector3D(0.0,0.0,1.0);
-    const aiVector3D v1  = aiVector3D(2*a,0,-invThree );
-    const aiVector3D v2  = aiVector3D(-a,b,-invThree );
-    const aiVector3D v3  = aiVector3D(-a,-b,-invThree );
-
-    ADD_TRIANGLE(v0,v1,v2);
-    ADD_TRIANGLE(v0,v2,v3);
-    ADD_TRIANGLE(v0,v3,v1);
-    ADD_TRIANGLE(v1,v3,v2);
-    return 3;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build a hexahedron with points.magnitude == 1
-unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D>& positions,
-    bool polygons /*= false*/)
-{
-    positions.reserve(positions.size()+36);
-    const ai_real length = ai_real(1.0)/ai_real(1.73205080);
-
-    const aiVector3D v0  = aiVector3D(-1.0,-1.0,-1.0)*length;
-    const aiVector3D v1  = aiVector3D(1.0,-1.0,-1.0)*length;
-    const aiVector3D v2  = aiVector3D(1.0,1.0,-1.0)*length;
-    const aiVector3D v3  = aiVector3D(-1.0,1.0,-1.0)*length;
-    const aiVector3D v4  = aiVector3D(-1.0,-1.0,1.0)*length;
-    const aiVector3D v5  = aiVector3D(1.0,-1.0,1.0)*length;
-    const aiVector3D v6  = aiVector3D(1.0,1.0,1.0)*length;
-    const aiVector3D v7  = aiVector3D(-1.0,1.0,1.0)*length;
-
-    ADD_QUAD(v0,v3,v2,v1);
-    ADD_QUAD(v0,v1,v5,v4);
-    ADD_QUAD(v0,v4,v7,v3);
-    ADD_QUAD(v6,v5,v1,v2);
-    ADD_QUAD(v6,v2,v3,v7);
-    ADD_QUAD(v6,v7,v4,v5);
-    return (polygons ? 4 : 3);
-}
-
-// Cleanup ...
-#undef ADD_TRIANGLE
-#undef ADD_QUAD
-#undef ADD_PENTAGON
-
-// ------------------------------------------------------------------------------------------------
-// Create a subdivision sphere
-void StandardShapes::MakeSphere(unsigned int    tess,
-    std::vector<aiVector3D>& positions)
-{
-    // Reserve enough storage. Every subdivision
-    // splits each triangle in 4, the icosahedron consists of 60 verts
-    positions.reserve(positions.size()+60 * integer_pow(4, tess));
-
-    // Construct an icosahedron to start with
-    MakeIcosahedron(positions);
-
-    // ... and subdivide it until the requested output
-    // tessellation is reached
-    for (unsigned int i = 0; i<tess;++i)
-        Subdivide(positions);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build a cone
-void StandardShapes::MakeCone(ai_real height,ai_real radius1,
-    ai_real radius2,unsigned int tess,
-    std::vector<aiVector3D>& positions,bool bOpen /*= false */)
-{
-    // Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE
-    if (tess < 3 || !height)
-        return;
-
-    size_t old = positions.size();
-
-    // No negative radii
-    radius1 = std::fabs(radius1);
-    radius2 = std::fabs(radius2);
-
-    ai_real halfHeight = height / ai_real(2.0);
-
-    // radius1 is always the smaller one
-    if (radius2 > radius1)
-    {
-        std::swap(radius2,radius1);
-        halfHeight = -halfHeight;
-    }
-    else old = SIZE_MAX;
-
-    // Use a large epsilon to check whether the cone is pointy
-    if (radius1 < (radius2-radius1)*10e-3)radius1 = 0.0;
-
-    // We will need 3*2 verts per segment + 3*2 verts per segment
-    // if the cone is closed
-    const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0);
-    positions.reserve(positions.size () + mem);
-
-    // Now construct all segments
-    const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
-    const ai_real angle_max   = (ai_real)AI_MATH_TWO_PI;
-
-    ai_real s = 1.0; // std::cos(angle == 0);
-    ai_real t = 0.0; // std::sin(angle == 0);
-
-    for (ai_real angle = 0.0; angle < angle_max; )
-    {
-        const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 );
-        const aiVector3D v2 = aiVector3D (s * radius2,  halfHeight, t * radius2 );
-
-        const ai_real next = angle + angle_delta;
-        ai_real s2 = std::cos(next);
-        ai_real t2 = std::sin(next);
-
-        const aiVector3D v3 = aiVector3D (s2 * radius2,  halfHeight, t2 * radius2 );
-        const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 );
-
-        positions.push_back(v1);
-        positions.push_back(v2);
-        positions.push_back(v3);
-        positions.push_back(v4);
-        positions.push_back(v1);
-        positions.push_back(v3);
-
-        if (!bOpen)
-        {
-            // generate the end 'cap'
-            positions.push_back(aiVector3D(s * radius2,  halfHeight, t * radius2 ));
-            positions.push_back(aiVector3D(s2 * radius2,  halfHeight, t2 * radius2 ));
-            positions.push_back(aiVector3D(0.0, halfHeight, 0.0));
-
-
-            if (radius1)
-            {
-                // generate the other end 'cap'
-                positions.push_back(aiVector3D(s * radius1,  -halfHeight, t * radius1 ));
-                positions.push_back(aiVector3D(s2 * radius1,  -halfHeight, t2 * radius1 ));
-                positions.push_back(aiVector3D(0.0, -halfHeight, 0.0));
-
-            }
-        }
-        s = s2;
-        t = t2;
-        angle = next;
-    }
-
-    // Need to flip face order?
-    if ( SIZE_MAX != old )  {
-        for (size_t p = old; p < positions.size();p += 3) {
-            std::swap(positions[p],positions[p+1]);
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Build a circle
-void StandardShapes::MakeCircle(ai_real radius, unsigned int tess,
-    std::vector<aiVector3D>& positions)
-{
-    // Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE
-    if (tess < 3 || !radius)
-        return;
-
-    radius = std::fabs(radius);
-
-    // We will need 3 vertices per segment
-    positions.reserve(positions.size()+tess*3);
-
-    const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
-    const ai_real angle_max   = (ai_real)AI_MATH_TWO_PI;
-
-    ai_real s = 1.0; // std::cos(angle == 0);
-    ai_real t = 0.0; // std::sin(angle == 0);
-
-    for (ai_real angle = 0.0; angle < angle_max;  )
-    {
-        positions.push_back(aiVector3D(s * radius,0.0,t * radius));
-        angle += angle_delta;
-        s = std::cos(angle);
-        t = std::sin(angle);
-        positions.push_back(aiVector3D(s * radius,0.0,t * radius));
-
-        positions.push_back(aiVector3D(0.0,0.0,0.0));
-    }
-}
-
-} // ! Assimp

+ 0 - 101
thirdparty/assimp/code/Common/StdOStreamLogStream.h

@@ -1,101 +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  StdOStreamLogStream.h
-*  @brief Implementation of StdOStreamLogStream
-*/
-
-#ifndef AI_STROSTREAMLOGSTREAM_H_INC
-#define AI_STROSTREAMLOGSTREAM_H_INC
-
-#include <assimp/LogStream.hpp>
-#include <ostream>
-
-namespace Assimp    {
-
-// ---------------------------------------------------------------------------
-/** @class  StdOStreamLogStream
- *  @brief  Logs into a std::ostream
- */
-class StdOStreamLogStream : public LogStream {
-public:
-    /** @brief  Construction from an existing std::ostream
-     *  @param _ostream Output stream to be used
-    */
-    explicit StdOStreamLogStream(std::ostream& _ostream);
-
-    /** @brief  Destructor  */
-    ~StdOStreamLogStream();
-
-    /** @brief  Writer  */
-    void write(const char* message);
-
-private:
-    std::ostream& mOstream;
-};
-
-// ---------------------------------------------------------------------------
-//  Default constructor
-inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream)
-: mOstream   (_ostream){
-    // empty
-}
-
-// ---------------------------------------------------------------------------
-//  Default constructor
-inline StdOStreamLogStream::~StdOStreamLogStream() {
-    // empty
-}
-
-// ---------------------------------------------------------------------------
-//  Write method
-inline void StdOStreamLogStream::write(const char* message) {
-    mOstream << message;
-    mOstream.flush();
-}
-
-// ---------------------------------------------------------------------------
-
-}   // Namespace Assimp
-
-#endif // guard

+ 0 - 589
thirdparty/assimp/code/Common/Subdivision.cpp

@@ -1,589 +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.
-
-----------------------------------------------------------------------
-*/
-
-#include <assimp/Subdivision.h>
-#include <assimp/SceneCombiner.h>
-#include <assimp/SpatialSort.h>
-#include <assimp/Vertex.h>
-#include <assimp/ai_assert.h>
-
-#include "PostProcessing/ProcessHelper.h"
-
-#include <stdio.h>
-
-using namespace Assimp;
-void mydummy() {}
-
-// ------------------------------------------------------------------------------------------------
-/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The
- *  implementation is basing on recursive refinement. Directly evaluating the result is also
- *  possible and much quicker, but it depends on lengthy matrix lookup tables. */
-// ------------------------------------------------------------------------------------------------
-class CatmullClarkSubdivider : public Subdivider {
-public:
-    void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input);
-    void Subdivide (aiMesh** smesh, size_t nmesh,
-        aiMesh** out, unsigned int num, bool discard_input);
-
-    // ---------------------------------------------------------------------------
-    /** Intermediate description of an edge between two corners of a polygon*/
-    // ---------------------------------------------------------------------------
-    struct Edge
-    {
-        Edge()
-            : ref(0)
-        {}
-        Vertex edge_point, midpoint;
-        unsigned int ref;
-    };
-
-    typedef std::vector<unsigned int> UIntVector;
-    typedef std::map<uint64_t,Edge> EdgeMap;
-
-    // ---------------------------------------------------------------------------
-    // Hashing function to derive an index into an #EdgeMap from two given
-    // 'unsigned int' vertex coordinates (!!distinct coordinates - same
-    // vertex position == same index!!).
-    // NOTE - this leads to rare hash collisions if a) sizeof(unsigned int)>4
-    // and (id[0]>2^32-1 or id[0]>2^32-1).
-    // MAKE_EDGE_HASH() uses temporaries, so INIT_EDGE_HASH() needs to be put
-    // at the head of every function which is about to use MAKE_EDGE_HASH().
-    // Reason is that the hash is that hash construction needs to hold the
-    // invariant id0<id1 to identify an edge - else two hashes would refer
-    // to the same edge.
-    // ---------------------------------------------------------------------------
-#define MAKE_EDGE_HASH(id0,id1) (eh_tmp0__=id0,eh_tmp1__=id1,\
-    (eh_tmp0__<eh_tmp1__?std::swap(eh_tmp0__,eh_tmp1__):mydummy()),(uint64_t)eh_tmp0__^((uint64_t)eh_tmp1__<<32u))
-
-
-#define INIT_EDGE_HASH_TEMPORARIES()\
-    unsigned int eh_tmp0__, eh_tmp1__;
-
-private:
-    void InternSubdivide (const aiMesh* const * smesh,
-        size_t nmesh,aiMesh** out, unsigned int num);
-};
-
-
-// ------------------------------------------------------------------------------------------------
-// Construct a subdivider of a specific type
-Subdivider* Subdivider::Create (Algorithm algo)
-{
-    switch (algo)
-    {
-    case CATMULL_CLARKE:
-        return new CatmullClarkSubdivider();
-    };
-
-    ai_assert(false);
-    return NULL; // shouldn't happen
-}
-
-// ------------------------------------------------------------------------------------------------
-// Call the Catmull Clark subdivision algorithm for one mesh
-void  CatmullClarkSubdivider::Subdivide (
-    aiMesh* mesh,
-    aiMesh*& out,
-    unsigned int num,
-    bool discard_input
-    )
-{
-    ai_assert(mesh != out);
-    
-    Subdivide(&mesh,1,&out,num,discard_input);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Call the Catmull Clark subdivision algorithm for multiple meshes
-void CatmullClarkSubdivider::Subdivide (
-    aiMesh** smesh,
-    size_t nmesh,
-    aiMesh** out,
-    unsigned int num,
-    bool discard_input
-    )
-{
-    ai_assert( NULL != smesh );
-    ai_assert( NULL != out );
-
-    // course, both regions may not overlap
-    ai_assert(smesh<out || smesh+nmesh>out+nmesh);
-    if (!num) {
-        // No subdivision at all. Need to copy all the meshes .. argh.
-        if (discard_input) {
-            for (size_t s = 0; s < nmesh; ++s) {
-                out[s] = smesh[s];
-                smesh[s] = NULL;
-            }
-        }
-        else {
-            for (size_t s = 0; s < nmesh; ++s) {
-                SceneCombiner::Copy(out+s,smesh[s]);
-            }
-        }
-        return;
-    }
-
-    std::vector<aiMesh*> inmeshes;
-    std::vector<aiMesh*> outmeshes;
-    std::vector<unsigned int> maptbl;
-
-    inmeshes.reserve(nmesh);
-    outmeshes.reserve(nmesh);
-    maptbl.reserve(nmesh);
-
-    // Remove pure line and point meshes from the working set to reduce the
-    // number of edge cases the subdivider is forced to deal with. Line and
-    // point meshes are simply passed through.
-    for (size_t s = 0; s < nmesh; ++s) {
-        aiMesh* i = smesh[s];
-        // FIX - mPrimitiveTypes might not yet be initialized
-        if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) {
-            ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh");
-
-            if (discard_input) {
-                out[s] = i;
-                smesh[s] = NULL;
-            }
-            else {
-                SceneCombiner::Copy(out+s,i);
-            }
-            continue;
-        }
-
-        outmeshes.push_back(NULL);inmeshes.push_back(i);
-        maptbl.push_back(static_cast<unsigned int>(s));
-    }
-
-    // Do the actual subdivision on the preallocated storage. InternSubdivide
-    // *always* assumes that enough storage is available, it does not bother
-    // checking any ranges.
-    ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size());
-    if (inmeshes.empty()) {
-        ASSIMP_LOG_WARN("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything");
-        return;
-    }
-    InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num);
-    for (unsigned int i = 0; i < maptbl.size(); ++i) {
-        ai_assert(nullptr != outmeshes[i]);
-        out[maptbl[i]] = outmeshes[i];
-    }
-
-    if (discard_input) {
-        for (size_t s = 0; s < nmesh; ++s) {
-            delete smesh[s];
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further
-// optimizations (except we're using some nice LUTs). A description of the algorithm can be found
-// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface
-//
-// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's
-// expected total runtime complexity. The implementation is able to work in-place on the same
-// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate
-// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings).
-// Previous data is replaced/deleted then.
-// ------------------------------------------------------------------------------------------------
-void CatmullClarkSubdivider::InternSubdivide (
-    const aiMesh* const * smesh,
-    size_t nmesh,
-    aiMesh** out,
-    unsigned int num
-    )
-{
-    ai_assert(NULL != smesh && NULL != out);
-    INIT_EDGE_HASH_TEMPORARIES();
-
-    // no subdivision requested or end of recursive refinement
-    if (!num) {
-        return;
-    }
-
-    UIntVector maptbl;
-    SpatialSort spatial;
-
-    // ---------------------------------------------------------------------
-    // 0. Offset table to index all meshes continuously, generate a spatially
-    // sorted representation of all vertices in all meshes.
-    // ---------------------------------------------------------------------
-    typedef std::pair<unsigned int,unsigned int> IntPair;
-    std::vector<IntPair> moffsets(nmesh);
-    unsigned int totfaces = 0, totvert = 0;
-    for (size_t t = 0; t < nmesh; ++t) {
-        const aiMesh* mesh = smesh[t];
-
-        spatial.Append(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D),false);
-        moffsets[t] = IntPair(totfaces,totvert);
-
-        totfaces += mesh->mNumFaces;
-        totvert  += mesh->mNumVertices;
-    }
-
-    spatial.Finalize();
-    const unsigned int num_unique = spatial.GenerateMappingTable(maptbl,ComputePositionEpsilon(smesh,nmesh));
-
-
-#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second+vert_idx)
-#define   FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first+face_idx)
-
-    // ---------------------------------------------------------------------
-    // 1. Compute the centroid point for all faces
-    // ---------------------------------------------------------------------
-    std::vector<Vertex> centroids(totfaces);
-    unsigned int nfacesout = 0;
-    for (size_t t = 0, n = 0; t < nmesh; ++t) {
-        const aiMesh* mesh = smesh[t];
-        for (unsigned int i = 0; i < mesh->mNumFaces;++i,++n)
-        {
-            const aiFace& face = mesh->mFaces[i];
-            Vertex& c = centroids[n];
-
-            for (unsigned int a = 0; a < face.mNumIndices;++a) {
-                c += Vertex(mesh,face.mIndices[a]);
-            }
-
-            c /= static_cast<float>(face.mNumIndices);
-            nfacesout += face.mNumIndices;
-        }
-    }
-
-    {
-    // we want edges to go away before the recursive calls so begin a new scope
-    EdgeMap edges;
-
-    // ---------------------------------------------------------------------
-    // 2. Set each edge point to be the average of all neighbouring
-    // face points and original points. Every edge exists twice
-    // if there is a neighboring face.
-    // ---------------------------------------------------------------------
-    for (size_t t = 0; t < nmesh; ++t) {
-        const aiMesh* mesh = smesh[t];
-
-        for (unsigned int i = 0; i < mesh->mNumFaces;++i)   {
-            const aiFace& face = mesh->mFaces[i];
-
-            for (unsigned int p =0; p< face.mNumIndices; ++p) {
-                const unsigned int id[] = {
-                    face.mIndices[p],
-                    face.mIndices[p==face.mNumIndices-1?0:p+1]
-                };
-                const unsigned int mp[] = {
-                    maptbl[FLATTEN_VERTEX_IDX(t,id[0])],
-                    maptbl[FLATTEN_VERTEX_IDX(t,id[1])]
-                };
-
-                Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])];
-                e.ref++;
-                if (e.ref<=2) {
-                    if (e.ref==1) { // original points (end points) - add only once
-                        e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]);
-                        e.midpoint *= 0.5f;
-                    }
-                    e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)];
-                }
-            }
-        }
-    }
-
-    // ---------------------------------------------------------------------
-    // 3. Normalize edge points
-    // ---------------------------------------------------------------------
-    {unsigned int bad_cnt = 0;
-    for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) {
-        if ((*it).second.ref < 2) {
-            ai_assert((*it).second.ref);
-            ++bad_cnt;
-        }
-        (*it).second.edge_point *= 1.f/((*it).second.ref+2.f);
-    }
-
-    if (bad_cnt) {
-        // Report the number of bad edges. bad edges are referenced by less than two
-        // faces in the mesh. They occur at outer model boundaries in non-closed
-        // shapes.
-        ASSIMP_LOG_DEBUG_F("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ", 
-            static_cast<unsigned int>(edges.size()), " edges). ");
-    }}
-
-    // ---------------------------------------------------------------------
-    // 4. Compute a vertex-face adjacency table. We can't reuse the code
-    // from VertexTriangleAdjacency because we need the table for multiple
-    // meshes and out vertex indices need to be mapped to distinct values
-    // first.
-    // ---------------------------------------------------------------------
-    UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); {
-    for (size_t t = 0; t < nmesh; ++t) {
-        const aiMesh* const minp = smesh[t];
-        for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
-
-            const aiFace& f = minp->mFaces[i];
-            for (unsigned int n = 0; n < f.mNumIndices; ++n) {
-                ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]];
-            }
-        }
-    }
-    unsigned int cur = 0;
-    for (size_t i = 0; i < cntadjfac.size(); ++i) {
-        ofsadjvec[i+1] = cur;
-        cur += cntadjfac[i];
-    }
-    for (size_t t = 0; t < nmesh; ++t) {
-        const aiMesh* const minp = smesh[t];
-        for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
-
-            const aiFace& f = minp->mFaces[i];
-            for (unsigned int n = 0; n < f.mNumIndices; ++n) {
-                faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i);
-            }
-        }
-    }
-
-    // check the other way round for consistency
-#ifdef ASSIMP_BUILD_DEBUG
-
-    for (size_t t = 0; t < ofsadjvec.size()-1; ++t) {
-        for (unsigned int m = 0; m <  cntadjfac[t]; ++m) {
-            const unsigned int fidx = faceadjac[ofsadjvec[t]+m];
-            ai_assert(fidx < totfaces);
-            for (size_t n = 1; n < nmesh; ++n) {
-
-                if (moffsets[n].first > fidx) {
-                    const aiMesh* msh = smesh[--n];
-                    const aiFace& f = msh->mFaces[fidx-moffsets[n].first];
-
-                    bool haveit = false;
-                    for (unsigned int i = 0; i < f.mNumIndices; ++i) {
-                        if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) {
-                            haveit = true;
-                            break;
-                        }
-                    }
-                    ai_assert(haveit);
-                    if (!haveit) {
-                        ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Index not used");
-                    }
-                    break;
-                }
-            }
-        }
-    }
-
-#endif
-    }
-
-#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \
-    fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx]
-
-    typedef std::pair<bool,Vertex> TouchedOVertex;
-    std::vector<TouchedOVertex > new_points(num_unique,TouchedOVertex(false,Vertex()));
-    // ---------------------------------------------------------------------
-    // 5. Spawn a quad from each face point to the corresponding edge points
-    // the original points being the fourth quad points.
-    // ---------------------------------------------------------------------
-    for (size_t t = 0; t < nmesh; ++t) {
-        const aiMesh* const minp = smesh[t];
-        aiMesh* const mout = out[t] = new aiMesh();
-
-        for (unsigned int a  = 0; a < minp->mNumFaces; ++a) {
-            mout->mNumFaces += minp->mFaces[a].mNumIndices;
-        }
-
-        // We need random access to the old face buffer, so reuse is not possible.
-        mout->mFaces = new aiFace[mout->mNumFaces];
-
-        mout->mNumVertices = mout->mNumFaces*4;
-        mout->mVertices = new aiVector3D[mout->mNumVertices];
-
-        // quads only, keep material index
-        mout->mPrimitiveTypes = aiPrimitiveType_POLYGON;
-        mout->mMaterialIndex = minp->mMaterialIndex;
-
-        if (minp->HasNormals()) {
-            mout->mNormals = new aiVector3D[mout->mNumVertices];
-        }
-
-        if (minp->HasTangentsAndBitangents()) {
-            mout->mTangents = new aiVector3D[mout->mNumVertices];
-            mout->mBitangents = new aiVector3D[mout->mNumVertices];
-        }
-
-        for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) {
-            mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices];
-            mout->mNumUVComponents[i] = minp->mNumUVComponents[i];
-        }
-
-        for(unsigned int i = 0; minp->HasVertexColors(i); ++i) {
-            mout->mColors[i] = new aiColor4D[mout->mNumVertices];
-        }
-
-        mout->mNumVertices = mout->mNumFaces<<2u;
-        for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) {
-
-            const aiFace& face = minp->mFaces[i];
-            for (unsigned int a = 0; a < face.mNumIndices;++a)  {
-
-                // Get a clean new face.
-                aiFace& faceOut = mout->mFaces[n++];
-                faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4];
-
-                // Spawn a new quadrilateral (ccw winding) for this original point between:
-                // a) face centroid
-                centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++);
-
-                // b) adjacent edge on the left, seen from the centroid
-                const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
-                    maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1])
-                    ])];  // fixme: replace with mod face.mNumIndices?
-
-                // c) adjacent edge on the right, seen from the centroid
-                const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
-                    maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1])
-                    ])];  // fixme: replace with mod face.mNumIndices?
-
-                e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++);
-                e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++);
-
-                // d= original point P with distinct index i
-                // F := 0
-                // R := 0
-                // n := 0
-                // for each face f containing i
-                //    F := F+ centroid of f
-                //    R := R+ midpoint of edge of f from i to i+1
-                //    n := n+1
-                //
-                // (F+2R+(n-3)P)/n
-                const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])];
-                TouchedOVertex& ov = new_points[org];
-
-                if (!ov.first) {
-                    ov.first = true;
-
-                    const unsigned int* adj; unsigned int cnt;
-                    GET_ADJACENT_FACES_AND_CNT(org,adj,cnt);
-
-                    if (cnt < 3) {
-                        ov.second = Vertex(minp,face.mIndices[a]);
-                    }
-                    else {
-
-                        Vertex F,R;
-                        for (unsigned int o = 0; o < cnt; ++o) {
-                            ai_assert(adj[o] < totfaces);
-                            F += centroids[adj[o]];
-
-                            // adj[0] is a global face index - search the face in the mesh list
-                            const aiMesh* mp = NULL;
-                            size_t nidx;
-
-                            if (adj[o] < moffsets[0].first) {
-                                mp = smesh[nidx=0];
-                            }
-                            else {
-                                for (nidx = 1; nidx<= nmesh; ++nidx) {
-                                    if (nidx == nmesh ||moffsets[nidx].first > adj[o]) {
-                                        mp = smesh[--nidx];
-                                        break;
-                                    }
-                                }
-                            }
-
-                            ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces);
-                            const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first];
-                            bool haveit = false;
-
-                            // find our original point in the face
-                            for (unsigned int m = 0; m < f.mNumIndices; ++m) {
-                                if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) {
-
-                                    // add *both* edges. this way, we can be sure that we add
-                                    // *all* adjacent edges to R. In a closed shape, every
-                                    // edge is added twice - so we simply leave out the
-                                    // factor 2.f in the amove formula and get the right
-                                    // result.
-
-                                    const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
-                                        nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])];
-                                    // fixme: replace with mod face.mNumIndices?
-
-                                    const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
-                                        nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])];
-                                    // fixme: replace with mod face.mNumIndices?
-                                    R += c0.midpoint+c1.midpoint;
-
-                                    haveit = true;
-                                    break;
-                                }
-                            }
-
-                            // this invariant *must* hold if the vertex-to-face adjacency table is valid
-                            ai_assert(haveit);
-                            if ( !haveit ) {
-                                ASSIMP_LOG_WARN( "OBJ: no name for material library specified." );
-                            }
-                        }
-
-                        const float div = static_cast<float>(cnt), divsq = 1.f/(div*div);
-                        ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq;
-                    }
-                }
-                ov.second.SortBack(mout,faceOut.mIndices[2]=v++);
-            }
-        }
-    }
-    }  // end of scope for edges, freeing its memory
-
-    // ---------------------------------------------------------------------
-    // 7. Apply the next subdivision step.
-    // ---------------------------------------------------------------------
-    if (num != 1) {
-        std::vector<aiMesh*> tmp(nmesh);
-        InternSubdivide (out,nmesh,&tmp.front(),num-1);
-        for (size_t i = 0; i < nmesh; ++i) {
-            delete out[i];
-            out[i] = tmp[i];
-        }
-    }
-}

+ 0 - 248
thirdparty/assimp/code/Common/TargetAnimation.cpp

@@ -1,248 +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.
-
-----------------------------------------------------------------------
-*/
-
-#include "TargetAnimation.h"
-#include <algorithm>
-#include <assimp/ai_assert.h>
-
-using namespace Assimp;
-
-
-// ------------------------------------------------------------------------------------------------
-KeyIterator::KeyIterator(const std::vector<aiVectorKey>* _objPos,
-    const std::vector<aiVectorKey>* _targetObjPos,
-    const aiVector3D*  defaultObjectPos /*= NULL*/,
-    const aiVector3D*  defaultTargetPos /*= NULL*/)
-
-        :   reachedEnd      (false)
-        ,   curTime         (-1.)
-        ,   objPos          (_objPos)
-        ,   targetObjPos    (_targetObjPos)
-        ,   nextObjPos      (0)
-        ,   nextTargetObjPos(0)
-{
-    // Generate default transformation tracks if necessary
-    if (!objPos || objPos->empty())
-    {
-        defaultObjPos.resize(1);
-        defaultObjPos.front().mTime  = 10e10;
-
-        if (defaultObjectPos)
-            defaultObjPos.front().mValue = *defaultObjectPos;
-
-        objPos = & defaultObjPos;
-    }
-    if (!targetObjPos || targetObjPos->empty())
-    {
-        defaultTargetObjPos.resize(1);
-        defaultTargetObjPos.front().mTime  = 10e10;
-
-        if (defaultTargetPos)
-            defaultTargetObjPos.front().mValue = *defaultTargetPos;
-
-        targetObjPos = & defaultTargetObjPos;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-template <class T>
-inline T Interpolate(const T& one, const T& two, ai_real val)
-{
-    return one + (two-one)*val;
-}
-
-// ------------------------------------------------------------------------------------------------
-void KeyIterator::operator ++()
-{
-    // If we are already at the end of all keyframes, return
-    if (reachedEnd) {
-        return;
-    }
-
-    // Now search in all arrays for the time value closest
-    // to our current position on the time line
-    double d0,d1;
-
-    d0 = objPos->at      ( std::min ( nextObjPos, static_cast<unsigned int>(objPos->size()-1))             ).mTime;
-    d1 = targetObjPos->at( std::min ( nextTargetObjPos, static_cast<unsigned int>(targetObjPos->size()-1)) ).mTime;
-
-    // Easiest case - all are identical. In this
-    // case we don't need to interpolate so we can
-    // return earlier
-    if ( d0 == d1 )
-    {
-        curTime = d0;
-        curPosition = objPos->at(nextObjPos).mValue;
-        curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue;
-
-        // increment counters
-        if (objPos->size() != nextObjPos-1)
-            ++nextObjPos;
-
-        if (targetObjPos->size() != nextTargetObjPos-1)
-            ++nextTargetObjPos;
-    }
-
-    // An object position key is closest to us
-    else if (d0 < d1)
-    {
-        curTime = d0;
-
-        // interpolate the other
-        if (1 == targetObjPos->size() || !nextTargetObjPos) {
-            curTargetPosition = targetObjPos->at(0).mValue;
-        }
-        else
-        {
-            const aiVectorKey& last  = targetObjPos->at(nextTargetObjPos);
-            const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1);
-
-            curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real) (
-                (curTime-first.mTime) / (last.mTime-first.mTime) ));
-        }
-
-        if (objPos->size() != nextObjPos-1)
-            ++nextObjPos;
-    }
-    // A target position key is closest to us
-    else
-    {
-        curTime = d1;
-
-        // interpolate the other
-        if (1 == objPos->size() || !nextObjPos) {
-            curPosition = objPos->at(0).mValue;
-        }
-        else
-        {
-            const aiVectorKey& last  = objPos->at(nextObjPos);
-            const aiVectorKey& first = objPos->at(nextObjPos-1);
-
-            curPosition = Interpolate(first.mValue, last.mValue, (ai_real) (
-                (curTime-first.mTime) / (last.mTime-first.mTime)));
-        }
-
-        if (targetObjPos->size() != nextTargetObjPos-1)
-            ++nextTargetObjPos;
-    }
-
-    if (nextObjPos >= objPos->size()-1 &&
-        nextTargetObjPos >= targetObjPos->size()-1)
-    {
-        // We reached the very last keyframe
-        reachedEnd = true;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-void TargetAnimationHelper::SetTargetAnimationChannel (
-    const std::vector<aiVectorKey>* _targetPositions)
-{
-    ai_assert(NULL != _targetPositions);
-    targetPositions = _targetPositions;
-}
-
-// ------------------------------------------------------------------------------------------------
-void TargetAnimationHelper::SetMainAnimationChannel (
-    const std::vector<aiVectorKey>* _objectPositions)
-{
-    ai_assert(NULL != _objectPositions);
-    objectPositions = _objectPositions;
-}
-
-// ------------------------------------------------------------------------------------------------
-void TargetAnimationHelper::SetFixedMainAnimationChannel(
-    const aiVector3D& fixed)
-{
-    objectPositions = NULL; // just to avoid confusion
-    fixedMain = fixed;
-}
-
-// ------------------------------------------------------------------------------------------------
-void TargetAnimationHelper::Process(std::vector<aiVectorKey>* distanceTrack)
-{
-    ai_assert(NULL != targetPositions && NULL != distanceTrack);
-
-    // TODO: in most cases we won't need the extra array
-    std::vector<aiVectorKey>  real;
-
-    std::vector<aiVectorKey>* fill = (distanceTrack == objectPositions ? &real : distanceTrack);
-    fill->reserve(std::max( objectPositions->size(), targetPositions->size() ));
-
-    // Iterate through all object keys and interpolate their values if necessary.
-    // Then get the corresponding target position, compute the difference
-    // vector between object and target position. Then compute a rotation matrix
-    // that rotates the base vector of the object coordinate system at that time
-    // to match the diff vector.
-
-    KeyIterator iter(objectPositions,targetPositions,&fixedMain);
-    for (;!iter.Finished();++iter)
-    {
-        const aiVector3D&  position  = iter.GetCurPosition();
-        const aiVector3D&  tposition = iter.GetCurTargetPosition();
-
-        // diff vector
-        aiVector3D diff = tposition - position;
-        ai_real f = diff.Length();
-
-        // output distance vector
-        if (f)
-        {
-            fill->push_back(aiVectorKey());
-            aiVectorKey& v = fill->back();
-            v.mTime  = iter.GetCurTime();
-            v.mValue = diff;
-
-            diff /= f;
-        }
-        else
-        {
-            // FIXME: handle this
-        }
-
-        // diff is now the vector in which our camera is pointing
-    }
-
-    if (real.size()) {
-        *distanceTrack = real;
-    }
-}

+ 0 - 183
thirdparty/assimp/code/Common/TargetAnimation.h

@@ -1,183 +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 a helper class for the ASE and 3DS loaders to
- help them compute camera and spot light animation channels */
-#ifndef AI_TARGET_ANIMATION_H_INC
-#define AI_TARGET_ANIMATION_H_INC
-
-#include <assimp/anim.h>
-#include <vector>
-
-namespace Assimp    {
-
-
-
-// ---------------------------------------------------------------------------
-/** Helper class to iterate through all keys in an animation channel.
- *
- *  Missing tracks are interpolated. This is a helper class for
- *  TargetAnimationHelper, but it can be freely used for other purposes.
-*/
-class KeyIterator
-{
-public:
-
-
-    // ------------------------------------------------------------------
-    /** Constructs a new key iterator
-     *
-     *  @param _objPos Object position track. May be NULL.
-     *  @param _targetObjPos Target object position track. May be NULL.
-     *  @param defaultObjectPos Default object position to be used if
-     *    no animated track is available. May be NULL.
-     *  @param defaultTargetPos Default target position to be used if
-     *    no animated track is available. May be NULL.
-     */
-    KeyIterator(const std::vector<aiVectorKey>* _objPos,
-        const std::vector<aiVectorKey>* _targetObjPos,
-        const aiVector3D*  defaultObjectPos = NULL,
-        const aiVector3D*  defaultTargetPos = NULL);
-
-    // ------------------------------------------------------------------
-    /** Returns true if all keys have been processed
-     */
-    bool Finished() const
-        {return reachedEnd;}
-
-    // ------------------------------------------------------------------
-    /** Increment the iterator
-     */
-    void operator++();
-    inline void operator++(int)
-        {return ++(*this);}
-
-
-
-    // ------------------------------------------------------------------
-    /** Getters to retrieve the current state of the iterator
-     */
-    inline const aiVector3D& GetCurPosition() const
-        {return curPosition;}
-
-    inline const aiVector3D& GetCurTargetPosition() const
-        {return curTargetPosition;}
-
-    inline double GetCurTime() const
-        {return curTime;}
-
-private:
-
-    //! Did we reach the end?
-    bool reachedEnd;
-
-    //! Represents the current position of the iterator
-    aiVector3D curPosition, curTargetPosition;
-
-    double curTime;
-
-    //! Input tracks and the next key to process
-    const std::vector<aiVectorKey>* objPos,*targetObjPos;
-
-    unsigned int nextObjPos, nextTargetObjPos;
-    std::vector<aiVectorKey> defaultObjPos,defaultTargetObjPos;
-};
-
-// ---------------------------------------------------------------------------
-/** Helper class for the 3DS and ASE loaders to compute camera and spot light
- *  animations.
- *
- * 3DS and ASE store the differently to Assimp - there is an animation
- * channel for the camera/spot light itself and a separate position
- * animation channels specifying the position of the camera/spot light
- * look-at target */
-class TargetAnimationHelper
-{
-public:
-
-    TargetAnimationHelper()
-        :   targetPositions     (NULL)
-        ,   objectPositions     (NULL)
-    {}
-
-
-    // ------------------------------------------------------------------
-    /** Sets the target animation channel
-     *
-     *  This channel specifies the position of the camera/spot light
-     *  target at a specific position.
-     *
-     *  @param targetPositions Translation channel*/
-    void SetTargetAnimationChannel (const
-        std::vector<aiVectorKey>* targetPositions);
-
-
-    // ------------------------------------------------------------------
-    /** Sets the main animation channel
-     *
-     *  @param objectPositions Translation channel */
-    void SetMainAnimationChannel ( const
-        std::vector<aiVectorKey>* objectPositions);
-
-    // ------------------------------------------------------------------
-    /** Sets the main animation channel to a fixed value
-     *
-     *  @param fixed Fixed value for the main animation channel*/
-    void SetFixedMainAnimationChannel(const aiVector3D& fixed);
-
-
-    // ------------------------------------------------------------------
-    /** Computes final animation channels
-     * @param distanceTrack Receive camera translation keys ... != NULL. */
-    void Process( std::vector<aiVectorKey>* distanceTrack );
-
-
-private:
-
-    const std::vector<aiVectorKey>* targetPositions,*objectPositions;
-    aiVector3D fixedMain;
-};
-
-
-} // ! end namespace Assimp
-
-#endif // include guard

+ 0 - 181
thirdparty/assimp/code/Common/Version.cpp

@@ -1,181 +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.
----------------------------------------------------------------------------
-*/
-
-// Actually just a dummy, used by the compiler to build the precompiled header.
-
-#include <assimp/version.h>
-#include <assimp/scene.h>
-#include "ScenePrivate.h"
-
-#include "revision.h"
-
-// --------------------------------------------------------------------------------
-// Legal information string - don't remove this.
-static const char* LEGAL_INFORMATION =
-
-"Open Asset Import Library (Assimp).\n"
-"A free C/C++ library to import various 3D file formats into applications\n\n"
-
-"(c) 2006-2019, assimp team\n"
-"License under the terms and conditions of the 3-clause BSD license\n"
-"http://assimp.org\n"
-;
-
-// ------------------------------------------------------------------------------------------------
-// Get legal string
-ASSIMP_API const char*  aiGetLegalString  ()    {
-    return LEGAL_INFORMATION;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get Assimp minor version
-ASSIMP_API unsigned int aiGetVersionMinor ()    {
-    return VER_MINOR;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get Assimp major version
-ASSIMP_API unsigned int aiGetVersionMajor ()    {
-    return VER_MAJOR;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Get flags used for compilation
-ASSIMP_API unsigned int aiGetCompileFlags ()    {
-
-    unsigned int flags = 0;
-
-#ifdef ASSIMP_BUILD_BOOST_WORKAROUND
-    flags |= ASSIMP_CFLAGS_NOBOOST;
-#endif
-#ifdef ASSIMP_BUILD_SINGLETHREADED
-    flags |= ASSIMP_CFLAGS_SINGLETHREADED;
-#endif
-#ifdef ASSIMP_BUILD_DEBUG
-    flags |= ASSIMP_CFLAGS_DEBUG;
-#endif
-#ifdef ASSIMP_BUILD_DLL_EXPORT
-    flags |= ASSIMP_CFLAGS_SHARED;
-#endif
-#ifdef _STLPORT_VERSION
-    flags |= ASSIMP_CFLAGS_STLPORT;
-#endif
-
-    return flags;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API unsigned int aiGetVersionRevision() {
-    return GitVersion;
-}
-
-ASSIMP_API const char *aiGetBranchName() {
-    return GitBranch;
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API aiScene::aiScene()
-: mFlags(0)
-, mRootNode(nullptr)
-, mNumMeshes(0)
-, mMeshes(nullptr)
-, mNumMaterials(0)
-, mMaterials(nullptr)
-, mNumAnimations(0)
-, mAnimations(nullptr)
-, mNumTextures(0)
-, mTextures(nullptr)
-, mNumLights(0)
-, mLights(nullptr)
-, mNumCameras(0)
-, mCameras(nullptr)
-, mMetaData(nullptr)
-, mPrivate(new Assimp::ScenePrivateData()) {
-	// empty
-}
-
-// ------------------------------------------------------------------------------------------------
-ASSIMP_API aiScene::~aiScene() {
-    // delete all sub-objects recursively
-    delete mRootNode;
-
-    // To make sure we won't crash if the data is invalid it's
-    // much better to check whether both mNumXXX and mXXX are
-    // valid instead of relying on just one of them.
-    if (mNumMeshes && mMeshes)
-        for( unsigned int a = 0; a < mNumMeshes; a++)
-            delete mMeshes[a];
-    delete [] mMeshes;
-
-    if (mNumMaterials && mMaterials) {
-        for (unsigned int a = 0; a < mNumMaterials; ++a ) {
-            delete mMaterials[ a ];
-        }
-    }
-    delete [] mMaterials;
-
-    if (mNumAnimations && mAnimations)
-        for( unsigned int a = 0; a < mNumAnimations; a++)
-            delete mAnimations[a];
-    delete [] mAnimations;
-
-    if (mNumTextures && mTextures)
-        for( unsigned int a = 0; a < mNumTextures; a++)
-            delete mTextures[a];
-    delete [] mTextures;
-
-    if (mNumLights && mLights)
-        for( unsigned int a = 0; a < mNumLights; a++)
-            delete mLights[a];
-    delete [] mLights;
-
-    if (mNumCameras && mCameras)
-        for( unsigned int a = 0; a < mNumCameras; a++)
-            delete mCameras[a];
-    delete [] mCameras;
-
-    aiMetadata::Dealloc(mMetaData);
-    mMetaData = nullptr;
-
-    delete static_cast<Assimp::ScenePrivateData*>( mPrivate );
-}
-

+ 0 - 134
thirdparty/assimp/code/Common/VertexTriangleAdjacency.cpp

@@ -1,134 +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 Implementation of the VertexTriangleAdjacency helper class
- */
-
-// internal headers
-#include "VertexTriangleAdjacency.h"
-#include <assimp/mesh.h>
-
-using namespace Assimp;
-
-// ------------------------------------------------------------------------------------------------
-VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces,
-    unsigned int iNumFaces,
-    unsigned int iNumVertices /*= 0*/,
-    bool bComputeNumTriangles /*= false*/)
-{
-    // compute the number of referenced vertices if it wasn't specified by the caller
-    const aiFace* const pcFaceEnd = pcFaces + iNumFaces;
-    if (!iNumVertices)  {
-        for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace)   {
-            ai_assert( nullptr != pcFace );
-            ai_assert(3 == pcFace->mNumIndices);
-            iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]);
-            iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]);
-            iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]);
-        }
-    }
-
-    mNumVertices = iNumVertices;
-
-    unsigned int* pi;
-
-    // allocate storage
-    if (bComputeNumTriangles)   {
-        pi = mLiveTriangles = new unsigned int[iNumVertices+1];
-        ::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1));
-        mOffsetTable = new unsigned int[iNumVertices+2]+1;
-    } else {
-        pi = mOffsetTable = new unsigned int[iNumVertices+2]+1;
-        ::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1));
-        mLiveTriangles = NULL; // important, otherwise the d'tor would crash
-    }
-
-    // get a pointer to the end of the buffer
-    unsigned int* piEnd = pi+iNumVertices;
-    *piEnd++ = 0u;
-
-    // first pass: compute the number of faces referencing each vertex
-    for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace)
-    {
-        unsigned nind = pcFace->mNumIndices;
-        unsigned * ind = pcFace->mIndices;
-        if (nind > 0) pi[ind[0]]++;
-        if (nind > 1) pi[ind[1]]++;
-        if (nind > 2) pi[ind[2]]++;
-    }
-
-    // second pass: compute the final offset table
-    unsigned int iSum = 0;
-    unsigned int* piCurOut = this->mOffsetTable;
-    for (unsigned int* piCur = pi; piCur != piEnd;++piCur,++piCurOut)   {
-
-        unsigned int iLastSum = iSum;
-        iSum += *piCur;
-        *piCurOut = iLastSum;
-    }
-    pi = this->mOffsetTable;
-
-    // third pass: compute the final table
-    this->mAdjacencyTable = new unsigned int[iSum];
-    iSum = 0;
-    for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace,++iSum)    {
-        unsigned nind = pcFace->mNumIndices;
-        unsigned * ind = pcFace->mIndices;
-
-        if (nind > 0) mAdjacencyTable[pi[ind[0]]++] = iSum;
-        if (nind > 1) mAdjacencyTable[pi[ind[1]]++] = iSum;
-        if (nind > 2) mAdjacencyTable[pi[ind[2]]++] = iSum;
-    }
-    // fourth pass: undo the offset computations made during the third pass
-    // We could do this in a separate buffer, but this would be TIMES slower.
-    --mOffsetTable;
-    *mOffsetTable = 0u;
-}
-// ------------------------------------------------------------------------------------------------
-VertexTriangleAdjacency::~VertexTriangleAdjacency()
-{
-    // delete allocated storage
-    delete[] mOffsetTable;
-    delete[] mAdjacencyTable;
-    delete[] mLiveTriangles;
-}

+ 0 - 117
thirdparty/assimp/code/Common/VertexTriangleAdjacency.h

@@ -1,117 +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 a helper class to compute a vertex-triangle adjacency map */
-#ifndef AI_VTADJACENCY_H_INC
-#define AI_VTADJACENCY_H_INC
-
-#include "BaseProcess.h"
-#include <assimp/types.h>
-#include <assimp/ai_assert.h>
-
-struct aiMesh;
-struct aiFace;
-
-namespace Assimp    {
-
-// --------------------------------------------------------------------------------------------
-/** @brief The VertexTriangleAdjacency class computes a vertex-triangle
- *  adjacency map from a given index buffer.
- *
- *  @note Although it is called #VertexTriangleAdjacency, the current version does also
- *    support arbitrary polygons. */
-// --------------------------------------------------------------------------------------------
-class ASSIMP_API VertexTriangleAdjacency {
-public:
-    // ----------------------------------------------------------------------------
-    /** @brief Construction from an existing index buffer
-     *  @param pcFaces Index buffer
-     *  @param iNumFaces Number of faces in the buffer
-     *  @param iNumVertices Number of referenced vertices. This value
-     *    is computed automatically if 0 is specified.
-     *  @param bComputeNumTriangles If you want the class to compute
-     *    a list containing the number of referenced triangles per vertex
-     *    per vertex - pass true.  */
-    VertexTriangleAdjacency(aiFace* pcFaces,unsigned int iNumFaces,
-        unsigned int iNumVertices = 0,
-        bool bComputeNumTriangles = true);
-
-    // ----------------------------------------------------------------------------
-    /** @brief Destructor */
-    ~VertexTriangleAdjacency();
-
-    // ----------------------------------------------------------------------------
-    /** @brief Get all triangles adjacent to a vertex
-     *  @param iVertIndex Index of the vertex
-     *  @return A pointer to the adjacency list. */
-    unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const {
-        ai_assert(iVertIndex < mNumVertices);
-        return &mAdjacencyTable[ mOffsetTable[iVertIndex]];
-    }
-
-    // ----------------------------------------------------------------------------
-    /** @brief Get the number of triangles that are referenced by
-     *    a vertex. This function returns a reference that can be modified
-     *  @param iVertIndex Index of the vertex
-     *  @return Number of referenced triangles */
-    unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) {
-        ai_assert( iVertIndex < mNumVertices );
-        ai_assert( nullptr != mLiveTriangles );
-        return mLiveTriangles[iVertIndex];
-    }
-
-    //! Offset table
-    unsigned int* mOffsetTable;
-
-    //! Adjacency table
-    unsigned int* mAdjacencyTable;
-
-    //! Table containing the number of referenced triangles per vertex
-    unsigned int* mLiveTriangles;
-
-    //! Debug: Number of referenced vertices
-    unsigned int mNumVertices;
-};
-
-} //! ns Assimp
-
-#endif // !! AI_VTADJACENCY_H_INC

+ 0 - 95
thirdparty/assimp/code/Common/Win32DebugLogStream.h

@@ -1,95 +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  Win32DebugLogStream.h
-*  @brief Implementation of Win32DebugLogStream
-*/
-#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC
-#define AI_WIN32DEBUGLOGSTREAM_H_INC
-
-#ifdef _WIN32
-
-#include <assimp/LogStream.hpp>
-#include "windows.h"
-
-namespace Assimp    {
-
-// ---------------------------------------------------------------------------
-/** @class  Win32DebugLogStream
- *  @brief  Logs into the debug stream from win32.
- */
-class Win32DebugLogStream : public LogStream {
-public:
-    /** @brief  Default constructor */
-    Win32DebugLogStream();
-
-    /** @brief  Destructor  */
-    ~Win32DebugLogStream();
-
-    /** @brief  Writer  */
-    void write(const char* messgae);
-};
-
-// ---------------------------------------------------------------------------
-inline 
-Win32DebugLogStream::Win32DebugLogStream(){ 
-    // empty
-}
-
-// ---------------------------------------------------------------------------
-inline 
-Win32DebugLogStream::~Win32DebugLogStream(){
-    // empty
-}
-
-// ---------------------------------------------------------------------------
-inline 
-void Win32DebugLogStream::write(const char* message) {
-    ::OutputDebugStringA( message);
-}
-
-// ---------------------------------------------------------------------------
-}   // Namespace Assimp
-
-#endif // ! _WIN32
-#endif // guard

+ 0 - 196
thirdparty/assimp/code/Common/assbin_chunks.h

@@ -1,196 +0,0 @@
-#ifndef INCLUDED_ASSBIN_CHUNKS_H
-#define INCLUDED_ASSBIN_CHUNKS_H
-
-#define ASSBIN_VERSION_MAJOR 1
-#define ASSBIN_VERSION_MINOR 0
-
-/**
-@page assfile .ASS File formats
-
-@section over Overview
-Assimp provides its own interchange format, which is intended to applications which need
-to serialize 3D-models and to reload them quickly. Assimp's file formats are designed to
-be read by Assimp itself. They encode additional information needed by Assimp to optimize
-its postprocessing pipeline. If you once apply specific steps to a scene, then save it
-and reread it from an ASS format using the same post processing settings, they won't
-be executed again.
-
-The format comes in two flavours: XML and binary - both of them hold a complete dump of
-the 'aiScene' data structure returned by the APIs. The focus for the binary format
-(<tt>.assbin</tt>) is fast loading. Optional deflate compression helps reduce file size. The XML
-flavour, <tt>.assxml</tt> or simply .xml, is just a plain-to-xml conversion of aiScene.
-
-ASSBIN is Assimp's binary interchange format. assimp_cmd (<tt>&lt;root&gt;/tools/assimp_cmd</tt>) is able to
-write it and the core library provides a loader for it.
-
-@section assxml XML File format
-
-The format is pretty much self-explanatory due to its similarity to the in-memory aiScene structure.
-With few exceptions, C structures are wrapped in XML elements.
-
-The DTD for ASSXML can be found in <tt>&lt;root&gt;/doc/AssXML_Scheme.xml</tt>. Or have   look
-at the output files generated by assimp_cmd.
-
-@section assbin Binary file format
-
-The ASSBIN file format is composed of chunks to represent the hierarchical aiScene data structure.
-This makes the format extensible and allows backward-compatibility with future data structure
-versions. The <tt>&lt;root&gt;/code/assbin_chunks.h</tt> header contains some magic constants
-for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found
-in <tt>&lt;root&gt;/tools/assimp_cmd/WriteDumb.cpp</tt> (yes, the 'b' is no typo ...).
-
-@verbatim
-
--------------------------------------------------------------------------------
-1. File structure:
--------------------------------------------------------------------------------
-
-----------------------
-| Header (512 bytes) |
-----------------------
-| Variable chunks    |
-----------------------
-
--------------------------------------------------------------------------------
-2. Definitions:
--------------------------------------------------------------------------------
-
-integer is four bytes wide, stored in little-endian byte order.
-short   is two bytes wide, stored in little-endian byte order.
-byte    is a single byte.
-string  is an integer n followed by n UTF-8 characters, not terminated by zero
-float   is an IEEE 754 single-precision floating-point value
-double  is an IEEE 754 double-precision floating-point value
-t[n]    is an array of n elements of type t
-
--------------------------------------------------------------------------------
-2. Header:
--------------------------------------------------------------------------------
-
-byte[44]    Magic identification string for ASSBIN files.
-                'ASSIMP.binary'
-
-integer     Major version of the Assimp library which wrote the file
-integer     Minor version of the Assimp library which wrote the file
-                match these against ASSBIN_VERSION_MAJOR and ASSBIN_VERSION_MINOR
-
-integer     SVN revision of the Assimp library (intended for our internal
-            debugging - if you write Ass files from your own APPs, set this value to 0.
-integer     Assimp compile flags
-
-short       0 for normal files, 1 for shortened dumps for regression tests
-                these should have the file extension assbin.regress
-
-short       1 if the data after the header is compressed with the DEFLATE algorithm,
-            0 for uncompressed files.
-                   For compressed files, the first integer after the header is
-                   always the uncompressed data size
-
-byte[256]   Zero-terminated source file name, UTF-8
-byte[128]   Zero-terminated command line parameters passed to assimp_cmd, UTF-8
-
-byte[64]    Reserved for future use
----> Total length: 512 bytes
-
--------------------------------------------------------------------------------
-3. Chunks:
--------------------------------------------------------------------------------
-
-integer     Magic chunk ID (ASSBIN_CHUNK_XXX)
-integer     Chunk data length, in bytes
-                (unknown chunks are possible, a good reader skips over them)
-                (chunk-data-length does not include the first two integers)
-
-byte[n]     chunk-data-length bytes of data, depending on the chunk type
-
-Chunks can contain nested chunks. Nested chunks are ALWAYS at the end of the chunk,
-their size is included in chunk-data-length.
-
-The chunk layout for all ASSIMP data structures is derived from their C declarations.
-The general 'rule' to get from Assimp headers to the serialized layout is:
-
-   1. POD members (i.e. aiMesh::mPrimitiveTypes, aiMesh::mNumVertices),
-        in order of declaration.
-
-   2. Array-members (aiMesh::mFaces, aiMesh::mVertices, aiBone::mWeights),
-        in order of declaration.
-
-   2. Object array members (i.e aiMesh::mBones, aiScene::mMeshes) are stored in
-      subchunks directly following the data written in 1.) and 2.)
-
-
-    Of course, there are some exceptions to this general order:
-
-[[aiScene]]
-
-   - The root node holding the scene structure is naturally stored in
-     a ASSBIN_CHUNK_AINODE subchunk following 1.) and 2.) (which is
-     empty for aiScene).
-
-[[aiMesh]]
-
-   - mTextureCoords and mNumUVComponents are serialized as follows:
-
-   [number of used uv channels times]
-       integer mNumUVComponents[n]
-       float mTextureCoords[n][3]
-
-       -> more than AI_MAX_TEXCOORD_CHANNELS can be stored. This allows Assimp
-       builds with different settings for AI_MAX_TEXCOORD_CHANNELS to exchange
-       data.
-       -> the on-disk format always uses 3 floats to write UV coordinates.
-       If mNumUVComponents[0] is 1, the corresponding mTextureCoords array
-       consists of 3 floats.
-
-   - The array member block of aiMesh is prefixed with an integer that specifies
-     the kinds of vertex components actually present in the mesh. This is a
-     bitwise combination of the ASSBIN_MESH_HAS_xxx constants.
-
-[[aiFace]]
-
-   - mNumIndices is stored as short
-   - mIndices are written as short, if aiMesh::mNumVertices<65536
-
-[[aiNode]]
-
-   - mParent is omitted
-
-[[aiLight]]
-
-   - mAttenuationXXX not written if aiLight::mType == aiLightSource_DIRECTIONAL
-   - mAngleXXX not written if aiLight::mType != aiLightSource_SPOT
-
-[[aiMaterial]]
-
-   - mNumAllocated is omitted, for obvious reasons :-)
-
-
- @endverbatim*/
-
-
-#define ASSBIN_HEADER_LENGTH 512
-
-// these are the magic chunk identifiers for the binary ASS file format
-#define ASSBIN_CHUNK_AICAMERA                   0x1234
-#define ASSBIN_CHUNK_AILIGHT                    0x1235
-#define ASSBIN_CHUNK_AITEXTURE                  0x1236
-#define ASSBIN_CHUNK_AIMESH                     0x1237
-#define ASSBIN_CHUNK_AINODEANIM                 0x1238
-#define ASSBIN_CHUNK_AISCENE                    0x1239
-#define ASSBIN_CHUNK_AIBONE                     0x123a
-#define ASSBIN_CHUNK_AIANIMATION                0x123b
-#define ASSBIN_CHUNK_AINODE                     0x123c
-#define ASSBIN_CHUNK_AIMATERIAL                 0x123d
-#define ASSBIN_CHUNK_AIMATERIALPROPERTY         0x123e
-
-#define ASSBIN_MESH_HAS_POSITIONS                   0x1
-#define ASSBIN_MESH_HAS_NORMALS                     0x2
-#define ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS     0x4
-#define ASSBIN_MESH_HAS_TEXCOORD_BASE               0x100
-#define ASSBIN_MESH_HAS_COLOR_BASE                  0x10000
-
-#define ASSBIN_MESH_HAS_TEXCOORD(n) (ASSBIN_MESH_HAS_TEXCOORD_BASE << n)
-#define ASSBIN_MESH_HAS_COLOR(n)    (ASSBIN_MESH_HAS_COLOR_BASE << n)
-
-
-#endif // INCLUDED_ASSBIN_CHUNKS_H

+ 0 - 140
thirdparty/assimp/code/Common/scene.cpp

@@ -1,140 +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.
----------------------------------------------------------------------------
-*/
-#include <assimp/scene.h>
-
-aiNode::aiNode()
-: mName("")
-, mParent(nullptr)
-, mNumChildren(0)
-, mChildren(nullptr)
-, mNumMeshes(0)
-, mMeshes(nullptr)
-, mMetaData(nullptr) {
-    // empty
-}
-
-aiNode::aiNode(const std::string& name)
-: mName(name)
-, mParent(nullptr)
-, mNumChildren(0)
-, mChildren(nullptr)
-, mNumMeshes(0)
-, mMeshes(nullptr)
-, mMetaData(nullptr) {
-    // empty
-}
-
-/** Destructor */
-aiNode::~aiNode() {
-    // delete all children recursively
-    // to make sure we won't crash if the data is invalid ...
-    if (mNumChildren && mChildren)
-    {
-        for (unsigned int a = 0; a < mNumChildren; a++)
-            delete mChildren[a];
-    }
-    delete[] mChildren;
-    delete[] mMeshes;
-    delete mMetaData;
-}
-
-const aiNode *aiNode::FindNode(const char* name) const {
-    if (nullptr == name) {
-        return nullptr;
-    }
-    if (!::strcmp(mName.data, name)) {
-        return this;
-    }
-    for (unsigned int i = 0; i < mNumChildren; ++i) {
-        const aiNode* const p = mChildren[i]->FindNode(name);
-        if (p) {
-            return p;
-        }
-    }
-    // there is definitely no sub-node with this name
-    return nullptr;
-}
-
-aiNode *aiNode::FindNode(const char* name) {
-    if (!::strcmp(mName.data, name))return this;
-    for (unsigned int i = 0; i < mNumChildren; ++i)
-    {
-        aiNode* const p = mChildren[i]->FindNode(name);
-        if (p) {
-            return p;
-        }
-    }
-    // there is definitely no sub-node with this name
-    return nullptr;
-}
-
-void aiNode::addChildren(unsigned int numChildren, aiNode **children) {
-    if (nullptr == children || 0 == numChildren) {
-        return;
-    }
-
-    for (unsigned int i = 0; i < numChildren; i++) {
-        aiNode *child = children[i];
-        if (nullptr != child) {
-            child->mParent = this;
-        }
-    }
-
-    if (mNumChildren > 0) {
-        aiNode **tmp = new aiNode*[mNumChildren];
-        ::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren);
-        delete[] mChildren;
-        mChildren = new aiNode*[mNumChildren + numChildren];
-        ::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren);
-        ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren);
-        mNumChildren += numChildren;
-        delete[] tmp;
-    }
-    else {
-        mChildren = new aiNode*[numChildren];
-        for (unsigned int i = 0; i < numChildren; i++) {
-            mChildren[i] = children[i];
-        }
-        mNumChildren = numChildren;
-    }
-}

+ 0 - 79
thirdparty/assimp/code/Common/simd.cpp

@@ -1,79 +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.
----------------------------------------------------------------------------
-*/
-#include "simd.h"
-
-namespace Assimp {
-
-bool CPUSupportsSSE2() {
-#if defined(__x86_64__) || defined(_M_X64)
-    //* x86_64 always has SSE2 instructions */
-    return true;
-#elif defined(__GNUC__) && defined(i386)
-    // for GCC x86 we check cpuid
-    unsigned int d;
-    __asm__(
-        "pushl %%ebx\n\t"
-        "cpuid\n\t"
-        "popl %%ebx\n\t"
-        : "=d" ( d )
-        :"a" ( 1 ) );
-    return ( d & 0x04000000 ) != 0;
-#elif (defined(_MSC_VER) && defined(_M_IX86))
-    // also check cpuid for MSVC x86
-    unsigned int d;
-    __asm {
-        xor     eax, eax
-        inc eax
-        push ebx
-        cpuid
-        pop ebx
-        mov d, edx
-    }
-    return ( d & 0x04000000 ) != 0;
-#else
-    return false;
-#endif
-}
-
-
-} // Namespace Assimp

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