summaryrefslogtreecommitdiff
path: root/thirdparty/assimp
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/assimp')
-rw-r--r--thirdparty/assimp/CREDITS183
-rw-r--r--thirdparty/assimp/LICENSE78
-rw-r--r--thirdparty/assimp/assimp/config.h980
-rw-r--r--thirdparty/assimp/code/BaseImporter.cpp616
-rw-r--r--thirdparty/assimp/code/BaseProcess.cpp107
-rw-r--r--thirdparty/assimp/code/BaseProcess.h290
-rw-r--r--thirdparty/assimp/code/Bitmap.cpp155
-rw-r--r--thirdparty/assimp/code/CInterfaceIOWrapper.cpp136
-rw-r--r--thirdparty/assimp/code/CInterfaceIOWrapper.h99
-rw-r--r--thirdparty/assimp/code/CalcTangentsProcess.cpp319
-rw-r--r--thirdparty/assimp/code/CalcTangentsProcess.h117
-rw-r--r--thirdparty/assimp/code/ComputeUVMappingProcess.cpp506
-rw-r--r--thirdparty/assimp/code/ComputeUVMappingProcess.h148
-rw-r--r--thirdparty/assimp/code/ConvertToLHProcess.cpp414
-rw-r--r--thirdparty/assimp/code/ConvertToLHProcess.h170
-rw-r--r--thirdparty/assimp/code/CreateAnimMesh.cpp92
-rw-r--r--thirdparty/assimp/code/DeboneProcess.cpp465
-rw-r--r--thirdparty/assimp/code/DeboneProcess.h134
-rw-r--r--thirdparty/assimp/code/DefaultIOStream.cpp154
-rw-r--r--thirdparty/assimp/code/DefaultIOSystem.cpp257
-rw-r--r--thirdparty/assimp/code/DefaultLogger.cpp418
-rw-r--r--thirdparty/assimp/code/DefaultProgressHandler.h65
-rw-r--r--thirdparty/assimp/code/DropFaceNormalsProcess.cpp109
-rw-r--r--thirdparty/assimp/code/DropFaceNormalsProcess.h86
-rw-r--r--thirdparty/assimp/code/EmbedTexturesProcess.cpp152
-rw-r--r--thirdparty/assimp/code/EmbedTexturesProcess.h85
-rw-r--r--thirdparty/assimp/code/Exporter.cpp648
-rw-r--r--thirdparty/assimp/code/FBXAnimation.cpp305
-rw-r--r--thirdparty/assimp/code/FBXBinaryTokenizer.cpp466
-rw-r--r--thirdparty/assimp/code/FBXCommon.h86
-rw-r--r--thirdparty/assimp/code/FBXCompileConfig.h70
-rw-r--r--thirdparty/assimp/code/FBXConverter.cpp3536
-rw-r--r--thirdparty/assimp/code/FBXConverter.h461
-rw-r--r--thirdparty/assimp/code/FBXDeformer.cpp213
-rw-r--r--thirdparty/assimp/code/FBXDocument.cpp726
-rw-r--r--thirdparty/assimp/code/FBXDocument.h1180
-rw-r--r--thirdparty/assimp/code/FBXDocumentUtil.cpp135
-rw-r--r--thirdparty/assimp/code/FBXDocumentUtil.h120
-rw-r--r--thirdparty/assimp/code/FBXExportNode.cpp568
-rw-r--r--thirdparty/assimp/code/FBXExportNode.h271
-rw-r--r--thirdparty/assimp/code/FBXExportProperty.cpp364
-rw-r--r--thirdparty/assimp/code/FBXExportProperty.h129
-rw-r--r--thirdparty/assimp/code/FBXExporter.cpp2480
-rw-r--r--thirdparty/assimp/code/FBXExporter.h178
-rw-r--r--thirdparty/assimp/code/FBXImportSettings.h153
-rw-r--r--thirdparty/assimp/code/FBXImporter.cpp197
-rw-r--r--thirdparty/assimp/code/FBXImporter.h100
-rw-r--r--thirdparty/assimp/code/FBXMaterial.cpp381
-rw-r--r--thirdparty/assimp/code/FBXMeshGeometry.cpp711
-rw-r--r--thirdparty/assimp/code/FBXMeshGeometry.h235
-rw-r--r--thirdparty/assimp/code/FBXModel.cpp153
-rw-r--r--thirdparty/assimp/code/FBXNodeAttribute.cpp170
-rw-r--r--thirdparty/assimp/code/FBXParser.cpp1313
-rw-r--r--thirdparty/assimp/code/FBXParser.h235
-rw-r--r--thirdparty/assimp/code/FBXProperties.cpp235
-rw-r--r--thirdparty/assimp/code/FBXProperties.h185
-rw-r--r--thirdparty/assimp/code/FBXTokenizer.cpp248
-rw-r--r--thirdparty/assimp/code/FBXTokenizer.h187
-rw-r--r--thirdparty/assimp/code/FBXUtil.cpp164
-rw-r--r--thirdparty/assimp/code/FBXUtil.h120
-rw-r--r--thirdparty/assimp/code/FIReader.cpp1834
-rw-r--r--thirdparty/assimp/code/FileLogStream.h107
-rw-r--r--thirdparty/assimp/code/FileSystemFilter.h345
-rw-r--r--thirdparty/assimp/code/FindDegenerates.cpp300
-rw-r--r--thirdparty/assimp/code/FindDegenerates.h129
-rw-r--r--thirdparty/assimp/code/FindInstancesProcess.cpp277
-rw-r--r--thirdparty/assimp/code/FindInstancesProcess.h137
-rw-r--r--thirdparty/assimp/code/FindInvalidDataProcess.cpp424
-rw-r--r--thirdparty/assimp/code/FindInvalidDataProcess.h105
-rw-r--r--thirdparty/assimp/code/FixNormalsStep.cpp184
-rw-r--r--thirdparty/assimp/code/FixNormalsStep.h91
-rw-r--r--thirdparty/assimp/code/GenFaceNormalsProcess.cpp146
-rw-r--r--thirdparty/assimp/code/GenFaceNormalsProcess.h87
-rw-r--r--thirdparty/assimp/code/GenVertexNormalsProcess.cpp239
-rw-r--r--thirdparty/assimp/code/GenVertexNormalsProcess.h115
-rw-r--r--thirdparty/assimp/code/Importer.cpp1171
-rw-r--r--thirdparty/assimp/code/Importer.h247
-rw-r--r--thirdparty/assimp/code/ImporterRegistry.cpp371
-rw-r--r--thirdparty/assimp/code/ImproveCacheLocality.cpp386
-rw-r--r--thirdparty/assimp/code/ImproveCacheLocality.h100
-rw-r--r--thirdparty/assimp/code/JoinVerticesProcess.cpp463
-rw-r--r--thirdparty/assimp/code/JoinVerticesProcess.h99
-rw-r--r--thirdparty/assimp/code/LimitBoneWeightsProcess.cpp201
-rw-r--r--thirdparty/assimp/code/LimitBoneWeightsProcess.h148
-rw-r--r--thirdparty/assimp/code/MMDCpp14.h83
-rw-r--r--thirdparty/assimp/code/MMDImporter.cpp370
-rw-r--r--thirdparty/assimp/code/MMDImporter.h96
-rw-r--r--thirdparty/assimp/code/MMDPmdParser.h597
-rw-r--r--thirdparty/assimp/code/MMDPmxParser.cpp604
-rw-r--r--thirdparty/assimp/code/MMDPmxParser.h782
-rw-r--r--thirdparty/assimp/code/MMDVmdParser.h376
-rw-r--r--thirdparty/assimp/code/MakeVerboseFormat.cpp226
-rw-r--r--thirdparty/assimp/code/MakeVerboseFormat.h105
-rw-r--r--thirdparty/assimp/code/MaterialSystem.cpp647
-rw-r--r--thirdparty/assimp/code/MaterialSystem.h72
-rw-r--r--thirdparty/assimp/code/OptimizeGraph.cpp353
-rw-r--r--thirdparty/assimp/code/OptimizeGraph.h145
-rw-r--r--thirdparty/assimp/code/OptimizeMeshes.cpp256
-rw-r--r--thirdparty/assimp/code/OptimizeMeshes.h186
-rw-r--r--thirdparty/assimp/code/PolyTools.h229
-rw-r--r--thirdparty/assimp/code/PostStepRegistry.cpp251
-rw-r--r--thirdparty/assimp/code/PretransformVertices.cpp728
-rw-r--r--thirdparty/assimp/code/PretransformVertices.h163
-rw-r--r--thirdparty/assimp/code/ProcessHelper.cpp443
-rw-r--r--thirdparty/assimp/code/ProcessHelper.h386
-rw-r--r--thirdparty/assimp/code/RawLoader.cpp331
-rw-r--r--thirdparty/assimp/code/RemoveComments.cpp113
-rw-r--r--thirdparty/assimp/code/RemoveRedundantMaterials.cpp221
-rw-r--r--thirdparty/assimp/code/RemoveRedundantMaterials.h107
-rw-r--r--thirdparty/assimp/code/RemoveVCProcess.cpp337
-rw-r--r--thirdparty/assimp/code/RemoveVCProcess.h123
-rw-r--r--thirdparty/assimp/code/SGSpatialSort.cpp168
-rw-r--r--thirdparty/assimp/code/ScaleProcess.cpp103
-rw-r--r--thirdparty/assimp/code/ScaleProcess.h88
-rw-r--r--thirdparty/assimp/code/SceneCombiner.cpp1300
-rw-r--r--thirdparty/assimp/code/ScenePreprocessor.cpp261
-rw-r--r--thirdparty/assimp/code/ScenePreprocessor.h125
-rw-r--r--thirdparty/assimp/code/ScenePrivate.h105
-rw-r--r--thirdparty/assimp/code/SkeletonMeshBuilder.cpp270
-rw-r--r--thirdparty/assimp/code/SortByPTypeProcess.cpp403
-rw-r--r--thirdparty/assimp/code/SortByPTypeProcess.h85
-rw-r--r--thirdparty/assimp/code/SpatialSort.cpp342
-rw-r--r--thirdparty/assimp/code/SplitByBoneCountProcess.cpp407
-rw-r--r--thirdparty/assimp/code/SplitByBoneCountProcess.h111
-rw-r--r--thirdparty/assimp/code/SplitLargeMeshes.cpp623
-rw-r--r--thirdparty/assimp/code/SplitLargeMeshes.h210
-rw-r--r--thirdparty/assimp/code/StandardShapes.cpp507
-rw-r--r--thirdparty/assimp/code/StdOStreamLogStream.h101
-rw-r--r--thirdparty/assimp/code/Subdivision.cpp588
-rw-r--r--thirdparty/assimp/code/TargetAnimation.cpp248
-rw-r--r--thirdparty/assimp/code/TargetAnimation.h183
-rw-r--r--thirdparty/assimp/code/TextureTransform.cpp566
-rw-r--r--thirdparty/assimp/code/TextureTransform.h232
-rw-r--r--thirdparty/assimp/code/TriangulateProcess.cpp530
-rw-r--r--thirdparty/assimp/code/TriangulateProcess.h95
-rw-r--r--thirdparty/assimp/code/ValidateDataStructure.cpp986
-rw-r--r--thirdparty/assimp/code/ValidateDataStructure.h188
-rw-r--r--thirdparty/assimp/code/Version.cpp185
-rw-r--r--thirdparty/assimp/code/VertexTriangleAdjacency.cpp134
-rw-r--r--thirdparty/assimp/code/VertexTriangleAdjacency.h117
-rw-r--r--thirdparty/assimp/code/Win32DebugLogStream.h95
-rw-r--r--thirdparty/assimp/code/revision.h7
-rw-r--r--thirdparty/assimp/code/scene.cpp140
-rw-r--r--thirdparty/assimp/code/simd.cpp79
-rw-r--r--thirdparty/assimp/code/simd.h53
-rw-r--r--thirdparty/assimp/contrib/utf8cpp/source/utf8.h34
-rw-r--r--thirdparty/assimp/contrib/utf8cpp/source/utf8/checked.h327
-rw-r--r--thirdparty/assimp/contrib/utf8cpp/source/utf8/core.h329
-rw-r--r--thirdparty/assimp/contrib/utf8cpp/source/utf8/unchecked.h228
-rw-r--r--thirdparty/assimp/include/assimp/.editorconfig8
-rw-r--r--thirdparty/assimp/include/assimp/BaseImporter.h361
-rw-r--r--thirdparty/assimp/include/assimp/Bitmap.h125
-rw-r--r--thirdparty/assimp/include/assimp/BlobIOSystem.h338
-rw-r--r--thirdparty/assimp/include/assimp/ByteSwapper.h287
-rw-r--r--thirdparty/assimp/include/assimp/Compiler/poppack1.h22
-rw-r--r--thirdparty/assimp/include/assimp/Compiler/pstdint.h912
-rw-r--r--thirdparty/assimp/include/assimp/Compiler/pushpack1.h43
-rw-r--r--thirdparty/assimp/include/assimp/CreateAnimMesh.h58
-rw-r--r--thirdparty/assimp/include/assimp/DefaultIOStream.h140
-rw-r--r--thirdparty/assimp/include/assimp/DefaultIOSystem.h93
-rw-r--r--thirdparty/assimp/include/assimp/DefaultLogger.hpp188
-rw-r--r--thirdparty/assimp/include/assimp/Defines.h49
-rw-r--r--thirdparty/assimp/include/assimp/Exceptional.h125
-rw-r--r--thirdparty/assimp/include/assimp/Exporter.hpp505
-rw-r--r--thirdparty/assimp/include/assimp/GenericProperty.h133
-rw-r--r--thirdparty/assimp/include/assimp/Hash.h118
-rw-r--r--thirdparty/assimp/include/assimp/IOStream.hpp142
-rw-r--r--thirdparty/assimp/include/assimp/IOStreamBuffer.h355
-rw-r--r--thirdparty/assimp/include/assimp/IOSystem.hpp357
-rw-r--r--thirdparty/assimp/include/assimp/Importer.hpp659
-rw-r--r--thirdparty/assimp/include/assimp/LineSplitter.h285
-rw-r--r--thirdparty/assimp/include/assimp/LogAux.h131
-rw-r--r--thirdparty/assimp/include/assimp/LogStream.hpp111
-rw-r--r--thirdparty/assimp/include/assimp/Logger.hpp305
-rw-r--r--thirdparty/assimp/include/assimp/Macros.h49
-rw-r--r--thirdparty/assimp/include/assimp/MathFunctions.h77
-rw-r--r--thirdparty/assimp/include/assimp/MemoryIOWrapper.h244
-rw-r--r--thirdparty/assimp/include/assimp/NullLogger.hpp99
-rw-r--r--thirdparty/assimp/include/assimp/ParsingUtils.h260
-rw-r--r--thirdparty/assimp/include/assimp/Profiler.h99
-rw-r--r--thirdparty/assimp/include/assimp/ProgressHandler.hpp145
-rw-r--r--thirdparty/assimp/include/assimp/RemoveComments.h91
-rw-r--r--thirdparty/assimp/include/assimp/SGSpatialSort.h150
-rw-r--r--thirdparty/assimp/include/assimp/SceneCombiner.h401
-rw-r--r--thirdparty/assimp/include/assimp/SkeletonMeshBuilder.h125
-rw-r--r--thirdparty/assimp/include/assimp/SmoothingGroups.h108
-rw-r--r--thirdparty/assimp/include/assimp/SmoothingGroups.inl138
-rw-r--r--thirdparty/assimp/include/assimp/SpatialSort.h174
-rw-r--r--thirdparty/assimp/include/assimp/StandardShapes.h200
-rw-r--r--thirdparty/assimp/include/assimp/StreamReader.h343
-rw-r--r--thirdparty/assimp/include/assimp/StreamWriter.h303
-rw-r--r--thirdparty/assimp/include/assimp/StringComparison.h233
-rw-r--r--thirdparty/assimp/include/assimp/StringUtils.h143
-rw-r--r--thirdparty/assimp/include/assimp/Subdivision.h131
-rw-r--r--thirdparty/assimp/include/assimp/TinyFormatter.h166
-rw-r--r--thirdparty/assimp/include/assimp/Vertex.h348
-rw-r--r--thirdparty/assimp/include/assimp/XMLTools.h83
-rw-r--r--thirdparty/assimp/include/assimp/ai_assert.h57
-rw-r--r--thirdparty/assimp/include/assimp/anim.h577
-rw-r--r--thirdparty/assimp/include/assimp/camera.h226
-rw-r--r--thirdparty/assimp/include/assimp/cexport.h261
-rw-r--r--thirdparty/assimp/include/assimp/cfileio.h138
-rw-r--r--thirdparty/assimp/include/assimp/cimport.h565
-rw-r--r--thirdparty/assimp/include/assimp/color4.h104
-rw-r--r--thirdparty/assimp/include/assimp/color4.inl205
-rw-r--r--thirdparty/assimp/include/assimp/config.h.in992
-rw-r--r--thirdparty/assimp/include/assimp/defs.h303
-rw-r--r--thirdparty/assimp/include/assimp/fast_atof.h373
-rw-r--r--thirdparty/assimp/include/assimp/importerdesc.h146
-rw-r--r--thirdparty/assimp/include/assimp/irrXMLWrapper.h144
-rw-r--r--thirdparty/assimp/include/assimp/light.h259
-rw-r--r--thirdparty/assimp/include/assimp/material.h1580
-rw-r--r--thirdparty/assimp/include/assimp/material.inl390
-rw-r--r--thirdparty/assimp/include/assimp/matrix3x3.h183
-rw-r--r--thirdparty/assimp/include/assimp/matrix3x3.inl357
-rw-r--r--thirdparty/assimp/include/assimp/matrix4x4.h280
-rw-r--r--thirdparty/assimp/include/assimp/matrix4x4.inl686
-rw-r--r--thirdparty/assimp/include/assimp/mesh.h852
-rw-r--r--thirdparty/assimp/include/assimp/metadata.h380
-rw-r--r--thirdparty/assimp/include/assimp/pbrmaterial.h77
-rw-r--r--thirdparty/assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h92
-rw-r--r--thirdparty/assimp/include/assimp/postprocess.h679
-rw-r--r--thirdparty/assimp/include/assimp/qnan.h165
-rw-r--r--thirdparty/assimp/include/assimp/quaternion.h130
-rw-r--r--thirdparty/assimp/include/assimp/quaternion.inl286
-rw-r--r--thirdparty/assimp/include/assimp/scene.h416
-rw-r--r--thirdparty/assimp/include/assimp/texture.h227
-rw-r--r--thirdparty/assimp/include/assimp/types.h529
-rw-r--r--thirdparty/assimp/include/assimp/vector2.h107
-rw-r--r--thirdparty/assimp/include/assimp/vector2.inl244
-rw-r--r--thirdparty/assimp/include/assimp/vector3.h146
-rw-r--r--thirdparty/assimp/include/assimp/vector3.inl309
-rw-r--r--thirdparty/assimp/include/assimp/version.h115
233 files changed, 72187 insertions, 0 deletions
diff --git a/thirdparty/assimp/CREDITS b/thirdparty/assimp/CREDITS
new file mode 100644
index 0000000000..26e21d2f41
--- /dev/null
+++ b/thirdparty/assimp/CREDITS
@@ -0,0 +1,183 @@
+===============================================================
+Open Asset Import Library (Assimp)
+Developers and Contributors
+===============================================================
+
+The following is a non-exhaustive list of all constributors over the years.
+If you think your name should be listed here, drop us a line and we'll add you.
+
+- Alexander Gessler,
+3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design).
+
+- Thomas Schulze,
+X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation.
+
+- Kim Kulling,
+Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ).
+
+- R.Schmidt,
+Linux build, eclipse support.
+
+- Matthias Gubisch,
+Assimp.net
+Visual Studio 9 support, bugfixes.
+
+- Mark Sibly
+B3D-Loader, Assimp testing
+
+- Jonathan Klein
+Ogre Loader, VC2010 fixes and CMake fixes.
+
+- Sebastian Hempel,
+PyAssimp (first version)
+Compile-Bugfixes for mingw, add environment for static library support in make.
+
+- Jonathan Pokrass
+Supplied a bugfix concerning the scaling in the md3 loader.
+
+- Andrew Galante,
+Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace.
+
+- Andreas Nagel
+First Assimp testing & verification under Windows Vista 64 Bit.
+
+- Marius Schr�der
+Allowed us to use many of his models for screenshots and testing.
+
+- Christian Schubert
+Supplied various XFiles for testing purposes.
+
+- Tizian Wieland
+Searched the web for hundreds of test models for internal use
+
+- John Connors
+Supplied patches for linux and SCons.
+
+- T. R.
+The GUY who performed some of the CSM mocaps.
+
+- Andy Maloney
+Contributed fixes for the documentation and the doxygen markup
+
+- Zhao Lei
+Contributed several bugfixes fixing memory leaks and improving float parsing
+
+- sueastside
+Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
+
+- Tobias Rittig
+Collada testing with Cinema 4D
+
+- Brad Grantham
+Improvements in OpenGL-Sample.
+
+- Robert Ramirez
+Add group loading feature to Obj-Loader.
+
+- Chris Maiwald
+Many bugreports, improving Assimp's portability, regular testing & feedback.
+
+- Stepan Hrbek
+Bugreport and fix for a obj-materialloader crash.
+
+- David Nadlinger
+D bindings, CMake install support.
+
+- Dario Accornero
+Contributed several patches regarding Mac OS/XCode targets, bug reports.
+
+- Martin Walser (Samhayne)
+Contributed the 'SimpleTexturedOpenGl' sample.
+
+- Matthias Fauconneau
+Contributed a fix for the Q3-BSP loader.
+
+- Jørgen P. Tjernø
+Contributed updated and improved xcode workspaces
+
+- drparallax
+Contributed the /samples/SimpleAssimpViewX sample
+
+- Carsten Fuchs
+Contributed a fix for the Normalize method in aiQuaternion.
+
+- dbburgess
+Contributes a Android-specific build issue: log the hardware architecture for ARM.
+
+- alfiereinre7
+Contributes a obj-fileparser fix: missing tokens in the obj-token list.
+
+- Roman Kharitonov
+Contributes a fix for the configure script environment.
+
+- Ed Diana
+Contributed AssimpDelphi (/port/AssimpDelphi).
+
+- rdb
+Contributes a bundle of fixes and improvements for the bsp-importer.
+
+- Mick P
+For contributing the De-bone postprocessing step and filing various bug reports.
+
+- Rosen Diankov
+Contributed patches to build assimp debian packages using cmake.
+
+- Mark Page
+Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
+
+- IOhannes
+Contributed the Debian build fixes ( architecture macro ).
+
+- gellule
+Several LWO and LWS fixes (pivoting).
+
+- Marcel Metz
+GCC/Linux fixes for the SimpleOpenGL sample.
+
+- Brian Miller
+Bugfix for a compiler fix for iOS on arm.
+
+- Séverin Lemaignan
+Rewrite of PyAssimp, distutils and Python3 support
+
+- albert-wang
+Bugfixes for the collada parser
+
+- Ya ping Jin
+Bugfixes for uv-tanget calculation.
+
+- Jonne Nauha
+Ogre Binary format support
+
+- Filip Wasil, Tieto Poland Sp. z o.o.
+Android JNI asset extraction support
+
+- Richard Steffen
+Contributed ExportProperties interface
+Contributed X File exporter
+Contributed Step (stp) exporter
+
+- Thomas Iorns (mesilliac)
+Initial FBX Export support
+
+For a more detailed list just check: https://github.com/assimp/assimp/network/members
+
+
+========
+Patreons
+========
+
+Huge thanks to our Patreons!
+
+- migenius
+- Marcus
+- Cort
+- elect
+- Steffen
+
+
+===================
+Commercial Sponsors
+===================
+
+- MyDidimo (mydidimo.com): Sponsored development of FBX Export support
diff --git a/thirdparty/assimp/LICENSE b/thirdparty/assimp/LICENSE
new file mode 100644
index 0000000000..262606aff3
--- /dev/null
+++ b/thirdparty/assimp/LICENSE
@@ -0,0 +1,78 @@
+Open Asset Import Library (assimp)
+
+Copyright (c) 2006-2016, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+******************************************************************************
+
+AN EXCEPTION applies to all files in the ./test/models-nonbsd folder.
+These are 3d models for testing purposes, from various free sources
+on the internet. They are - unless otherwise stated - copyright of
+their respective creators, which may impose additional requirements
+on the use of their work. For any of these models, see
+<model-name>.source.txt for more legal information. Contact us if you
+are a copyright holder and believe that we credited you inproperly or
+if you don't want your files to appear in the repository.
+
+
+******************************************************************************
+
+Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+http://code.google.com/p/poly2tri/
+
+All rights reserved.
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+* Neither the name of Poly2Tri nor the names of its contributors may be
+ used to endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/thirdparty/assimp/assimp/config.h b/thirdparty/assimp/assimp/config.h
new file mode 100644
index 0000000000..8b0634d28b
--- /dev/null
+++ b/thirdparty/assimp/assimp/config.h
@@ -0,0 +1,980 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2018, 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 config.h
+ * @brief Defines constants for configurable properties for the library
+ *
+ * Typically these properties are set via
+ * #Assimp::Importer::SetPropertyFloat,
+ * #Assimp::Importer::SetPropertyInteger or
+ * #Assimp::Importer::SetPropertyString,
+ * depending on the data type of a property. All properties have a
+ * default value. See the doc for the mentioned methods for more details.
+ *
+ * <br><br>
+ * The corresponding functions for use with the plain-c API are:
+ * #aiSetImportPropertyInteger,
+ * #aiSetImportPropertyFloat,
+ * #aiSetImportPropertyString
+ */
+#pragma once
+#ifndef AI_CONFIG_H_INC
+#define AI_CONFIG_H_INC
+
+// ###########################################################################
+// LIBRARY SETTINGS
+// General, global settings
+// ###########################################################################
+
+// ---------------------------------------------------------------------------
+/** @brief Enables time measurements.
+ *
+ * If enabled, measures the time needed for each part of the loading
+ * process (i.e. IO time, importing, postprocessing, ..) and dumps
+ * these timings to the DefaultLogger. See the @link perf Performance
+ * Page@endlink for more information on this topic.
+ *
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_GLOB_MEASURE_TIME \
+ "GLOB_MEASURE_TIME"
+
+// ---------------------------------------------------------------------------
+/** @brief Global setting to disable generation of skeleton dummy meshes
+ *
+ * Skeleton dummy meshes are generated as a visualization aid in cases which
+ * the input data contains no geometry, but only animation data.
+ * Property data type: bool. Default value: false
+ */
+// ---------------------------------------------------------------------------
+#define AI_CONFIG_IMPORT_NO_SKELETON_MESHES \
+ "IMPORT_NO_SKELETON_MESHES"
+
+#if 0 // not implemented yet
+// ---------------------------------------------------------------------------
+/** @brief Set Assimp's multithreading policy.
+ *
+ * This setting is ignored if Assimp was built without boost.thread
+ * support (ASSIMP_BUILD_NO_THREADING, which is implied by ASSIMP_BUILD_BOOST_WORKAROUND).
+ * Possible values are: -1 to let Assimp decide what to do, 0 to disable
+ * multithreading entirely and any number larger than 0 to force a specific
+ * number of threads. Assimp is always free to ignore this settings, which is
+ * merely a hint. Usually, the default value (-1) will be fine. However, if
+ * Assimp is used concurrently from multiple user threads, it might be useful
+ * to limit each Importer instance to a specific number of cores.
+ *
+ * For more information, see the @link threading Threading page@endlink.
+ * Property type: int, default value: -1.
+ */
+#define AI_CONFIG_GLOB_MULTITHREADING \
+ "GLOB_MULTITHREADING"
+#endif
+
+// ###########################################################################
+// POST PROCESSING SETTINGS
+// Various stuff to fine-tune the behavior of a specific post processing step.
+// ###########################################################################
+
+// ---------------------------------------------------------------------------
+/** @brief Maximum bone count per mesh for the SplitbyBoneCount step.
+ *
+ * Meshes are split until the maximum number of bones is reached. The default
+ * value is AI_SBBC_DEFAULT_MAX_BONES, which may be altered at
+ * compile-time.
+ * Property data type: integer.
+ */
+// ---------------------------------------------------------------------------
+#define AI_CONFIG_PP_SBBC_MAX_BONES \
+ "PP_SBBC_MAX_BONES"
+
+// default limit for bone count
+#if (!defined AI_SBBC_DEFAULT_MAX_BONES)
+#define AI_SBBC_DEFAULT_MAX_BONES 60
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies the maximum angle that may be between two vertex tangents
+ * that their tangents and bi-tangents are smoothed.
+ *
+ * This applies to the CalcTangentSpace-Step. The angle is specified
+ * in degrees. The maximum value is 175.
+ * Property type: float. Default value: 45 degrees
+ */
+#define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE \
+ "PP_CT_MAX_SMOOTHING_ANGLE"
+
+// ---------------------------------------------------------------------------
+/** @brief Source UV channel for tangent space computation.
+ *
+ * The specified channel must exist or an error will be raised.
+ * Property type: integer. Default value: 0
+ */
+// ---------------------------------------------------------------------------
+#define AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX \
+ "PP_CT_TEXTURE_CHANNEL_INDEX"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies the maximum angle that may be between two face normals
+ * at the same vertex position that their are smoothed together.
+ *
+ * Sometimes referred to as 'crease angle'.
+ * This applies to the GenSmoothNormals-Step. The angle is specified
+ * in degrees, so 180 is PI. The default value is 175 degrees (all vertex
+ * normals are smoothed). The maximum value is 175, too. Property type: float.
+ * Warning: setting this option may cause a severe loss of performance. The
+ * performance is unaffected if the #AI_CONFIG_FAVOUR_SPEED flag is set but
+ * the output quality may be reduced.
+ */
+#define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE \
+ "PP_GSN_MAX_SMOOTHING_ANGLE"
+
+// ---------------------------------------------------------------------------
+/** @brief Sets the colormap (= palette) to be used to decode embedded
+ * textures in MDL (Quake or 3DGS) files.
+ *
+ * This must be a valid path to a file. The file is 768 (256*3) bytes
+ * large and contains RGB triplets for each of the 256 palette entries.
+ * The default value is colormap.lmp. If the file is not found,
+ * a default palette (from Quake 1) is used.
+ * Property type: string.
+ */
+#define AI_CONFIG_IMPORT_MDL_COLORMAP \
+ "IMPORT_MDL_COLORMAP"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_RemoveRedundantMaterials step to
+ * keep materials matching a name in a given list.
+ *
+ * This is a list of 1 to n strings, ' ' serves as delimiter character.
+ * Identifiers containing whitespaces must be enclosed in *single*
+ * quotation marks. For example:<tt>
+ * "keep-me and_me_to anotherMaterialToBeKept \'name with whitespace\'"</tt>.
+ * If a material matches on of these names, it will not be modified or
+ * removed by the postprocessing step nor will other materials be replaced
+ * by a reference to it. <br>
+ * This option might be useful if you are using some magic material names
+ * to pass additional semantics through the content pipeline. This ensures
+ * they won't be optimized away, but a general optimization is still
+ * performed for materials not contained in the list.
+ * Property type: String. Default value: n/a
+ * @note Linefeeds, tabs or carriage returns are treated as whitespace.
+ * Material names are case sensitive.
+ */
+#define AI_CONFIG_PP_RRM_EXCLUDE_LIST \
+ "PP_RRM_EXCLUDE_LIST"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to
+ * keep the scene hierarchy. Meshes are moved to worldspace, but
+ * no optimization is performed (read: meshes with equal materials are not
+ * joined. The total number of meshes won't change).
+ *
+ * This option could be of use for you if the scene hierarchy contains
+ * important additional information which you intend to parse.
+ * For rendering, you can still render all meshes in the scene without
+ * any transformations.
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_PP_PTV_KEEP_HIERARCHY \
+ "PP_PTV_KEEP_HIERARCHY"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to normalize
+ * all vertex components into the [-1,1] range. That is, a bounding box
+ * for the whole scene is computed, the maximum component is taken and all
+ * meshes are scaled appropriately (uniformly of course!).
+ * This might be useful if you don't know the spatial dimension of the input
+ * data*/
+#define AI_CONFIG_PP_PTV_NORMALIZE \
+ "PP_PTV_NORMALIZE"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to use
+ * a users defined matrix as the scene root node transformation before
+ * transforming vertices.
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION \
+ "PP_PTV_ADD_ROOT_TRANSFORMATION"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to use
+ * a users defined matrix as the scene root node transformation before
+ * transforming vertices. This property correspond to the 'a1' component
+ * of the transformation matrix.
+ * Property type: aiMatrix4x4.
+ */
+#define AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION \
+ "PP_PTV_ROOT_TRANSFORMATION"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_FindDegenerates step to
+ * remove degenerated primitives from the import - immediately.
+ *
+ * The default behaviour converts degenerated triangles to lines and
+ * degenerated lines to points. See the documentation to the
+ * #aiProcess_FindDegenerates step for a detailed example of the various ways
+ * to get rid of these lines and points if you don't want them.
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_PP_FD_REMOVE \
+ "PP_FD_REMOVE"
+
+// ---------------------------------------------------------------------------
+/**
+ * @brief Configures the #aiProcess_FindDegenerates to check the area of a
+ * trinagle to be greates than e-6. If this is not the case the triangle will
+ * be removed if #AI_CONFIG_PP_FD_REMOVE is set to true.
+ */
+#define AI_CONFIG_PP_FD_CHECKAREA \
+ "PP_FD_CHECKAREA"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_OptimizeGraph step to preserve nodes
+ * matching a name in a given list.
+ *
+ * This is a list of 1 to n strings, ' ' serves as delimiter character.
+ * Identifiers containing whitespaces must be enclosed in *single*
+ * quotation marks. For example:<tt>
+ * "keep-me and_me_to anotherNodeToBeKept \'name with whitespace\'"</tt>.
+ * If a node matches on of these names, it will not be modified or
+ * removed by the postprocessing step.<br>
+ * This option might be useful if you are using some magic node names
+ * to pass additional semantics through the content pipeline. This ensures
+ * they won't be optimized away, but a general optimization is still
+ * performed for nodes not contained in the list.
+ * Property type: String. Default value: n/a
+ * @note Linefeeds, tabs or carriage returns are treated as whitespace.
+ * Node names are case sensitive.
+ */
+#define AI_CONFIG_PP_OG_EXCLUDE_LIST \
+ "PP_OG_EXCLUDE_LIST"
+
+// ---------------------------------------------------------------------------
+/** @brief Set the maximum number of triangles in a mesh.
+ *
+ * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
+ * whether a mesh must be split or not.
+ * @note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES
+ * Property type: integer.
+ */
+#define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT \
+ "PP_SLM_TRIANGLE_LIMIT"
+
+// default value for AI_CONFIG_PP_SLM_TRIANGLE_LIMIT
+#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES)
+#define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the maximum number of vertices in a mesh.
+ *
+ * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
+ * whether a mesh must be split or not.
+ * @note The default value is AI_SLM_DEFAULT_MAX_VERTICES
+ * Property type: integer.
+ */
+#define AI_CONFIG_PP_SLM_VERTEX_LIMIT \
+ "PP_SLM_VERTEX_LIMIT"
+
+// default value for AI_CONFIG_PP_SLM_VERTEX_LIMIT
+#if (!defined AI_SLM_DEFAULT_MAX_VERTICES)
+#define AI_SLM_DEFAULT_MAX_VERTICES 1000000
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the maximum number of bones affecting a single vertex
+ *
+ * This is used by the #aiProcess_LimitBoneWeights PostProcess-Step.
+ * @note The default value is AI_LMW_MAX_WEIGHTS
+ * Property type: integer.*/
+#define AI_CONFIG_PP_LBW_MAX_WEIGHTS \
+ "PP_LBW_MAX_WEIGHTS"
+
+// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
+#if (!defined AI_LMW_MAX_WEIGHTS)
+#define AI_LMW_MAX_WEIGHTS 0x4
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+// ---------------------------------------------------------------------------
+/** @brief Lower the deboning threshold in order to remove more bones.
+ *
+ * This is used by the #aiProcess_Debone PostProcess-Step.
+ * @note The default value is AI_DEBONE_THRESHOLD
+ * Property type: float.*/
+#define AI_CONFIG_PP_DB_THRESHOLD \
+ "PP_DB_THRESHOLD"
+
+// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
+#if (!defined AI_DEBONE_THRESHOLD)
+#define AI_DEBONE_THRESHOLD 1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------------------------------------------------------------------------
+/** @brief Require all bones qualify for deboning before removing any
+ *
+ * This is used by the #aiProcess_Debone PostProcess-Step.
+ * @note The default value is 0
+ * Property type: bool.*/
+#define AI_CONFIG_PP_DB_ALL_OR_NONE \
+ "PP_DB_ALL_OR_NONE"
+
+/** @brief Default value for the #AI_CONFIG_PP_ICL_PTCACHE_SIZE property
+ */
+#ifndef PP_ICL_PTCACHE_SIZE
+#define PP_ICL_PTCACHE_SIZE 12
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the size of the post-transform vertex cache to optimize the
+ * vertices for. This configures the #aiProcess_ImproveCacheLocality step.
+ *
+ * The size is given in vertices. Of course you can't know how the vertex
+ * format will exactly look like after the import returns, but you can still
+ * guess what your meshes will probably have.
+ * @note The default value is #PP_ICL_PTCACHE_SIZE. That results in slight
+ * performance improvements for most nVidia/AMD cards since 2002.
+ * Property type: integer.
+ */
+#define AI_CONFIG_PP_ICL_PTCACHE_SIZE "PP_ICL_PTCACHE_SIZE"
+
+// ---------------------------------------------------------------------------
+/** @brief Enumerates components of the aiScene and aiMesh data structures
+ * that can be excluded from the import using the #aiProcess_RemoveComponent step.
+ *
+ * See the documentation to #aiProcess_RemoveComponent for more details.
+ */
+enum aiComponent {
+/** Normal vectors */
+#ifdef SWIG
+ aiComponent_NORMALS = 0x2,
+#else
+ aiComponent_NORMALS = 0x2u,
+#endif
+
+/** Tangents and bitangents go always together ... */
+#ifdef SWIG
+ aiComponent_TANGENTS_AND_BITANGENTS = 0x4,
+#else
+ aiComponent_TANGENTS_AND_BITANGENTS = 0x4u,
+#endif
+
+ /** ALL color sets
+ * Use aiComponent_COLORn(N) to specify the N'th set */
+ aiComponent_COLORS = 0x8,
+
+ /** ALL texture UV sets
+ * aiComponent_TEXCOORDn(N) to specify the N'th set */
+ aiComponent_TEXCOORDS = 0x10,
+
+ /** Removes all bone weights from all meshes.
+ * The scenegraph nodes corresponding to the bones are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_BONEWEIGHTS = 0x20,
+
+ /** Removes all node animations (aiScene::mAnimations).
+ * The corresponding scenegraph nodes are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_ANIMATIONS = 0x40,
+
+ /** Removes all embedded textures (aiScene::mTextures) */
+ aiComponent_TEXTURES = 0x80,
+
+ /** Removes all light sources (aiScene::mLights).
+ * The corresponding scenegraph nodes are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_LIGHTS = 0x100,
+
+ /** Removes all cameras (aiScene::mCameras).
+ * The corresponding scenegraph nodes are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_CAMERAS = 0x200,
+
+ /** Removes all meshes (aiScene::mMeshes). */
+ aiComponent_MESHES = 0x400,
+
+ /** Removes all materials. One default material will
+ * be generated, so aiScene::mNumMaterials will be 1. */
+ aiComponent_MATERIALS = 0x800,
+
+/** This value is not used. It is just there to force the
+ * compiler to map this enum to a 32 Bit integer. */
+#ifndef SWIG
+ _aiComponent_Force32Bit = 0x9fffffff
+#endif
+};
+
+// Remove a specific color channel 'n'
+#define aiComponent_COLORSn(n) (1u << (n + 20u))
+
+// Remove a specific UV channel 'n'
+#define aiComponent_TEXCOORDSn(n) (1u << (n + 25u))
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_RemoveComponent step:
+ * Specifies the parts of the data structure to be removed.
+ *
+ * See the documentation to this step for further details. The property
+ * is expected to be an integer, a bitwise combination of the
+ * #aiComponent flags defined above in this header. The default
+ * value is 0. Important: if no valid mesh is remaining after the
+ * step has been executed (e.g you thought it was funny to specify ALL
+ * of the flags defined above) the import FAILS. Mainly because there is
+ * no data to work on anymore ...
+ */
+#define AI_CONFIG_PP_RVC_FLAGS \
+ "PP_RVC_FLAGS"
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_SortByPType step:
+ * Specifies which primitive types are removed by the step.
+ *
+ * This is a bitwise combination of the aiPrimitiveType flags.
+ * Specifying all of them is illegal, of course. A typical use would
+ * be to exclude all line and point meshes from the import. This
+ * is an integer property, its default value is 0.
+ */
+#define AI_CONFIG_PP_SBP_REMOVE \
+ "PP_SBP_REMOVE"
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_FindInvalidData step:
+ * Specifies the floating-point accuracy for animation values. The step
+ * checks for animation tracks where all frame values are absolutely equal
+ * and removes them. This tweakable controls the epsilon for floating-point
+ * comparisons - two keys are considered equal if the invariant
+ * abs(n0-n1)>epsilon holds true for all vector respectively quaternion
+ * components. The default value is 0.f - comparisons are exact then.
+ */
+#define AI_CONFIG_PP_FID_ANIM_ACCURACY \
+ "PP_FID_ANIM_ACCURACY"
+
+// TransformUVCoords evaluates UV scalings
+#define AI_UVTRAFO_SCALING 0x1
+
+// TransformUVCoords evaluates UV rotations
+#define AI_UVTRAFO_ROTATION 0x2
+
+// TransformUVCoords evaluates UV translation
+#define AI_UVTRAFO_TRANSLATION 0x4
+
+// Everything baked together -> default value
+#define AI_UVTRAFO_ALL (AI_UVTRAFO_SCALING | AI_UVTRAFO_ROTATION | AI_UVTRAFO_TRANSLATION)
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_FindInvalidData step:
+ * Set to true to ignore texture coordinates. This may be useful if you have
+ * to assign different kind of textures like one for the summer or one for the winter.
+ */
+#define AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS \
+ "PP_FID_IGNORE_TEXTURECOORDS"
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_TransformUVCoords step:
+ * Specifies which UV transformations are evaluated.
+ *
+ * This is a bitwise combination of the AI_UVTRAFO_XXX flags (integer
+ * property, of course). By default all transformations are enabled
+ * (AI_UVTRAFO_ALL).
+ */
+#define AI_CONFIG_PP_TUV_EVALUATE \
+ "PP_TUV_EVALUATE"
+
+// ---------------------------------------------------------------------------
+/** @brief A hint to assimp to favour speed against import quality.
+ *
+ * Enabling this option may result in faster loading, but it needn't.
+ * It represents just a hint to loaders and post-processing steps to use
+ * faster code paths, if possible.
+ * This property is expected to be an integer, != 0 stands for true.
+ * The default value is 0.
+ */
+#define AI_CONFIG_FAVOUR_SPEED \
+ "FAVOUR_SPEED"
+
+// ###########################################################################
+// IMPORTER SETTINGS
+// Various stuff to fine-tune the behaviour of specific importer plugins.
+// ###########################################################################
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will merge all geometry layers present
+ * in the source file or take only the first.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS \
+ "IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read all materials present in the
+ * source file or take only the referenced materials.
+ *
+ * This is void unless IMPORT_FBX_READ_MATERIALS=1.
+ *
+ * The default value is false (0)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS \
+ "IMPORT_FBX_READ_ALL_MATERIALS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read materials.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_MATERIALS \
+ "IMPORT_FBX_READ_MATERIALS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read embedded textures.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_TEXTURES \
+ "IMPORT_FBX_READ_TEXTURES"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read cameras.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_CAMERAS \
+ "IMPORT_FBX_READ_CAMERAS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read light sources.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_LIGHTS \
+ "IMPORT_FBX_READ_LIGHTS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read animations.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS \
+ "IMPORT_FBX_READ_ANIMATIONS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will act in strict mode in which only
+ * FBX 2013 is supported and any other sub formats are rejected. FBX 2013
+ * is the primary target for the importer, so this format is best
+ * supported and well-tested.
+ *
+ * The default value is false (0)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_STRICT_MODE \
+ "IMPORT_FBX_STRICT_MODE"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will preserve pivot points for
+ * transformations (as extra nodes). If set to false, pivots and offsets
+ * will be evaluated whenever possible.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS \
+ "IMPORT_FBX_PRESERVE_PIVOTS"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the importer will drop empty animation curves or
+ * animation curves which match the bind pose transformation over their
+ * entire defined range.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES \
+ "IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will use the legacy embedded texture naming.
+*
+* The default value is false (0)
+* Property type: bool
+*/
+#define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \
+ "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
+
+// ---------------------------------------------------------------------------
+/** @brief Set the vertex animation keyframe to be imported
+ *
+ * ASSIMP does not support vertex keyframes (only bone animation is supported).
+ * The library reads only one frame of models with vertex animations.
+ * By default this is the first frame.
+ * \note The default value is 0. This option applies to all importers.
+ * However, it is also possible to override the global setting
+ * for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME
+ * options (where XXX is a placeholder for the file format for which you
+ * want to override the global setting).
+ * Property type: integer.
+ */
+#define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME "IMPORT_GLOBAL_KEYFRAME"
+
+#define AI_CONFIG_IMPORT_MD3_KEYFRAME "IMPORT_MD3_KEYFRAME"
+#define AI_CONFIG_IMPORT_MD2_KEYFRAME "IMPORT_MD2_KEYFRAME"
+#define AI_CONFIG_IMPORT_MDL_KEYFRAME "IMPORT_MDL_KEYFRAME"
+#define AI_CONFIG_IMPORT_MDC_KEYFRAME "IMPORT_MDC_KEYFRAME"
+#define AI_CONFIG_IMPORT_SMD_KEYFRAME "IMPORT_SMD_KEYFRAME"
+#define AI_CONFIG_IMPORT_UNREAL_KEYFRAME "IMPORT_UNREAL_KEYFRAME"
+
+// ---------------------------------------------------------------------------
+/** Smd load multiple animations
+ *
+ * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_SMD_LOAD_ANIMATION_LIST "IMPORT_SMD_LOAD_ANIMATION_LIST"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the AC loader to collect all surfaces which have the
+ * "Backface cull" flag set in separate meshes.
+ *
+ * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL \
+ "IMPORT_AC_SEPARATE_BFCULL"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures whether the AC loader evaluates subdivision surfaces (
+ * indicated by the presence of the 'subdiv' attribute in the file). By
+ * default, Assimp performs the subdivision using the standard
+ * Catmull-Clark algorithm
+ *
+ * * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION \
+ "IMPORT_AC_EVAL_SUBDIVISION"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the UNREAL 3D loader to separate faces with different
+ * surface flags (e.g. two-sided vs. single-sided).
+ *
+ * * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS \
+ "UNREAL_HANDLE_FLAGS"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the terragen import plugin to compute uv's for
+ * terrains, if not given. Furthermore a default texture is assigned.
+ *
+ * UV coordinates for terrains are so simple to compute that you'll usually
+ * want to compute them on your own, if you need them. This option is intended
+ * for model viewers which want to offer an easy way to apply textures to
+ * terrains.
+ * * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_TER_MAKE_UVS \
+ "IMPORT_TER_MAKE_UVS"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the ASE loader to always reconstruct normal vectors
+ * basing on the smoothing groups loaded from the file.
+ *
+ * Some ASE files have carry invalid normals, other don't.
+ * * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS \
+ "IMPORT_ASE_RECONSTRUCT_NORMALS"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the M3D loader to detect and process multi-part
+ * Quake player models.
+ *
+ * These models usually consist of 3 files, lower.md3, upper.md3 and
+ * head.md3. If this property is set to true, Assimp will try to load and
+ * combine all three files if one of them is loaded.
+ * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART \
+ "IMPORT_MD3_HANDLE_MULTIPART"
+
+// ---------------------------------------------------------------------------
+/** @brief Tells the MD3 loader which skin files to load.
+ *
+ * When loading MD3 files, Assimp checks whether a file
+ * [md3_file_name]_[skin_name].skin is existing. These files are used by
+ * Quake III to be able to assign different skins (e.g. red and blue team)
+ * to models. 'default', 'red', 'blue' are typical skin names.
+ * Property type: String. Default value: "default".
+ */
+#define AI_CONFIG_IMPORT_MD3_SKIN_NAME \
+ "IMPORT_MD3_SKIN_NAME"
+
+// ---------------------------------------------------------------------------
+/** @brief Specify the Quake 3 shader file to be used for a particular
+ * MD3 file. This can also be a search path.
+ *
+ * By default Assimp's behaviour is as follows: If a MD3 file
+ * <tt>any_path/models/any_q3_subdir/model_name/file_name.md3</tt> is
+ * loaded, the library tries to locate the corresponding shader file in
+ * <tt>any_path/scripts/model_name.shader</tt>. This property overrides this
+ * behaviour. It can either specify a full path to the shader to be loaded
+ * or alternatively the path (relative or absolute) to the directory where
+ * the shaders for all MD3s to be loaded reside. Assimp attempts to open
+ * <tt>IMPORT_MD3_SHADER_SRC/model_name.shader</tt> first, <tt>IMPORT_MD3_SHADER_SRC/file_name.shader</tt>
+ * is the fallback file. Note that IMPORT_MD3_SHADER_SRC should have a terminal (back)slash.
+ * Property type: String. Default value: n/a.
+ */
+#define AI_CONFIG_IMPORT_MD3_SHADER_SRC \
+ "IMPORT_MD3_SHADER_SRC"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the LWO loader to load just one layer from the model.
+ *
+ * LWO files consist of layers and in some cases it could be useful to load
+ * only one of them. This property can be either a string - which specifies
+ * the name of the layer - or an integer - the index of the layer. If the
+ * property is not set the whole LWO model is loaded. Loading fails if the
+ * requested layer is not available. The layer index is zero-based and the
+ * layer name may not be empty.<br>
+ * Property type: Integer. Default value: all layers are loaded.
+ */
+#define AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY \
+ "IMPORT_LWO_ONE_LAYER_ONLY"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the MD5 loader to not load the MD5ANIM file for
+ * a MD5MESH file automatically.
+ *
+ * The default strategy is to look for a file with the same name but the
+ * MD5ANIM extension in the same directory. If it is found, it is loaded
+ * and combined with the MD5MESH file. This configuration option can be
+ * used to disable this behaviour.
+ *
+ * * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD \
+ "IMPORT_MD5_NO_ANIM_AUTOLOAD"
+
+// ---------------------------------------------------------------------------
+/** @brief Defines the begin of the time range for which the LWS loader
+ * evaluates animations and computes aiNodeAnim's.
+ *
+ * Assimp provides full conversion of LightWave's envelope system, including
+ * pre and post conditions. The loader computes linearly subsampled animation
+ * chanels with the frame rate given in the LWS file. This property defines
+ * the start time. Note: animation channels are only generated if a node
+ * has at least one envelope with more tan one key assigned. This property.
+ * is given in frames, '0' is the first frame. By default, if this property
+ * is not set, the importer takes the animation start from the input LWS
+ * file ('FirstFrame' line)<br>
+ * Property type: Integer. Default value: taken from file.
+ *
+ * @see AI_CONFIG_IMPORT_LWS_ANIM_END - end of the imported time range
+ */
+#define AI_CONFIG_IMPORT_LWS_ANIM_START \
+ "IMPORT_LWS_ANIM_START"
+#define AI_CONFIG_IMPORT_LWS_ANIM_END \
+ "IMPORT_LWS_ANIM_END"
+
+// ---------------------------------------------------------------------------
+/** @brief Defines the output frame rate of the IRR loader.
+ *
+ * IRR animations are difficult to convert for Assimp and there will
+ * always be a loss of quality. This setting defines how many keys per second
+ * are returned by the converter.<br>
+ * Property type: integer. Default value: 100
+ */
+#define AI_CONFIG_IMPORT_IRR_ANIM_FPS \
+ "IMPORT_IRR_ANIM_FPS"
+
+// ---------------------------------------------------------------------------
+/** @brief Ogre Importer will try to find referenced materials from this file.
+ *
+ * Ogre meshes reference with material names, this does not tell Assimp the file
+ * where it is located in. Assimp will try to find the source file in the following
+ * order: <material-name>.material, <mesh-filename-base>.material and
+ * lastly the material name defined by this config property.
+ * <br>
+ * Property type: String. Default value: Scene.material.
+ */
+#define AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE \
+ "IMPORT_OGRE_MATERIAL_FILE"
+
+// ---------------------------------------------------------------------------
+/** @brief Ogre Importer detect the texture usage from its filename.
+ *
+ * Ogre material texture units do not define texture type, the textures usage
+ * depends on the used shader or Ogre's fixed pipeline. If this config property
+ * is true Assimp will try to detect the type from the textures filename postfix:
+ * _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map, _s, _spec,
+ * _specular and _specularmap for specular map, _l, _light, _lightmap, _occ
+ * and _occlusion for light map, _disp and _displacement for displacement map.
+ * The matching is case insensitive. Post fix is taken between the last
+ * underscore and the last period.
+ * Default behavior is to detect type from lower cased texture unit name by
+ * matching against: normalmap, specularmap, lightmap and displacementmap.
+ * For both cases if no match is found aiTextureType_DIFFUSE is used.
+ * <br>
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME \
+ "IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME"
+
+/** @brief Specifies whether the Android JNI asset extraction is supported.
+ *
+ * Turn on this option if you want to manage assets in native
+ * Android application without having to keep the internal directory and asset
+ * manager pointer.
+ */
+#define AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT "AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the IFC loader skips over IfcSpace elements.
+ *
+ * IfcSpace elements (and their geometric representations) are used to
+ * represent, well, free space in a building storey.<br>
+ * Property type: Bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS "IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the IFC loader will use its own, custom triangulation
+ * algorithm to triangulate wall and floor meshes.
+ *
+ * If this property is set to false, walls will be either triangulated by
+ * #aiProcess_Triangulate or will be passed through as huge polygons with
+ * faked holes (i.e. holes that are connected with the outer boundary using
+ * a dummy edge). It is highly recommended to set this property to true
+ * if you want triangulated data because #aiProcess_Triangulate is known to
+ * have problems with the kind of polygons that the IFC loader spits out for
+ * complicated meshes.
+ * Property type: Bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION "IMPORT_IFC_CUSTOM_TRIANGULATION"
+
+// ---------------------------------------------------------------------------
+/** @brief Set the tessellation conic angle for IFC smoothing curves.
+ *
+ * This is used by the IFC importer to determine the tessellation parameter
+ * for smoothing curves.
+ * @note The default value is AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE and the
+ * accepted values are in range [5.0, 120.0].
+ * Property type: Float.
+ */
+#define AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE "IMPORT_IFC_SMOOTHING_ANGLE"
+
+// default value for AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE
+#if (!defined AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE)
+#define AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE 10.0f
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the tessellation for IFC cylindrical shapes.
+ *
+ * This is used by the IFC importer to determine the tessellation parameter
+ * for cylindrical shapes, i.e. the number of segments used to approximate a circle.
+ * @note The default value is AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION and the
+ * accepted values are in range [3, 180].
+ * Property type: Integer.
+ */
+#define AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION "IMPORT_IFC_CYLINDRICAL_TESSELLATION"
+
+// default value for AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION
+#if (!defined AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION)
+#define AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION 32
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the Collada loader will ignore the provided up direction.
+ *
+ * If this property is set to true, the up direction provided in the file header will
+ * be ignored and the file will be loaded as is.
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the Collada loader should use Collada names as node names.
+ *
+ * If this property is set to true, the Collada names will be used as the
+ * node name. The default is to use the id tag (resp. sid tag, if no id tag is present)
+ * instead.
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES "IMPORT_COLLADA_USE_COLLADA_NAMES"
+
+// ---------- All the Export defines ------------
+
+/** @brief Specifies the xfile use double for real values of float
+ *
+ * Property type: Bool. Default value: false.
+ */
+
+#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT"
+
+/**
+ *
+ */
+#define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS"
+
+/**
+ * @brief Specifies a gobal key factor for scale, float value
+ */
+#define AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY "GLOBAL_SCALE_FACTOR"
+
+#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT)
+#define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------- All the Build/Compile-time defines ------------
+
+/** @brief Specifies if double precision is supported inside assimp
+ *
+ * Property type: Bool. Default value: undefined.
+ */
+
+/* #cmakedefine ASSIMP_DOUBLE_PRECISION 1 */
+
+#endif // !! AI_CONFIG_H_INC
diff --git a/thirdparty/assimp/code/BaseImporter.cpp b/thirdparty/assimp/code/BaseImporter.cpp
new file mode 100644
index 0000000000..4803c6d6f2
--- /dev/null
+++ b/thirdparty/assimp/code/BaseImporter.cpp
@@ -0,0 +1,616 @@
+/*
+---------------------------------------------------------------------------
+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() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BaseImporter::~BaseImporter() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file and returns the imported data.
+aiScene* BaseImporter::ReadFile(const 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);
+
+ } 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;
+}
+
+#include "../contrib/utf8cpp/source/utf8.h"
+
+// ------------------------------------------------------------------------------------------------
+// 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 %%%");
+ }
+}
diff --git a/thirdparty/assimp/code/BaseProcess.cpp b/thirdparty/assimp/code/BaseProcess.cpp
new file mode 100644
index 0000000000..18872c3693
--- /dev/null
+++ b/thirdparty/assimp/code/BaseProcess.cpp
@@ -0,0 +1,107 @@
+/*
+---------------------------------------------------------------------------
+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 = NULL;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseProcess::SetupProperties(const Importer* /*pImp*/)
+{
+ // the default implementation does nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+bool BaseProcess::RequireVerboseFormat() const
+{
+ return true;
+}
+
diff --git a/thirdparty/assimp/code/BaseProcess.h b/thirdparty/assimp/code/BaseProcess.h
new file mode 100644
index 0000000000..4d5c7a76be
--- /dev/null
+++ b/thirdparty/assimp/code/BaseProcess.h
@@ -0,0 +1,290 @@
+/*
+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
diff --git a/thirdparty/assimp/code/Bitmap.cpp b/thirdparty/assimp/code/Bitmap.cpp
new file mode 100644
index 0000000000..b22b71ea9e
--- /dev/null
+++ b/thirdparty/assimp/code/Bitmap.cpp
@@ -0,0 +1,155 @@
+/*
+---------------------------------------------------------------------------
+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);
+ }
+ }
+
+}
diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.cpp b/thirdparty/assimp/code/CInterfaceIOWrapper.cpp
new file mode 100644
index 0000000000..5a3a49565a
--- /dev/null
+++ b/thirdparty/assimp/code/CInterfaceIOWrapper.cpp
@@ -0,0 +1,136 @@
+/*
+---------------------------------------------------------------------------
+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;
+}
+
+}
diff --git a/thirdparty/assimp/code/CInterfaceIOWrapper.h b/thirdparty/assimp/code/CInterfaceIOWrapper.h
new file mode 100644
index 0000000000..2162320302
--- /dev/null
+++ b/thirdparty/assimp/code/CInterfaceIOWrapper.h
@@ -0,0 +1,99 @@
+/*
+---------------------------------------------------------------------------
+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
+
diff --git a/thirdparty/assimp/code/CalcTangentsProcess.cpp b/thirdparty/assimp/code/CalcTangentsProcess.cpp
new file mode 100644
index 0000000000..b30f39c274
--- /dev/null
+++ b/thirdparty/assimp/code/CalcTangentsProcess.cpp
@@ -0,0 +1,319 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to calculate
+ * tangents and bitangents for all imported meshes
+ */
+
+// internal headers
+#include "CalcTangentsProcess.h"
+#include "ProcessHelper.h"
+#include <assimp/TinyFormatter.h>
+#include <assimp/qnan.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CalcTangentsProcess::CalcTangentsProcess()
+: configMaxAngle( AI_DEG_TO_RAD(45.f) )
+, configSourceUV( 0 ) {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+CalcTangentsProcess::~CalcTangentsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_CalcTangentSpace) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void CalcTangentsProcess::SetupProperties(const Importer* pImp)
+{
+ ai_assert( NULL != pImp );
+
+ // get the current value of the property
+ configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
+ configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f);
+ configMaxAngle = AI_DEG_TO_RAD(configMaxAngle);
+
+ configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void CalcTangentsProcess::Execute( aiScene* pScene)
+{
+ ai_assert( NULL != pScene );
+
+ ASSIMP_LOG_DEBUG("CalcTangentsProcess begin");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++ ) {
+ if(ProcessMesh( pScene->mMeshes[a],a))bHas = true;
+ }
+
+ if ( bHas ) {
+ ASSIMP_LOG_INFO("CalcTangentsProcess finished. Tangents have been calculated");
+ } else {
+ ASSIMP_LOG_DEBUG("CalcTangentsProcess finished");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates tangents and bi-tangents for the given mesh
+bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
+{
+ // we assume that the mesh is still in the verbose vertex format where each face has its own set
+ // of vertices and no vertices are shared between faces. Sadly I don't know any quick test to
+ // assert() it here.
+ // assert( must be verbose, dammit);
+
+ if (pMesh->mTangents) // this implies that mBitangents is also there
+ return false;
+
+ // If the mesh consists of lines and/or points but not of
+ // triangles or higher-order polygons the normal vectors
+ // are undefined.
+ if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
+ {
+ ASSIMP_LOG_INFO("Tangents are undefined for line and point meshes");
+ return false;
+ }
+
+ // what we can check, though, is if the mesh has normals and texture coordinates. That's a requirement
+ if( pMesh->mNormals == NULL)
+ {
+ ASSIMP_LOG_ERROR("Failed to compute tangents; need normals");
+ return false;
+ }
+ if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] )
+ {
+ ASSIMP_LOG_ERROR((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV));
+ return false;
+ }
+
+ const float angleEpsilon = 0.9999f;
+
+ std::vector<bool> vertexDone( pMesh->mNumVertices, false);
+ const float qnan = get_qnan();
+
+ // create space for the tangents and bitangents
+ pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
+
+ const aiVector3D* meshPos = pMesh->mVertices;
+ const aiVector3D* meshNorm = pMesh->mNormals;
+ const aiVector3D* meshTex = pMesh->mTextureCoords[configSourceUV];
+ aiVector3D* meshTang = pMesh->mTangents;
+ aiVector3D* meshBitang = pMesh->mBitangents;
+
+ // calculate the tangent and bitangent for every face
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ const aiFace& face = pMesh->mFaces[a];
+ if (face.mNumIndices < 3)
+ {
+ // There are less than three indices, thus the tangent vector
+ // is not defined. We are finished with these vertices now,
+ // their tangent vectors are set to qnan.
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ {
+ unsigned int idx = face.mIndices[i];
+ vertexDone [idx] = true;
+ meshTang [idx] = aiVector3D(qnan);
+ meshBitang [idx] = aiVector3D(qnan);
+ }
+
+ continue;
+ }
+
+ // triangle or polygon... we always use only the first three indices. A polygon
+ // is supposed to be planar anyways....
+ // FIXME: (thom) create correct calculation for multi-vertex polygons maybe?
+ const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2];
+
+ // position differences p1->p2 and p1->p3
+ aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0];
+
+ // texture offset p1->p2 and p1->p3
+ float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y;
+ float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
+ float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
+ // when t1, t2, t3 in same position in UV space, just use default UV direction.
+ if ( sx * ty == sy * tx ) {
+ sx = 0.0; sy = 1.0;
+ tx = 1.0; ty = 0.0;
+ }
+
+ // tangent points in the direction where to positive X axis of the texture coord's would point in model space
+ // bitangent's points along the positive Y axis of the texture coord's, respectively
+ aiVector3D tangent, bitangent;
+ tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
+ tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
+ tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
+ bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
+ bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
+ bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
+
+ // store for every vertex of that face
+ for( unsigned int b = 0; b < face.mNumIndices; ++b ) {
+ unsigned int p = face.mIndices[b];
+
+ // project tangent and bitangent into the plane formed by the vertex' normal
+ aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
+ aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
+ localTangent.Normalize(); localBitangent.Normalize();
+
+ // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
+ bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
+ bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z);
+ if (invalid_tangent != invalid_bitangent) {
+ if (invalid_tangent) {
+ localTangent = meshNorm[p] ^ localBitangent;
+ localTangent.Normalize();
+ } else {
+ localBitangent = localTangent ^ meshNorm[p];
+ localBitangent.Normalize();
+ }
+ }
+
+ // and write it into the mesh.
+ meshTang[ p ] = localTangent;
+ meshBitang[ p ] = localBitangent;
+ }
+ }
+
+
+ // create a helper to quickly find locally close vertices among the vertex array
+ // FIX: check whether we can reuse the SpatialSort of a previous step
+ SpatialSort* vertexFinder = NULL;
+ SpatialSort _vertexFinder;
+ float posEpsilon;
+ if (shared)
+ {
+ std::vector<std::pair<SpatialSort,float> >* avf;
+ shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
+ if (avf)
+ {
+ std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
+ vertexFinder = &blubb.first;
+ posEpsilon = blubb.second;;
+ }
+ }
+ if (!vertexFinder)
+ {
+ _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+ vertexFinder = &_vertexFinder;
+ posEpsilon = ComputePositionEpsilon(pMesh);
+ }
+ std::vector<unsigned int> verticesFound;
+
+ const float fLimit = std::cos(configMaxAngle);
+ std::vector<unsigned int> closeVertices;
+
+ // in the second pass we now smooth out all tangents and bitangents at the same local position
+ // if they are not too far off.
+ for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+ {
+ if( vertexDone[a])
+ continue;
+
+ const aiVector3D& origPos = pMesh->mVertices[a];
+ const aiVector3D& origNorm = pMesh->mNormals[a];
+ const aiVector3D& origTang = pMesh->mTangents[a];
+ const aiVector3D& origBitang = pMesh->mBitangents[a];
+ closeVertices.resize( 0 );
+
+ // find all vertices close to that position
+ vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
+
+ closeVertices.reserve (verticesFound.size()+5);
+ closeVertices.push_back( a);
+
+ // look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
+ for( unsigned int b = 0; b < verticesFound.size(); b++)
+ {
+ unsigned int idx = verticesFound[b];
+ if( vertexDone[idx])
+ continue;
+ if( meshNorm[idx] * origNorm < angleEpsilon)
+ continue;
+ if( meshTang[idx] * origTang < fLimit)
+ continue;
+ if( meshBitang[idx] * origBitang < fLimit)
+ continue;
+
+ // it's similar enough -> add it to the smoothing group
+ closeVertices.push_back( idx);
+ vertexDone[idx] = true;
+ }
+
+ // smooth the tangents and bitangents of all vertices that were found to be close enough
+ aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
+ for( unsigned int b = 0; b < closeVertices.size(); ++b)
+ {
+ smoothTangent += meshTang[ closeVertices[b] ];
+ smoothBitangent += meshBitang[ closeVertices[b] ];
+ }
+ smoothTangent.Normalize();
+ smoothBitangent.Normalize();
+
+ // and write it back into all affected tangents
+ for( unsigned int b = 0; b < closeVertices.size(); ++b)
+ {
+ meshTang[ closeVertices[b] ] = smoothTangent;
+ meshBitang[ closeVertices[b] ] = smoothBitangent;
+ }
+ }
+ return true;
+}
diff --git a/thirdparty/assimp/code/CalcTangentsProcess.h b/thirdparty/assimp/code/CalcTangentsProcess.h
new file mode 100644
index 0000000000..18775abcc7
--- /dev/null
+++ b/thirdparty/assimp/code/CalcTangentsProcess.h
@@ -0,0 +1,117 @@
+/*
+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 calculate tangents and
+ bitangents on all imported meshes.*/
+#ifndef AI_CALCTANGENTSPROCESS_H_INC
+#define AI_CALCTANGENTSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex
+ * of all meshes. It is expected to be run before the JoinVerticesProcess runs
+ * because the joining of vertices also considers tangents and bitangents for
+ * uniqueness.
+ */
+class ASSIMP_API_WINONLY CalcTangentsProcess : public BaseProcess
+{
+public:
+
+ CalcTangentsProcess();
+ ~CalcTangentsProcess();
+
+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.
+ */
+ void SetupProperties(const Importer* pImp);
+
+
+ // setter for configMaxAngle
+ inline void SetMaxSmoothAngle(float f)
+ {
+ configMaxAngle =f;
+ }
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Calculates tangents and bitangents for a specific mesh.
+ * @param pMesh The mesh to process.
+ * @param meshIndex Index of the mesh
+ */
+ bool ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
+
+ // -------------------------------------------------------------------
+ /** Executes the post processing step on the given imported data.
+ * @param pScene The imported data to work at.
+ */
+ void Execute( aiScene* pScene);
+
+private:
+
+ /** Configuration option: maximum smoothing angle, in radians*/
+ float configMaxAngle;
+ unsigned int configSourceUV;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC
diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.cpp b/thirdparty/assimp/code/ComputeUVMappingProcess.cpp
new file mode 100644
index 0000000000..bb571a551b
--- /dev/null
+++ b/thirdparty/assimp/code/ComputeUVMappingProcess.cpp
@@ -0,0 +1,506 @@
+/*
+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 GenUVCoords step */
+
+
+#include "ComputeUVMappingProcess.h"
+#include "ProcessHelper.h"
+#include <assimp/Exceptional.h>
+
+using namespace Assimp;
+
+namespace {
+
+ const static aiVector3D base_axis_y(0.0,1.0,0.0);
+ const static aiVector3D base_axis_x(1.0,0.0,0.0);
+ const static aiVector3D base_axis_z(0.0,0.0,1.0);
+ const static ai_real angle_epsilon = ai_real( 0.95 );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ComputeUVMappingProcess::ComputeUVMappingProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ComputeUVMappingProcess::~ComputeUVMappingProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_GenUVCoords) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether a ray intersects a plane and find the intersection point
+inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
+ const aiVector3D& planeNormal, aiVector3D& pos)
+{
+ const ai_real b = planeNormal * (planePos - ray.pos);
+ ai_real h = ray.dir * planeNormal;
+ if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0)
+ return false;
+
+ pos = ray.pos + (ray.dir * h);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find the first empty UV channel in a mesh
+inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
+{
+ for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m)
+ if (!mesh->mTextureCoords[m])return m;
+
+ ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found");
+ return UINT_MAX;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to remove UV seams
+void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
+{
+ // TODO: just a very rough algorithm. I think it could be done
+ // much easier, but I don't know how and am currently too tired to
+ // to think about a better solution.
+
+ const static ai_real LOWER_LIMIT = ai_real( 0.1 );
+ const static ai_real UPPER_LIMIT = ai_real( 0.9 );
+
+ const static ai_real LOWER_EPSILON = ai_real( 10e-3 );
+ const static ai_real UPPER_EPSILON = ai_real( 1.0-10e-3 );
+
+ for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx)
+ {
+ const aiFace& face = mesh->mFaces[fidx];
+ if (face.mNumIndices < 3) continue; // triangles and polygons only, please
+
+ unsigned int small = face.mNumIndices, large = small;
+ bool zero = false, one = false, round_to_zero = false;
+
+ // Check whether this face lies on a UV seam. We can just guess,
+ // but the assumption that a face with at least one very small
+ // on the one side and one very large U coord on the other side
+ // lies on a UV seam should work for most cases.
+ for (unsigned int n = 0; n < face.mNumIndices;++n)
+ {
+ if (out[face.mIndices[n]].x < LOWER_LIMIT)
+ {
+ small = n;
+
+ // If we have a U value very close to 0 we can't
+ // round the others to 0, too.
+ if (out[face.mIndices[n]].x <= LOWER_EPSILON)
+ zero = true;
+ else round_to_zero = true;
+ }
+ if (out[face.mIndices[n]].x > UPPER_LIMIT)
+ {
+ large = n;
+
+ // If we have a U value very close to 1 we can't
+ // round the others to 1, too.
+ if (out[face.mIndices[n]].x >= UPPER_EPSILON)
+ one = true;
+ }
+ }
+ if (small != face.mNumIndices && large != face.mNumIndices)
+ {
+ for (unsigned int n = 0; n < face.mNumIndices;++n)
+ {
+ // If the u value is over the upper limit and no other u
+ // value of that face is 0, round it to 0
+ if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
+ out[face.mIndices[n]].x = 0.0;
+
+ // If the u value is below the lower limit and no other u
+ // value of that face is 1, round it to 1
+ else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one)
+ out[face.mIndices[n]].x = 1.0;
+
+ // The face contains both 0 and 1 as UV coords. This can occur
+ // for faces which have an edge that lies directly on the seam.
+ // Due to numerical inaccuracies one U coord becomes 0, the
+ // other 1. But we do still have a third UV coord to determine
+ // to which side we must round to.
+ else if (one && zero)
+ {
+ if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
+ out[face.mIndices[n]].x = 0.0;
+ else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
+ out[face.mIndices[n]].x = 1.0;
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
+{
+ aiVector3D center, min, max;
+ FindMeshCenter(mesh, center, min, max);
+
+ // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
+ // currently the mapping axis will always be one of x,y,z, except if the
+ // PretransformVertices step is used (it transforms the meshes into worldspace,
+ // thus changing the mapping axis)
+ if (axis * base_axis_x >= angle_epsilon) {
+
+ // For each point get a normalized projection vector in the sphere,
+ // get its longitude and latitude and map them to their respective
+ // UV axes. Problems occur around the poles ... unsolvable.
+ //
+ // The spherical coordinate system looks like this:
+ // x = cos(lon)*cos(lat)
+ // y = sin(lon)*cos(lat)
+ // z = sin(lat)
+ //
+ // Thus we can derive:
+ // lat = arcsin (z)
+ // lon = arctan (y/x)
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
+ out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (std::asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+ }
+ }
+ else if (axis * base_axis_y >= angle_epsilon) {
+ // ... just the same again
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
+ out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (std::asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+ }
+ }
+ else if (axis * base_axis_z >= angle_epsilon) {
+ // ... just the same again
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
+ out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (std::asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+ }
+ }
+ // slower code path in case the mapping axis is not one of the coordinate system axes
+ else {
+ aiMatrix4x4 mTrafo;
+ aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
+
+ // again the same, except we're applying a transformation now
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize();
+ out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+ }
+ }
+
+
+ // Now find and remove UV seams. A seam occurs if a face has a tcoord
+ // close to zero on the one side, and a tcoord close to one on the
+ // other side.
+ RemoveUVSeams(mesh,out);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
+{
+ aiVector3D center, min, max;
+
+ // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
+ // currently the mapping axis will always be one of x,y,z, except if the
+ // PretransformVertices step is used (it transforms the meshes into worldspace,
+ // thus changing the mapping axis)
+ if (axis * base_axis_x >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const ai_real diff = max.x - min.x;
+
+ // If the main axis is 'z', the z coordinate of a point 'p' is mapped
+ // directly to the texture V axis. The other axis is derived from
+ // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
+ // 'c' is the center point of the mesh.
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.x - min.x) / diff;
+ uv.x = (std::atan2( pos.z - center.z, pos.y - center.y) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+ }
+ }
+ else if (axis * base_axis_y >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const ai_real diff = max.y - min.y;
+
+ // just the same ...
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.y - min.y) / diff;
+ uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+ }
+ }
+ else if (axis * base_axis_z >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const ai_real diff = max.z - min.z;
+
+ // just the same ...
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.z - min.z) / diff;
+ uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+ }
+ }
+ // slower code path in case the mapping axis is not one of the coordinate system axes
+ else {
+ aiMatrix4x4 mTrafo;
+ aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
+ FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
+ const ai_real diff = max.y - min.y;
+
+ // again the same, except we're applying a transformation now
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){
+ const aiVector3D pos = mTrafo* mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.y - min.y) / diff;
+ uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+ }
+ }
+
+ // Now find and remove UV seams. A seam occurs if a face has a tcoord
+ // close to zero on the one side, and a tcoord close to one on the
+ // other side.
+ RemoveUVSeams(mesh,out);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
+{
+ ai_real diffu,diffv;
+ aiVector3D center, min, max;
+
+ // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
+ // currently the mapping axis will always be one of x,y,z, except if the
+ // PretransformVertices step is used (it transforms the meshes into worldspace,
+ // thus changing the mapping axis)
+ if (axis * base_axis_x >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ diffu = max.z - min.z;
+ diffv = max.y - min.y;
+
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv,0.0);
+ }
+ }
+ else if (axis * base_axis_y >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ diffu = max.x - min.x;
+ diffv = max.z - min.z;
+
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
+ }
+ }
+ else if (axis * base_axis_z >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ diffu = max.y - min.y;
+ diffv = max.z - min.z;
+
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv,0.0);
+ }
+ }
+ // slower code path in case the mapping axis is not one of the coordinate system axes
+ else
+ {
+ aiMatrix4x4 mTrafo;
+ aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
+ FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
+ diffu = max.x - min.x;
+ diffv = max.z - min.z;
+
+ // again the same, except we're applying a transformation now
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
+ out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
+ }
+ }
+
+ // shouldn't be necessary to remove UV seams ...
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* )
+{
+ ASSIMP_LOG_ERROR("Mapping type currently not implemented");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin");
+ char buffer[1024];
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
+ throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
+
+ std::list<MappingInfo> mappingStack;
+
+ /* Iterate through all materials and search for non-UV mapped textures
+ */
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ {
+ mappingStack.clear();
+ aiMaterial* mat = pScene->mMaterials[i];
+ for (unsigned int a = 0; a < mat->mNumProperties;++a)
+ {
+ aiMaterialProperty* prop = mat->mProperties[a];
+ if (!::strcmp( prop->mKey.data, "$tex.mapping"))
+ {
+ aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData);
+ if (aiTextureMapping_UV != mapping)
+ {
+ if (!DefaultLogger::isNullLogger())
+ {
+ ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s",
+ TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
+ MappingTypeToString(mapping));
+
+ ASSIMP_LOG_INFO(buffer);
+ }
+
+ if (aiTextureMapping_OTHER == mapping)
+ continue;
+
+ MappingInfo info (mapping);
+
+ // Get further properties - currently only the major axis
+ for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
+ {
+ aiMaterialProperty* prop2 = mat->mProperties[a2];
+ if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
+ continue;
+
+ if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) {
+ info.axis = *((aiVector3D*)prop2->mData);
+ break;
+ }
+ }
+
+ unsigned int idx( 99999999 );
+
+ // Check whether we have this mapping mode already
+ std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info);
+ if (mappingStack.end() != it)
+ {
+ idx = (*it).uv;
+ }
+ else
+ {
+ /* We have found a non-UV mapped texture. Now
+ * we need to find all meshes using this material
+ * that we can compute UV channels for them.
+ */
+ for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
+ {
+ aiMesh* mesh = pScene->mMeshes[m];
+ unsigned int outIdx = 0;
+ if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX ||
+ !mesh->mNumVertices)
+ {
+ continue;
+ }
+
+ // Allocate output storage
+ aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
+
+ switch (mapping)
+ {
+ case aiTextureMapping_SPHERE:
+ ComputeSphereMapping(mesh,info.axis,p);
+ break;
+ case aiTextureMapping_CYLINDER:
+ ComputeCylinderMapping(mesh,info.axis,p);
+ break;
+ case aiTextureMapping_PLANE:
+ ComputePlaneMapping(mesh,info.axis,p);
+ break;
+ case aiTextureMapping_BOX:
+ ComputeBoxMapping(mesh,p);
+ break;
+ default:
+ ai_assert(false);
+ }
+ if (m && idx != outIdx)
+ {
+ ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to "
+ "this material have equal numbers of UV channels. The UV index stored in "
+ "the material structure does therefore not apply for all meshes. ");
+ }
+ idx = outIdx;
+ }
+ info.uv = idx;
+ mappingStack.push_back(info);
+ }
+
+ // Update the material property list
+ mapping = aiTextureMapping_UV;
+ ((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
+ }
+ }
+ }
+ }
+ ASSIMP_LOG_DEBUG("GenUVCoordsProcess finished");
+}
diff --git a/thirdparty/assimp/code/ComputeUVMappingProcess.h b/thirdparty/assimp/code/ComputeUVMappingProcess.h
new file mode 100644
index 0000000000..24f6bb7218
--- /dev/null
+++ b/thirdparty/assimp/code/ComputeUVMappingProcess.h
@@ -0,0 +1,148 @@
+/*
+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 compute UV coordinates
+ from abstract mappings, such as box or spherical*/
+#ifndef AI_COMPUTEUVMAPPING_H_INC
+#define AI_COMPUTEUVMAPPING_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+#include <assimp/material.h>
+#include <assimp/types.h>
+
+class ComputeUVMappingTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** ComputeUVMappingProcess - converts special mappings, such as spherical,
+ * cylindrical or boxed to proper UV coordinates for rendering.
+*/
+class ComputeUVMappingProcess : public BaseProcess
+{
+public:
+ ComputeUVMappingProcess();
+ ~ComputeUVMappingProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Computes spherical UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param axis Main axis
+ * @param out Receives output UV coordinates
+ */
+ void ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis,
+ aiVector3D* out);
+
+ // -------------------------------------------------------------------
+ /** Computes cylindrical UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param axis Main axis
+ * @param out Receives output UV coordinates
+ */
+ void ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis,
+ aiVector3D* out);
+
+ // -------------------------------------------------------------------
+ /** Computes planar UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param axis Main axis
+ * @param out Receives output UV coordinates
+ */
+ void ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis,
+ aiVector3D* out);
+
+ // -------------------------------------------------------------------
+ /** Computes cubic UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param out Receives output UV coordinates
+ */
+ void ComputeBoxMapping(aiMesh* mesh, aiVector3D* out);
+
+private:
+
+ // temporary structure to describe a mapping
+ struct MappingInfo
+ {
+ explicit MappingInfo(aiTextureMapping _type)
+ : type (_type)
+ , axis (0.f,1.f,0.f)
+ , uv (0u)
+ {}
+
+ aiTextureMapping type;
+ aiVector3D axis;
+ unsigned int uv;
+
+ bool operator== (const MappingInfo& other)
+ {
+ return type == other.type && axis == other.axis;
+ }
+ };
+};
+
+} // end of namespace Assimp
+
+#endif // AI_COMPUTEUVMAPPING_H_INC
diff --git a/thirdparty/assimp/code/ConvertToLHProcess.cpp b/thirdparty/assimp/code/ConvertToLHProcess.cpp
new file mode 100644
index 0000000000..b7cd4f0bc6
--- /dev/null
+++ b/thirdparty/assimp/code/ConvertToLHProcess.cpp
@@ -0,0 +1,414 @@
+/*
+---------------------------------------------------------------------------
+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 MakeLeftHandedProcess.cpp
+ * @brief Implementation of the post processing step to convert all
+ * imported data to a left-handed coordinate system.
+ *
+ * Face order & UV flip are also implemented here, for the sake of a
+ * better location.
+ */
+
+
+#include "ConvertToLHProcess.h"
+#include <assimp/scene.h>
+#include <assimp/postprocess.h>
+#include <assimp/DefaultLogger.hpp>
+
+using namespace Assimp;
+
+#ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
+
+namespace {
+
+template <typename aiMeshType>
+void flipUVs(aiMeshType* pMesh) {
+ if (pMesh == nullptr) { return; }
+ // mirror texture y coordinate
+ for (unsigned int tcIdx = 0; tcIdx < AI_MAX_NUMBER_OF_TEXTURECOORDS; tcIdx++) {
+ if (!pMesh->HasTextureCoords(tcIdx)) {
+ break;
+ }
+
+ for (unsigned int vIdx = 0; vIdx < pMesh->mNumVertices; vIdx++) {
+ pMesh->mTextureCoords[tcIdx][vIdx].y = 1.0f - pMesh->mTextureCoords[tcIdx][vIdx].y;
+ }
+ }
+}
+
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MakeLeftHandedProcess::MakeLeftHandedProcess()
+: BaseProcess() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MakeLeftHandedProcess::~MakeLeftHandedProcess() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_MakeLeftHanded);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void MakeLeftHandedProcess::Execute( aiScene* pScene)
+{
+ // Check for an existent root node to proceed
+ ai_assert(pScene->mRootNode != NULL);
+ ASSIMP_LOG_DEBUG("MakeLeftHandedProcess begin");
+
+ // recursively convert all the nodes
+ ProcessNode( pScene->mRootNode, aiMatrix4x4());
+
+ // process the meshes accordingly
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
+ ProcessMesh( pScene->mMeshes[ a ] );
+ }
+
+ // process the materials accordingly
+ for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) {
+ ProcessMaterial( pScene->mMaterials[ a ] );
+ }
+
+ // transform all animation channels as well
+ for( unsigned int a = 0; a < pScene->mNumAnimations; a++)
+ {
+ aiAnimation* anim = pScene->mAnimations[a];
+ for( unsigned int b = 0; b < anim->mNumChannels; b++)
+ {
+ aiNodeAnim* nodeAnim = anim->mChannels[b];
+ ProcessAnimation( nodeAnim);
+ }
+ }
+ ASSIMP_LOG_DEBUG("MakeLeftHandedProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively converts a node, all of its children and all of its meshes
+void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation)
+{
+ // mirror all base vectors at the local Z axis
+ pNode->mTransformation.c1 = -pNode->mTransformation.c1;
+ pNode->mTransformation.c2 = -pNode->mTransformation.c2;
+ pNode->mTransformation.c3 = -pNode->mTransformation.c3;
+ pNode->mTransformation.c4 = -pNode->mTransformation.c4;
+
+ // now invert the Z axis again to keep the matrix determinant positive.
+ // The local meshes will be inverted accordingly so that the result should look just fine again.
+ pNode->mTransformation.a3 = -pNode->mTransformation.a3;
+ pNode->mTransformation.b3 = -pNode->mTransformation.b3;
+ pNode->mTransformation.c3 = -pNode->mTransformation.c3;
+ pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways...
+
+ // continue for all children
+ for( size_t a = 0; a < pNode->mNumChildren; ++a ) {
+ ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single mesh to left handed coordinates.
+void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) {
+ if ( nullptr == pMesh ) {
+ ASSIMP_LOG_ERROR( "Nullptr to mesh found." );
+ return;
+ }
+ // mirror positions, normals and stuff along the Z axis
+ for( size_t a = 0; a < pMesh->mNumVertices; ++a)
+ {
+ pMesh->mVertices[a].z *= -1.0f;
+ if (pMesh->HasNormals()) {
+ pMesh->mNormals[a].z *= -1.0f;
+ }
+ if( pMesh->HasTangentsAndBitangents())
+ {
+ pMesh->mTangents[a].z *= -1.0f;
+ pMesh->mBitangents[a].z *= -1.0f;
+ }
+ }
+
+ // mirror anim meshes positions, normals and stuff along the Z axis
+ for (size_t m = 0; m < pMesh->mNumAnimMeshes; ++m)
+ {
+ for (size_t a = 0; a < pMesh->mAnimMeshes[m]->mNumVertices; ++a)
+ {
+ pMesh->mAnimMeshes[m]->mVertices[a].z *= -1.0f;
+ if (pMesh->mAnimMeshes[m]->HasNormals()) {
+ pMesh->mAnimMeshes[m]->mNormals[a].z *= -1.0f;
+ }
+ if (pMesh->mAnimMeshes[m]->HasTangentsAndBitangents())
+ {
+ pMesh->mAnimMeshes[m]->mTangents[a].z *= -1.0f;
+ pMesh->mAnimMeshes[m]->mBitangents[a].z *= -1.0f;
+ }
+ }
+ }
+
+ // mirror offset matrices of all bones
+ for( size_t a = 0; a < pMesh->mNumBones; ++a)
+ {
+ aiBone* bone = pMesh->mBones[a];
+ bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3;
+ bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3;
+ bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3;
+ bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1;
+ bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2;
+ bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4;
+ }
+
+ // mirror bitangents as well as they're derived from the texture coords
+ if( pMesh->HasTangentsAndBitangents())
+ {
+ for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+ pMesh->mBitangents[a] *= -1.0f;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single material to left handed coordinates.
+void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) {
+ if ( nullptr == _mat ) {
+ ASSIMP_LOG_ERROR( "Nullptr to aiMaterial found." );
+ return;
+ }
+
+ aiMaterial* mat = (aiMaterial*)_mat;
+ for (unsigned int a = 0; a < mat->mNumProperties;++a) {
+ aiMaterialProperty* prop = mat->mProperties[a];
+
+ // Mapping axis for UV mappings?
+ if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) {
+ ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */
+ aiVector3D* pff = (aiVector3D*)prop->mData;
+ pff->z *= -1.f;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the given animation to LH coordinates.
+void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim)
+{
+ // position keys
+ for( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
+ pAnim->mPositionKeys[a].mValue.z *= -1.0f;
+
+ // rotation keys
+ for( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++)
+ {
+ /* That's the safe version, but the float errors add up. So we try the short version instead
+ aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
+ rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3;
+ rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2;
+ aiQuaternion rotquat( rotmat);
+ pAnim->mRotationKeys[a].mValue = rotquat;
+ */
+ pAnim->mRotationKeys[a].mValue.x *= -1.0f;
+ pAnim->mRotationKeys[a].mValue.y *= -1.0f;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
+#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS
+// # FlipUVsProcess
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FlipUVsProcess::FlipUVsProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FlipUVsProcess::~FlipUVsProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FlipUVsProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_FlipUVs);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FlipUVsProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("FlipUVsProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ ProcessMesh(pScene->mMeshes[i]);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ ProcessMaterial(pScene->mMaterials[i]);
+ ASSIMP_LOG_DEBUG("FlipUVsProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single material
+void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
+{
+ aiMaterial* mat = (aiMaterial*)_mat;
+ for (unsigned int a = 0; a < mat->mNumProperties;++a) {
+ aiMaterialProperty* prop = mat->mProperties[a];
+ if( !prop ) {
+ ASSIMP_LOG_DEBUG( "Property is null" );
+ continue;
+ }
+
+ // UV transformation key?
+ if (!::strcmp( prop->mKey.data, "$tex.uvtrafo")) {
+ ai_assert( prop->mDataLength >= sizeof(aiUVTransform)); /* something is wrong with the validation if we end up here */
+ aiUVTransform* uv = (aiUVTransform*)prop->mData;
+
+ // just flip it, that's everything
+ uv->mTranslation.y *= -1.f;
+ uv->mRotation *= -1.f;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single mesh
+void FlipUVsProcess::ProcessMesh( aiMesh* pMesh)
+{
+ flipUVs(pMesh);
+ for (unsigned int idx = 0; idx < pMesh->mNumAnimMeshes; idx++) {
+ flipUVs(pMesh->mAnimMeshes[idx]);
+ }
+}
+
+#endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS
+#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
+// # FlipWindingOrderProcess
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FlipWindingOrderProcess::FlipWindingOrderProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FlipWindingOrderProcess::~FlipWindingOrderProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_FlipWindingOrder);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FlipWindingOrderProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("FlipWindingOrderProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ ProcessMesh(pScene->mMeshes[i]);
+ ASSIMP_LOG_DEBUG("FlipWindingOrderProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single mesh
+void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh)
+{
+ // invert the order of all faces in this mesh
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ aiFace& face = pMesh->mFaces[a];
+ for (unsigned int b = 0; b < face.mNumIndices / 2; b++) {
+ std::swap(face.mIndices[b], face.mIndices[face.mNumIndices - 1 - b]);
+ }
+ }
+
+ // invert the order of all components in this mesh anim meshes
+ for (unsigned int m = 0; m < pMesh->mNumAnimMeshes; m++) {
+ aiAnimMesh* animMesh = pMesh->mAnimMeshes[m];
+ unsigned int numVertices = animMesh->mNumVertices;
+ if (animMesh->HasPositions()) {
+ for (unsigned int a = 0; a < numVertices; a++)
+ {
+ std::swap(animMesh->mVertices[a], animMesh->mVertices[numVertices - 1 - a]);
+ }
+ }
+ if (animMesh->HasNormals()) {
+ for (unsigned int a = 0; a < numVertices; a++)
+ {
+ std::swap(animMesh->mNormals[a], animMesh->mNormals[numVertices - 1 - a]);
+ }
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++) {
+ if (animMesh->HasTextureCoords(i)) {
+ for (unsigned int a = 0; a < numVertices; a++)
+ {
+ std::swap(animMesh->mTextureCoords[i][a], animMesh->mTextureCoords[i][numVertices - 1 - a]);
+ }
+ }
+ }
+ if (animMesh->HasTangentsAndBitangents()) {
+ for (unsigned int a = 0; a < numVertices; a++)
+ {
+ std::swap(animMesh->mTangents[a], animMesh->mTangents[numVertices - 1 - a]);
+ std::swap(animMesh->mBitangents[a], animMesh->mBitangents[numVertices - 1 - a]);
+ }
+ }
+ for (unsigned int v = 0; v < AI_MAX_NUMBER_OF_COLOR_SETS; v++) {
+ if (animMesh->HasVertexColors(v)) {
+ for (unsigned int a = 0; a < numVertices; a++)
+ {
+ std::swap(animMesh->mColors[v][a], animMesh->mColors[v][numVertices - 1 - a]);
+ }
+ }
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
diff --git a/thirdparty/assimp/code/ConvertToLHProcess.h b/thirdparty/assimp/code/ConvertToLHProcess.h
new file mode 100644
index 0000000000..63351568d0
--- /dev/null
+++ b/thirdparty/assimp/code/ConvertToLHProcess.h
@@ -0,0 +1,170 @@
+/*
+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 MakeLeftHandedProcess.h
+ * @brief Defines a bunch of post-processing steps to handle
+ * coordinate system conversions.
+ *
+ * - LH to RH
+ * - UV origin upper-left to lower-left
+ * - face order cw to ccw
+ */
+#ifndef AI_CONVERTTOLHPROCESS_H_INC
+#define AI_CONVERTTOLHPROCESS_H_INC
+
+#include <assimp/types.h>
+#include "BaseProcess.h"
+
+struct aiMesh;
+struct aiNodeAnim;
+struct aiNode;
+struct aiMaterial;
+
+namespace Assimp {
+
+// -----------------------------------------------------------------------------------
+/** @brief The MakeLeftHandedProcess converts all imported data to a left-handed
+ * coordinate system.
+ *
+ * This implies a mirroring of the Z axis of the coordinate system. But to keep
+ * transformation matrices free from reflections we shift the reflection to other
+ * places. We mirror the meshes and adapt the rotations.
+ *
+ * @note RH-LH and LH-RH is the same, so this class can be used for both
+ */
+class MakeLeftHandedProcess : public BaseProcess
+{
+
+
+public:
+ MakeLeftHandedProcess();
+ ~MakeLeftHandedProcess();
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Recursively converts a node and all of its children
+ */
+ void ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation);
+
+ // -------------------------------------------------------------------
+ /** Converts a single mesh to left handed coordinates.
+ * This means that positions, normals and tangents are mirrored at
+ * the local Z axis and the order of all faces are inverted.
+ * @param pMesh The mesh to convert.
+ */
+ void ProcessMesh( aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** Converts a single material to left-handed coordinates
+ * @param pMat Material to convert
+ */
+ void ProcessMaterial( aiMaterial* pMat);
+
+ // -------------------------------------------------------------------
+ /** Converts the given animation to LH coordinates.
+ * The rotation and translation keys are transformed, the scale keys
+ * work in local space and can therefore be left untouched.
+ * @param pAnim The bone animation to transform
+ */
+ void ProcessAnimation( aiNodeAnim* pAnim);
+};
+
+
+// ---------------------------------------------------------------------------
+/** Postprocessing step to flip the face order of the imported data
+ */
+class FlipWindingOrderProcess : public BaseProcess
+{
+ friend class Importer;
+
+public:
+ /** Constructor to be privately used by Importer */
+ FlipWindingOrderProcess();
+
+ /** Destructor, private as well */
+ ~FlipWindingOrderProcess();
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+ void ProcessMesh( aiMesh* pMesh);
+};
+
+// ---------------------------------------------------------------------------
+/** Postprocessing step to flip the UV coordinate system of the import data
+ */
+class FlipUVsProcess : public BaseProcess
+{
+ friend class Importer;
+
+public:
+ /** Constructor to be privately used by Importer */
+ FlipUVsProcess();
+
+ /** Destructor, private as well */
+ ~FlipUVsProcess();
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+ void ProcessMesh( aiMesh* pMesh);
+ void ProcessMaterial( aiMaterial* mat);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CONVERTTOLHPROCESS_H_INC
diff --git a/thirdparty/assimp/code/CreateAnimMesh.cpp b/thirdparty/assimp/code/CreateAnimMesh.cpp
new file mode 100644
index 0000000000..1a052849bb
--- /dev/null
+++ b/thirdparty/assimp/code/CreateAnimMesh.cpp
@@ -0,0 +1,92 @@
+/*
+---------------------------------------------------------------------------
+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->mVertices = NULL;
+ animesh->mNormals = NULL;
+ animesh->mTangents = NULL;
+ animesh->mBitangents = NULL;
+ 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
diff --git a/thirdparty/assimp/code/DeboneProcess.cpp b/thirdparty/assimp/code/DeboneProcess.cpp
new file mode 100644
index 0000000000..83b8336bc9
--- /dev/null
+++ b/thirdparty/assimp/code/DeboneProcess.cpp
@@ -0,0 +1,465 @@
+/*
+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 DeboneProcess.cpp
+/** Implementation of the DeboneProcess post processing step */
+
+
+
+// internal headers of the post-processing framework
+#include "ProcessHelper.h"
+#include "DeboneProcess.h"
+#include <stdio.h>
+
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+DeboneProcess::DeboneProcess()
+{
+ mNumBones = 0;
+ mNumBonesCanDoWithout = 0;
+
+ mThreshold = AI_DEBONE_THRESHOLD;
+ mAllOrNone = false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+DeboneProcess::~DeboneProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool DeboneProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_Debone) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void DeboneProcess::SetupProperties(const Importer* pImp)
+{
+ // get the current value of the property
+ mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false;
+ mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void DeboneProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("DeboneProcess begin");
+
+ if(!pScene->mNumMeshes) {
+ return;
+ }
+
+ std::vector<bool> splitList(pScene->mNumMeshes);
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ splitList[a] = ConsiderMesh( pScene->mMeshes[a] );
+ }
+
+ int numSplits = 0;
+
+ if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) {
+ for(unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ if(splitList[a]) {
+ numSplits++;
+ }
+ }
+ }
+
+ if(numSplits) {
+ // we need to do something. Let's go.
+ //mSubMeshIndices.clear(); // really needed?
+ mSubMeshIndices.resize(pScene->mNumMeshes); // because we're doing it here anyway
+
+ // 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<std::pair<aiMesh*,const aiBone*> > newMeshes;
+
+ if(splitList[a]) {
+ SplitMesh(srcMesh,newMeshes);
+ }
+
+ // mesh was split
+ if(!newMeshes.empty()) {
+ unsigned int out = 0, in = srcMesh->mNumBones;
+
+ // store new meshes and indices of the new meshes
+ for(unsigned int b=0;b<newMeshes.size();b++) {
+ const aiString *find = newMeshes[b].second?&newMeshes[b].second->mName:0;
+
+ aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0;
+ std::pair<unsigned int,aiNode*> push_pair(static_cast<unsigned int>(meshes.size()),theNode);
+
+ mSubMeshIndices[a].push_back(push_pair);
+ meshes.push_back(newMeshes[b].first);
+
+ out+=newMeshes[b].first->mNumBones;
+ }
+
+ if(!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_INFO_F("Removed %u bones. Input bones:", in - out, ". Output bones: ", out);
+ }
+
+ // 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(std::pair<unsigned int,aiNode*>(static_cast<unsigned int>(meshes.size()),(aiNode*)0));
+ 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("DeboneProcess end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts bones total/removable in a given mesh.
+bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
+{
+ if(!pMesh->HasBones()) {
+ return false;
+ }
+
+ bool split = false;
+
+ //interstitial faces not permitted
+ bool isInterstitialRequired = false;
+
+ std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
+ std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
+
+ const unsigned int cUnowned = UINT_MAX;
+ const unsigned int cCoowned = UINT_MAX-1;
+
+ for(unsigned int i=0;i<pMesh->mNumBones;i++) {
+ for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) {
+ float w = pMesh->mBones[i]->mWeights[j].mWeight;
+
+ if(w==0.0f) {
+ continue;
+ }
+
+ unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
+ if(w>=mThreshold) {
+
+ if(vertexBones[vid]!=cUnowned) {
+ if(vertexBones[vid]==i) //double entry
+ {
+ ASSIMP_LOG_WARN("Encountered double entry in bone weights");
+ }
+ else //TODO: track attraction in order to break tie
+ {
+ vertexBones[vid] = cCoowned;
+ }
+ }
+ else vertexBones[vid] = i;
+ }
+
+ if(!isBoneNecessary[i]) {
+ isBoneNecessary[i] = w<mThreshold;
+ }
+ }
+
+ if(!isBoneNecessary[i]) {
+ isInterstitialRequired = true;
+ }
+ }
+
+ if(isInterstitialRequired) {
+ for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+ unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
+
+ for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
+ unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
+
+ if(v!=w) {
+ if(v<pMesh->mNumBones) isBoneNecessary[v] = true;
+ if(w<pMesh->mNumBones) isBoneNecessary[w] = true;
+ }
+ }
+ }
+ }
+
+ for(unsigned int i=0;i<pMesh->mNumBones;i++) {
+ if(!isBoneNecessary[i]) {
+ mNumBonesCanDoWithout++;
+ split = true;
+ }
+
+ mNumBones++;
+ }
+ return split;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Splits the given mesh by bone count.
+void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const
+{
+ // same deal here as ConsiderMesh basically
+
+ std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
+ std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
+
+ const unsigned int cUnowned = UINT_MAX;
+ const unsigned int cCoowned = UINT_MAX-1;
+
+ for(unsigned int i=0;i<pMesh->mNumBones;i++) {
+ for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) {
+ float w = pMesh->mBones[i]->mWeights[j].mWeight;
+
+ if(w==0.0f) {
+ continue;
+ }
+
+ unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
+
+ if(w>=mThreshold) {
+ if(vertexBones[vid]!=cUnowned) {
+ if(vertexBones[vid]==i) //double entry
+ {
+ ASSIMP_LOG_WARN("Encountered double entry in bone weights");
+ }
+ else //TODO: track attraction in order to break tie
+ {
+ vertexBones[vid] = cCoowned;
+ }
+ }
+ else vertexBones[vid] = i;
+ }
+
+ if(!isBoneNecessary[i]) {
+ isBoneNecessary[i] = w<mThreshold;
+ }
+ }
+ }
+
+ unsigned int nFacesUnowned = 0;
+
+ std::vector<unsigned int> faceBones(pMesh->mNumFaces,UINT_MAX);
+ std::vector<unsigned int> facesPerBone(pMesh->mNumBones,0);
+
+ for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+ unsigned int nInterstitial = 1;
+
+ unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
+
+ for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
+ unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
+
+ if(v!=w) {
+ if(v<pMesh->mNumBones) isBoneNecessary[v] = true;
+ if(w<pMesh->mNumBones) isBoneNecessary[w] = true;
+ }
+ else nInterstitial++;
+ }
+
+ if(v<pMesh->mNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices) {
+ faceBones[i] = v; //primitive belongs to bone #v
+ facesPerBone[v]++;
+ }
+ else nFacesUnowned++;
+ }
+
+ // invalidate any "cojoined" faces
+ for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+ if(faceBones[i]<pMesh->mNumBones&&isBoneNecessary[faceBones[i]])
+ {
+ ai_assert(facesPerBone[faceBones[i]]>0);
+ facesPerBone[faceBones[i]]--;
+
+ nFacesUnowned++;
+ faceBones[i] = cUnowned;
+ }
+ }
+
+ if(nFacesUnowned) {
+ std::vector<unsigned int> subFaces;
+
+ for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+ if(faceBones[i]==cUnowned) {
+ subFaces.push_back(i);
+ }
+ }
+
+ aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0);
+ std::pair<aiMesh*,const aiBone*> push_pair(baseMesh,(const aiBone*)0);
+
+ poNewMeshes.push_back(push_pair);
+ }
+
+ for(unsigned int i=0;i<pMesh->mNumBones;i++) {
+
+ if(!isBoneNecessary[i]&&facesPerBone[i]>0) {
+ std::vector<unsigned int> subFaces;
+
+ for(unsigned int j=0;j<pMesh->mNumFaces;j++) {
+ if(faceBones[j]==i) {
+ subFaces.push_back(j);
+ }
+ }
+
+ unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES;
+ aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f);
+
+ //Lifted from PretransformVertices.cpp
+ ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix);
+ std::pair<aiMesh*,const aiBone*> push_pair(subMesh,pMesh->mBones[i]);
+
+ poNewMeshes.push_back(push_pair);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively updates the node's mesh list to account for the changed mesh list
+void DeboneProcess::UpdateNode(aiNode* pNode) const
+{
+ // rebuild the node's mesh index list
+
+ std::vector<unsigned int> newMeshList;
+
+ // this will require two passes
+
+ unsigned int m = static_cast<unsigned int>(pNode->mNumMeshes), n = static_cast<unsigned int>(mSubMeshIndices.size());
+
+ // first pass, look for meshes which have not moved
+
+ for(unsigned int a=0;a<m;a++) {
+
+ unsigned int srcIndex = pNode->mMeshes[a];
+ const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex];
+ unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
+
+ for(unsigned int b=0;b<nSubmeshes;b++) {
+ if(!subMeshes[b].second) {
+ newMeshList.push_back(subMeshes[b].first);
+ }
+ }
+ }
+
+ // second pass, collect deboned meshes
+
+ for(unsigned int a=0;a<n;a++)
+ {
+ const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a];
+ unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
+
+ for(unsigned int b=0;b<nSubmeshes;b++) {
+ if(subMeshes[b].second == pNode) {
+ newMeshList.push_back(subMeshes[b].first);
+ }
+ }
+ }
+
+ if( pNode->mNumMeshes > 0 ) {
+ delete [] pNode->mMeshes; pNode->mMeshes = NULL;
+ }
+
+ pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size());
+
+ if(pNode->mNumMeshes) {
+ 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]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the node transformation to a mesh
+void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const
+{
+ // Check whether we need to transform the coordinates at all
+ if (!mat.IsIdentity()) {
+
+ if (mesh->HasPositions()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mVertices[i] = mat * mesh->mVertices[i];
+ }
+ }
+ if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
+ aiMatrix4x4 mWorldIT = mat;
+ mWorldIT.Inverse().Transpose();
+
+ // TODO: implement Inverse() for aiMatrix3x3
+ aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+ if (mesh->HasNormals()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
+ }
+ }
+ if (mesh->HasTangentsAndBitangents()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
+ mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
+ }
+ }
+ }
+ }
+}
diff --git a/thirdparty/assimp/code/DeboneProcess.h b/thirdparty/assimp/code/DeboneProcess.h
new file mode 100644
index 0000000000..ba77aba70e
--- /dev/null
+++ b/thirdparty/assimp/code/DeboneProcess.h
@@ -0,0 +1,134 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+/** Defines a post processing step to limit the number of bones affecting a single vertex. */
+#ifndef AI_DEBONEPROCESS_H_INC
+#define AI_DEBONEPROCESS_H_INC
+
+#include <vector>
+#include <utility>
+#include "BaseProcess.h"
+
+#include <assimp/mesh.h>
+#include <assimp/scene.h>
+
+class DeboneTest;
+
+namespace Assimp
+{
+
+#if (!defined AI_DEBONE_THRESHOLD)
+# define AI_DEBONE_THRESHOLD 1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------------------------------------------------------------------------
+/** This post processing step removes bones nearly losslessly or according to
+* a configured threshold. In order to remove the bone, the primitives affected by
+* the bone are split from the mesh. The split off (new) mesh is boneless. At any
+* point in time, bones without affect upon a given mesh are to be removed.
+*/
+class DeboneProcess : public BaseProcess
+{
+public:
+
+ DeboneProcess();
+ ~DeboneProcess();
+
+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.
+ */
+ 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);
+
+ // -------------------------------------------------------------------
+ /** Counts bones total/removable in a given mesh.
+ * @param pMesh The mesh to process.
+ */
+ bool ConsiderMesh( const aiMesh* pMesh);
+
+ /// 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< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const;
+
+ /// Recursively updates the node's mesh list to account for the changed mesh list
+ void UpdateNode(aiNode* pNode) const;
+
+ // -------------------------------------------------------------------
+ // Apply transformation to a mesh
+ void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const;
+
+public:
+ /** Number of bones present in the scene. */
+ unsigned int mNumBones;
+ unsigned int mNumBonesCanDoWithout;
+
+ float mThreshold;
+ bool mAllOrNone;
+
+ /// Per mesh index: Array of indices of the new submeshes.
+ std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_DEBONEPROCESS_H_INC
diff --git a/thirdparty/assimp/code/DefaultIOStream.cpp b/thirdparty/assimp/code/DefaultIOStream.cpp
new file mode 100644
index 0000000000..1c100b6189
--- /dev/null
+++ b/thirdparty/assimp/code/DefaultIOStream.cpp
@@ -0,0 +1,154 @@
+/*
+---------------------------------------------------------------------------
+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);
+ }
+}
+
+// ----------------------------------------------------------------------------------
diff --git a/thirdparty/assimp/code/DefaultIOSystem.cpp b/thirdparty/assimp/code/DefaultIOSystem.cpp
new file mode 100644
index 0000000000..d40b67de32
--- /dev/null
+++ b/thirdparty/assimp/code/DefaultIOSystem.cpp
@@ -0,0 +1,257 @@
+/*
+---------------------------------------------------------------------------
+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;
+
+// maximum path length
+// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
+#ifdef PATH_MAX
+# define PATHLIMIT PATH_MAX
+#else
+# define PATHLIMIT 4096
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Tests for the existence of a file at the given path.
+bool DefaultIOSystem::Exists( const char* pFile) const
+{
+#ifdef _WIN32
+ wchar_t fileName16[PATHLIMIT];
+
+#ifndef WindowsStore
+ bool isUnicode = IsTextUnicode(pFile, static_cast<int>(strlen(pFile)), NULL) != 0;
+ if (isUnicode) {
+
+ MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT);
+ struct __stat64 filestat;
+ if (0 != _wstat64(fileName16, &filestat)) {
+ return false;
+ }
+ } else {
+#endif
+ FILE* file = ::fopen(pFile, "rb");
+ if (!file)
+ return false;
+
+ ::fclose(file);
+#ifndef WindowsStore
+ }
+#endif
+#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(NULL != strFile);
+ ai_assert(NULL != strMode);
+ FILE* file;
+#ifdef _WIN32
+ wchar_t fileName16[PATHLIMIT];
+#ifndef WindowsStore
+ bool isUnicode = IsTextUnicode(strFile, static_cast<int>(strlen(strFile)), NULL) != 0;
+ if (isUnicode) {
+ MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT);
+ std::string mode8(strMode);
+ file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str());
+ } else {
+#endif
+ file = ::fopen(strFile, strMode);
+#ifndef WindowsStore
+ }
+#endif
+#else
+ file = ::fopen(strFile, strMode);
+#endif
+ if (nullptr == file)
+ return nullptr;
+
+ return new DefaultIOStream(file, (std::string) 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 void MakeAbsolutePath (const char* in, char* _out)
+{
+ ai_assert(in && _out);
+#if defined( _MSC_VER ) || defined( __MINGW32__ )
+#ifndef WindowsStore
+ bool isUnicode = IsTextUnicode(in, static_cast<int>(strlen(in)), NULL) != 0;
+ if (isUnicode) {
+ wchar_t out16[PATHLIMIT];
+ wchar_t in16[PATHLIMIT];
+ MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT);
+ wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT);
+ if (ret) {
+ WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr);
+ }
+ if (!ret) {
+ // preserve the input path, maybe someone else is able to fix
+ // the path before it is accessed (e.g. our file system filter)
+ ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
+ strcpy(_out, in);
+ }
+
+ } else {
+#endif
+ char* ret = :: _fullpath(_out, in, PATHLIMIT);
+ if (!ret) {
+ // preserve the input path, maybe someone else is able to fix
+ // the path before it is accessed (e.g. our file system filter)
+ ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
+ strcpy(_out, in);
+ }
+#ifndef WindowsStore
+ }
+#endif
+#else
+ // use realpath
+ char* ret = realpath(in, _out);
+ if(!ret) {
+ // preserve the input path, maybe someone else is able to fix
+ // the path before it is accessed (e.g. our file system filter)
+ ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
+ strcpy(_out,in);
+ }
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+ char temp1[PATHLIMIT];
+ char temp2[PATHLIMIT];
+
+ MakeAbsolutePath (one, temp1);
+ MakeAbsolutePath (second, temp2);
+
+ 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 != ret.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;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+#undef PATHLIMIT
diff --git a/thirdparty/assimp/code/DefaultLogger.cpp b/thirdparty/assimp/code/DefaultLogger.cpp
new file mode 100644
index 0000000000..de3528d2b4
--- /dev/null
+++ b/thirdparty/assimp/code/DefaultLogger.cpp
@@ -0,0 +1,418 @@
+/*
+---------------------------------------------------------------------------
+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
diff --git a/thirdparty/assimp/code/DefaultProgressHandler.h b/thirdparty/assimp/code/DefaultProgressHandler.h
new file mode 100644
index 0000000000..bd2cce00be
--- /dev/null
+++ b/thirdparty/assimp/code/DefaultProgressHandler.h
@@ -0,0 +1,65 @@
+/*
+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
diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.cpp b/thirdparty/assimp/code/DropFaceNormalsProcess.cpp
new file mode 100644
index 0000000000..b11615bb82
--- /dev/null
+++ b/thirdparty/assimp/code/DropFaceNormalsProcess.cpp
@@ -0,0 +1,109 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to drop face
+* normals for all imported faces.
+*/
+
+
+#include "DropFaceNormalsProcess.h"
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Exceptional.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+DropFaceNormalsProcess::DropFaceNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+DropFaceNormalsProcess::~DropFaceNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool DropFaceNormalsProcess::IsActive( unsigned int pFlags) const {
+ return (pFlags & aiProcess_DropNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void DropFaceNormalsProcess::Execute( aiScene* pScene) {
+ ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin");
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
+ throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
+ }
+
+ bool bHas = false;
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]);
+ }
+ if (bHas) {
+ ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. "
+ "Face normals have been removed");
+ } else {
+ ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. "
+ "No normals were present");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* pMesh) {
+ if (NULL == pMesh->mNormals) {
+ return false;
+ }
+
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = nullptr;
+ return true;
+}
diff --git a/thirdparty/assimp/code/DropFaceNormalsProcess.h b/thirdparty/assimp/code/DropFaceNormalsProcess.h
new file mode 100644
index 0000000000..0d116663b7
--- /dev/null
+++ b/thirdparty/assimp/code/DropFaceNormalsProcess.h
@@ -0,0 +1,86 @@
+/*
+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 compute face normals for all loaded faces*/
+#ifndef AI_DROPFACENORMALPROCESS_H_INC
+#define AI_DROPFACENORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The DropFaceNormalsProcess computes face normals for all faces of all meshes
+*/
+class ASSIMP_API_WINONLY DropFaceNormalsProcess : public BaseProcess
+{
+public:
+
+ DropFaceNormalsProcess();
+ ~DropFaceNormalsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+private:
+ bool DropMeshFaceNormals(aiMesh* pcMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_DROPFACENORMALPROCESS_H_INC
diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.cpp b/thirdparty/assimp/code/EmbedTexturesProcess.cpp
new file mode 100644
index 0000000000..739382a057
--- /dev/null
+++ b/thirdparty/assimp/code/EmbedTexturesProcess.cpp
@@ -0,0 +1,152 @@
+/*
+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 "EmbedTexturesProcess.h"
+#include <assimp/ParsingUtils.h>
+#include "ProcessHelper.h"
+
+#include <fstream>
+
+using namespace Assimp;
+
+EmbedTexturesProcess::EmbedTexturesProcess()
+: BaseProcess() {
+}
+
+EmbedTexturesProcess::~EmbedTexturesProcess() {
+}
+
+bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const {
+ return (pFlags & aiProcess_EmbedTextures) != 0;
+}
+
+void EmbedTexturesProcess::SetupProperties(const Importer* pImp) {
+ mRootPath = pImp->GetPropertyString("sourceFilePath");
+ mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u);
+}
+
+void EmbedTexturesProcess::Execute(aiScene* pScene) {
+ if (pScene == nullptr || pScene->mRootNode == nullptr) return;
+
+ aiString path;
+
+ uint32_t embeddedTexturesCount = 0u;
+
+ for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) {
+ auto material = pScene->mMaterials[matId];
+
+ for (auto ttId = 1u; ttId < AI_TEXTURE_TYPE_MAX; ++ttId) {
+ auto tt = static_cast<aiTextureType>(ttId);
+ auto texturesCount = material->GetTextureCount(tt);
+
+ for (auto texId = 0u; texId < texturesCount; ++texId) {
+ material->GetTexture(tt, texId, &path);
+ if (path.data[0] == '*') continue; // Already embedded
+
+ // Indeed embed
+ if (addTexture(pScene, path.data)) {
+ auto embeddedTextureId = pScene->mNumTextures - 1u;
+ ::ai_snprintf(path.data, 1024, "*%u", embeddedTextureId);
+ material->AddProperty(&path, AI_MATKEY_TEXTURE(tt, texId));
+ embeddedTexturesCount++;
+ }
+ }
+ }
+ }
+
+ ASSIMP_LOG_INFO_F("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." );
+}
+
+bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
+ std::streampos imageSize = 0;
+ std::string imagePath = path;
+
+ // Test path directly
+ std::ifstream file(imagePath, std::ios::binary | std::ios::ate);
+ if ((imageSize = file.tellg()) == std::streampos(-1)) {
+ ASSIMP_LOG_WARN_F("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder.");
+
+ // Test path in root path
+ imagePath = mRootPath + path;
+ file.open(imagePath, std::ios::binary | std::ios::ate);
+ if ((imageSize = file.tellg()) == std::streampos(-1)) {
+ // Test path basename in root path
+ imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u);
+ file.open(imagePath, std::ios::binary | std::ios::ate);
+ if ((imageSize = file.tellg()) == std::streampos(-1)) {
+ ASSIMP_LOG_ERROR_F("EmbedTexturesProcess: Unable to embed texture: ", path, ".");
+ return false;
+ }
+ }
+ }
+
+ aiTexel* imageContent = new aiTexel[ 1ul + static_cast<unsigned long>( imageSize ) / sizeof(aiTexel)];
+ file.seekg(0, std::ios::beg);
+ file.read(reinterpret_cast<char*>(imageContent), imageSize);
+
+ // Enlarging the textures table
+ unsigned int textureId = pScene->mNumTextures++;
+ auto oldTextures = pScene->mTextures;
+ pScene->mTextures = new aiTexture*[pScene->mNumTextures];
+ ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u));
+
+ // Add the new texture
+ auto pTexture = new aiTexture;
+ pTexture->mHeight = 0; // Means that this is still compressed
+ pTexture->mWidth = static_cast<uint32_t>(imageSize);
+ pTexture->pcData = imageContent;
+
+ auto extension = path.substr(path.find_last_of('.') + 1u);
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if (extension == "jpeg") {
+ extension = "jpg";
+ }
+
+ size_t len = extension.size();
+ if (len > HINTMAXTEXTURELEN -1 ) {
+ len = HINTMAXTEXTURELEN - 1;
+ }
+ ::strncpy(pTexture->achFormatHint, extension.c_str(), len);
+ pScene->mTextures[textureId] = pTexture;
+
+ return true;
+}
diff --git a/thirdparty/assimp/code/EmbedTexturesProcess.h b/thirdparty/assimp/code/EmbedTexturesProcess.h
new file mode 100644
index 0000000000..cdf40bef74
--- /dev/null
+++ b/thirdparty/assimp/code/EmbedTexturesProcess.h
@@ -0,0 +1,85 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#pragma once
+
+#include "BaseProcess.h"
+
+#include <string>
+
+struct aiNode;
+
+namespace Assimp {
+
+/**
+ * Force embedding of textures (using the path = "*1" convention).
+ * If a texture's file does not exist at the specified path
+ * (due, for instance, to an absolute path generated on another system),
+ * it will check if a file with the same name exists at the root folder
+ * of the imported model. And if so, it uses that.
+ */
+class ASSIMP_API EmbedTexturesProcess : public BaseProcess {
+public:
+ /// The default class constructor.
+ EmbedTexturesProcess();
+
+ /// The class destructor.
+ virtual ~EmbedTexturesProcess();
+
+ /// Overwritten, @see BaseProcess
+ virtual bool IsActive(unsigned int pFlags) const;
+
+ /// Overwritten, @see BaseProcess
+ virtual void SetupProperties(const Importer* pImp);
+
+ /// Overwritten, @see BaseProcess
+ virtual void Execute(aiScene* pScene);
+
+private:
+ // Resolve the path and add the file content to the scene as a texture.
+ bool addTexture(aiScene* pScene, std::string path) const;
+
+private:
+ std::string mRootPath;
+};
+
+} // namespace Assimp
diff --git a/thirdparty/assimp/code/Exporter.cpp b/thirdparty/assimp/code/Exporter.cpp
new file mode 100644
index 0000000000..8848e87f5b
--- /dev/null
+++ b/thirdparty/assimp/code/Exporter.cpp
@@ -0,0 +1,648 @@
+/*
+---------------------------------------------------------------------------
+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 "DefaultProgressHandler.h"
+#include "BaseProcess.h"
+#include "JoinVerticesProcess.h"
+#include "MakeVerboseFormat.h"
+#include "ConvertToLHProcess.h"
+#include "PretransformVertices.h"
+#include <assimp/Exceptional.h>
+#include "ScenePrivate.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* );
+
+// ------------------------------------------------------------------------------------------------
+// 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", "assbin" , &ExportSceneAssbin, 0 ),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
+ Exporter::ExportFormatEntry( "assxml", "Assxml 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_3MF_EXPORTER
+ Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 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, 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())) {
+ pimpl->mIOSystem = old;
+ return nullptr;
+ }
+
+ pimpl->blob = blobio->GetBlobChain();
+ pimpl->mIOSystem = old;
+
+ return pimpl->blob;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool IsVerboseFormat(const aiMesh* mesh) {
+ // avoid slow vector<bool> specialization
+ std::vector<unsigned int> seen(mesh->mNumVertices,0);
+ for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+ const aiFace& f = mesh->mFaces[i];
+ for(unsigned int j = 0; j < f.mNumIndices; ++j) {
+ if(++seen[f.mIndices[j]] == 2) {
+ // found a duplicate index
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool IsVerboseFormat(const aiScene* pScene) {
+ for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ if(!IsVerboseFormat(pScene->mMeshes[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+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) || 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.
+ exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties);
+
+ 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
diff --git a/thirdparty/assimp/code/FBXAnimation.cpp b/thirdparty/assimp/code/FBXAnimation.cpp
new file mode 100644
index 0000000000..874914431b
--- /dev/null
+++ b/thirdparty/assimp/code/FBXAnimation.cpp
@@ -0,0 +1,305 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXDocumentUtil.h"
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/)
+: Object(id, element, name)
+{
+ const Scope& sc = GetRequiredScope(element);
+ const Element& KeyTime = GetRequiredElement(sc,"KeyTime");
+ const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat");
+
+ 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);
+ }
+
+ // check if the key times are well-ordered
+ if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less<KeyTimeList::value_type>())) {
+ DOMError("the keyframes are not in ascending order",&KeyTime);
+ }
+
+ const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"];
+ if(KeyAttrDataFloat) {
+ ParseVectorDataArray(attributes, *KeyAttrDataFloat);
+ }
+
+ const Element* KeyAttrFlags = sc["KeyAttrFlags"];
+ if(KeyAttrFlags) {
+ ParseVectorDataArray(flags, *KeyAttrFlags);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::~AnimationCurve()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name,
+ const Document& doc, const char* const * target_prop_whitelist /*= NULL*/,
+ size_t whitelist_size /*= 0*/)
+: Object(id, element, name)
+, target()
+, doc(doc)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ // 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;
+ }
+
+ if(target_prop_whitelist) {
+ const char* const s = con->PropertyName().c_str();
+ bool ok = false;
+ for (size_t i = 0; i < whitelist_size; ++i) {
+ if (!strcmp(s, target_prop_whitelist[i])) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ throw std::range_error("AnimationCurveNode target property is not in whitelist");
+ }
+ }
+
+ const Object* const ob = con->DestinationObject();
+ if(!ob) {
+ DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element);
+ continue;
+ }
+
+ // XXX support constraints as DOM class
+ //ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob));
+ target = ob;
+ if(!target) {
+ continue;
+ }
+
+ prop = con->PropertyName();
+ break;
+ }
+
+ if(!target) {
+ DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element);
+ }
+
+ props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false);
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::~AnimationCurveNode()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const AnimationCurveMap& AnimationCurveNode::Curves() const
+{
+ if ( curves.empty() ) {
+ // resolve attached animation curves
+ const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
+
+ for(const Connection* con : conns) {
+
+ // link should go for a property
+ if (!con->PropertyName().length()) {
+ continue;
+ }
+
+ const Object* const ob = con->SourceObject();
+ if(!ob) {
+ DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element);
+ continue;
+ }
+
+ const AnimationCurve* const anim = dynamic_cast<const AnimationCurve*>(ob);
+ if(!anim) {
+ DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element);
+ continue;
+ }
+
+ curves[con->PropertyName()] = anim;
+ }
+ }
+
+ return curves;
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+: Object(id, element, name)
+, doc(doc)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ // note: the props table here bears little importance and is usually absent
+ props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true);
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::~AnimationLayer()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/,
+ 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;
+ }
+
+ const Object* const ob = con->SourceObject();
+ if(!ob) {
+ DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element);
+ continue;
+ }
+
+ const AnimationCurveNode* const anim = dynamic_cast<const AnimationCurveNode*>(ob);
+ if(!anim) {
+ DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode",&element);
+ 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; // pray for NRVO
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+: Object(id, element, name)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ // note: we don't currently use any of these properties so we shouldn't bother if it is missing
+ props = GetPropertyTable(doc,"AnimationStack.FbxAnimStack",element,sc, true);
+
+ // 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;
+ }
+
+ const Object* const ob = con->SourceObject();
+ if(!ob) {
+ DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element);
+ continue;
+ }
+
+ const AnimationLayer* const anim = dynamic_cast<const AnimationLayer*>(ob);
+ if(!anim) {
+ DOMWarning("source object for ->AnimationStack link is not an AnimationLayer",&element);
+ continue;
+ }
+ layers.push_back(anim);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::~AnimationStack()
+{
+ // empty
+}
+
+} //!FBX
+} //!Assimp
+
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER
diff --git a/thirdparty/assimp/code/FBXBinaryTokenizer.cpp b/thirdparty/assimp/code/FBXBinaryTokenizer.cpp
new file mode 100644
index 0000000000..7138df4315
--- /dev/null
+++ b/thirdparty/assimp/code/FBXBinaryTokenizer.cpp
@@ -0,0 +1,466 @@
+/*
+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.
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXTokenizer.h"
+#include "FBXUtil.h"
+#include <assimp/defs.h>
+#include <stdint.h>
+#include <assimp/Exceptional.h>
+#include <assimp/ByteSwapper.h>
+
+namespace Assimp {
+namespace FBX {
+
+//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, unsigned int offset)
+ :
+ #ifdef DEBUG
+ contents(sbegin, static_cast<size_t>(send-sbegin)),
+ #endif
+ sbegin(sbegin)
+ , send(send)
+ , type(type)
+ , line(offset)
+ , column(BINARY_MARKER)
+{
+ ai_assert(sbegin);
+ ai_assert(send);
+
+ // binary tokens may have zero length because they are sometimes dummies
+ // inserted by TokenizeBinary()
+ ai_assert(send >= sbegin);
+}
+
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) AI_WONT_RETURN_SUFFIX;
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset)
+{
+ throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+uint32_t Offset(const char* begin, const char* cursor) {
+ ai_assert(begin <= cursor);
+
+ return static_cast<unsigned int>(cursor - begin);
+}
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string& message, const char* begin, const char* cursor) {
+ 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:
+ ai_assert(false);
+ };
+ 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, unsigned int length)
+{
+ ai_assert(input);
+
+ 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);
+ const bool is64bits = version >= 7500;
+ const char *end = input + length;
+ while (cursor < end ) {
+ if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
+ break;
+ }
+ }
+}
+
+} // !FBX
+} // !Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXCommon.h b/thirdparty/assimp/code/FBXCommon.h
new file mode 100644
index 0000000000..fcb20a5cad
--- /dev/null
+++ b/thirdparty/assimp/code/FBXCommon.h
@@ -0,0 +1,86 @@
+/*
+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 AI_FBXCOMMON_H_INC
+#define AI_FBXCOMMON_H_INC
+
+#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
+
+
+namespace FBX
+{
+ const std::string NULL_RECORD = { // 13 null bytes
+ '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'
+ }; // who knows why
+ const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings
+ const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
+ const int64_t SECOND = 46186158000; // FBX's kTime unit
+
+ // rotation order. We'll probably use EulerXYZ for everything
+ enum RotOrder {
+ RotOrder_EulerXYZ = 0,
+ RotOrder_EulerXZY,
+ RotOrder_EulerYZX,
+ RotOrder_EulerYXZ,
+ RotOrder_EulerZXY,
+ RotOrder_EulerZYX,
+
+ RotOrder_SphericXYZ,
+
+ RotOrder_MAX // end-of-enum sentinel
+ };
+
+ // transformation inheritance method. Most of the time RSrs
+ enum TransformInheritance {
+ TransformInheritance_RrSs = 0,
+ TransformInheritance_RSrs,
+ TransformInheritance_Rrs,
+
+ TransformInheritance_MAX // end-of-enum sentinel
+ };
+}
+
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#endif // AI_FBXCOMMON_H_INC
diff --git a/thirdparty/assimp/code/FBXCompileConfig.h b/thirdparty/assimp/code/FBXCompileConfig.h
new file mode 100644
index 0000000000..3a3841fa5b
--- /dev/null
+++ b/thirdparty/assimp/code/FBXCompileConfig.h
@@ -0,0 +1,70 @@
+/*
+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 FBXCompileConfig.h
+ * @brief FBX importer compile-time switches
+ */
+#ifndef INCLUDED_AI_FBX_COMPILECONFIG_H
+#define INCLUDED_AI_FBX_COMPILECONFIG_H
+
+#include <map>
+
+//
+#if _MSC_VER > 1500 || (defined __GNUC___)
+# define ASSIMP_FBX_USE_UNORDERED_MULTIMAP
+# else
+# define fbx_unordered_map map
+# define fbx_unordered_multimap multimap
+#endif
+
+#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
+# include <unordered_map>
+# if _MSC_VER > 1600
+# define fbx_unordered_map unordered_map
+# define fbx_unordered_multimap unordered_multimap
+# else
+# define fbx_unordered_map tr1::unordered_map
+# define fbx_unordered_multimap tr1::unordered_multimap
+# endif
+#endif
+
+#endif // INCLUDED_AI_FBX_COMPILECONFIG_H
diff --git a/thirdparty/assimp/code/FBXConverter.cpp b/thirdparty/assimp/code/FBXConverter.cpp
new file mode 100644
index 0000000000..09ae06a64f
--- /dev/null
+++ b/thirdparty/assimp/code/FBXConverter.cpp
@@ -0,0 +1,3536 @@
+/*
+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 FBXConverter.cpp
+ * @brief Implementation of the FBX DOM -> aiScene converter
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXConverter.h"
+#include "FBXParser.h"
+#include "FBXMeshGeometry.h"
+#include "FBXDocument.h"
+#include "FBXUtil.h"
+#include "FBXProperties.h"
+#include "FBXImporter.h"
+
+#include <assimp/StringComparison.h>
+
+#include <assimp/scene.h>
+
+#include <assimp/CreateAnimMesh.h>
+
+#include <tuple>
+#include <memory>
+#include <iterator>
+#include <vector>
+#include <sstream>
+#include <iomanip>
+
+namespace Assimp {
+ namespace FBX {
+
+ using namespace Util;
+
+#define MAGIC_NODE_TAG "_$AssimpFbx$"
+
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
+
+ FBXConverter::FBXConverter(aiScene* out, const Document& doc)
+ : defaultMaterialIndex()
+ , lights()
+ , cameras()
+ , textures()
+ , materials_converted()
+ , textures_converted()
+ , meshes_converted()
+ , node_anim_chain_bits()
+ , mNodeNameInstances()
+ , mNodeNames()
+ , anim_fps()
+ , out(out)
+ , doc(doc) {
+ // animations need to be converted first since this will
+ // populate the node_anim_chain_bits map, which is needed
+ // to determine which nodes need to be generated.
+ ConvertAnimations();
+ ConvertRootNode();
+
+ if (doc.Settings().readAllMaterials) {
+ // unfortunately this means we have to evaluate all objects
+ for (const ObjectMap::value_type& v : doc.Objects()) {
+
+ const Object* ob = v.second->Get();
+ if (!ob) {
+ continue;
+ }
+
+ const Material* mat = dynamic_cast<const Material*>(ob);
+ if (mat) {
+
+ if (materials_converted.find(mat) == materials_converted.end()) {
+ ConvertMaterial(*mat, 0);
+ }
+ }
+ }
+ }
+
+ ConvertGlobalSettings();
+ TransferDataToScene();
+
+ // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
+ // to make sure the scene passes assimp's validation. FBX files
+ // need not contain geometry (i.e. camera animations, raw armatures).
+ if (out->mNumMeshes == 0) {
+ out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+ }
+
+
+ FBXConverter::~FBXConverter() {
+ std::for_each(meshes.begin(), meshes.end(), Util::delete_fun<aiMesh>());
+ std::for_each(materials.begin(), materials.end(), Util::delete_fun<aiMaterial>());
+ std::for_each(animations.begin(), animations.end(), Util::delete_fun<aiAnimation>());
+ std::for_each(lights.begin(), lights.end(), Util::delete_fun<aiLight>());
+ std::for_each(cameras.begin(), cameras.end(), Util::delete_fun<aiCamera>());
+ std::for_each(textures.begin(), textures.end(), Util::delete_fun<aiTexture>());
+ }
+
+ void FBXConverter::ConvertRootNode() {
+ out->mRootNode = new aiNode();
+ out->mRootNode->mName.Set("RootNode");
+
+ // root has ID 0
+ ConvertNodes(0L, *out->mRootNode);
+ }
+
+ void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) {
+ const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
+
+ std::vector<aiNode*> nodes;
+ nodes.reserve(conns.size());
+
+ std::vector<aiNode*> nodes_chain;
+ std::vector<aiNode*> post_nodes_chain;
+
+ try {
+ for (const Connection* con : conns) {
+
+ // ignore object-property links
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ const Object* const object = con->SourceObject();
+ if (nullptr == object) {
+ FBXImporter::LogWarn("failed to convert source object for Model link");
+ continue;
+ }
+
+ const Model* const model = dynamic_cast<const Model*>(object);
+
+ if (nullptr != model) {
+ nodes_chain.clear();
+ post_nodes_chain.clear();
+
+ aiMatrix4x4 new_abs_transform = parent_transform;
+
+ // even though there is only a single input node, the design of
+ // assimp (or rather: the complicated transformation chain that
+ // is employed by fbx) means that we may need multiple aiNode's
+ // to represent a fbx node's transformation.
+ GenerateTransformationNodeChain(*model, nodes_chain, post_nodes_chain);
+
+ ai_assert(nodes_chain.size());
+
+ std::string original_name = FixNodeName(model->Name());
+
+ // check if any of the nodes in the chain has the name the fbx node
+ // is supposed to have. If there is none, add another node to
+ // preserve the name - people might have scripts etc. that rely
+ // on specific node names.
+ aiNode* name_carrier = NULL;
+ for (aiNode* prenode : nodes_chain) {
+ if (!strcmp(prenode->mName.C_Str(), original_name.c_str())) {
+ name_carrier = prenode;
+ break;
+ }
+ }
+
+ if (!name_carrier) {
+ std::string old_original_name = original_name;
+ GetUniqueName(old_original_name, original_name);
+ nodes_chain.push_back(new aiNode(original_name));
+ }
+ else {
+ original_name = nodes_chain.back()->mName.C_Str();
+ }
+
+ //setup metadata on newest node
+ SetupNodeMetadata(*model, *nodes_chain.back());
+
+ // link all nodes in a row
+ aiNode* last_parent = &parent;
+ for (aiNode* prenode : nodes_chain) {
+ ai_assert(prenode);
+
+ if (last_parent != &parent) {
+ last_parent->mNumChildren = 1;
+ last_parent->mChildren = new aiNode*[1];
+ last_parent->mChildren[0] = prenode;
+ }
+
+ prenode->mParent = last_parent;
+ last_parent = prenode;
+
+ new_abs_transform *= prenode->mTransformation;
+ }
+
+ // attach geometry
+ ConvertModel(*model, *nodes_chain.back(), new_abs_transform);
+
+ // check if there will be any child nodes
+ const std::vector<const Connection*>& child_conns
+ = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
+
+ // if so, link the geometric transform inverse nodes
+ // before we attach any child nodes
+ if (child_conns.size()) {
+ for (aiNode* postnode : post_nodes_chain) {
+ ai_assert(postnode);
+
+ if (last_parent != &parent) {
+ last_parent->mNumChildren = 1;
+ last_parent->mChildren = new aiNode*[1];
+ last_parent->mChildren[0] = postnode;
+ }
+
+ postnode->mParent = last_parent;
+ last_parent = postnode;
+
+ new_abs_transform *= postnode->mTransformation;
+ }
+ }
+ else {
+ // free the nodes we allocated as we don't need them
+ Util::delete_fun<aiNode> deleter;
+ std::for_each(
+ post_nodes_chain.begin(),
+ post_nodes_chain.end(),
+ deleter
+ );
+ }
+
+ // attach sub-nodes (if any)
+ ConvertNodes(model->ID(), *last_parent, new_abs_transform);
+
+ if (doc.Settings().readLights) {
+ ConvertLights(*model, original_name);
+ }
+
+ if (doc.Settings().readCameras) {
+ ConvertCameras(*model, original_name);
+ }
+
+ nodes.push_back(nodes_chain.front());
+ nodes_chain.clear();
+ }
+ }
+
+ if (nodes.size()) {
+ parent.mChildren = new aiNode*[nodes.size()]();
+ parent.mNumChildren = static_cast<unsigned int>(nodes.size());
+
+ std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren);
+ }
+ }
+ catch (std::exception&) {
+ Util::delete_fun<aiNode> deleter;
+ std::for_each(nodes.begin(), nodes.end(), deleter);
+ std::for_each(nodes_chain.begin(), nodes_chain.end(), deleter);
+ std::for_each(post_nodes_chain.begin(), post_nodes_chain.end(), deleter);
+ }
+ }
+
+
+ void FBXConverter::ConvertLights(const Model& model, const std::string &orig_name) {
+ const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
+ for (const NodeAttribute* attr : node_attrs) {
+ const Light* const light = dynamic_cast<const Light*>(attr);
+ if (light) {
+ ConvertLight(*light, orig_name);
+ }
+ }
+ }
+
+ void FBXConverter::ConvertCameras(const Model& model, const std::string &orig_name) {
+ const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
+ for (const NodeAttribute* attr : node_attrs) {
+ const Camera* const cam = dynamic_cast<const Camera*>(attr);
+ if (cam) {
+ ConvertCamera(*cam, orig_name);
+ }
+ }
+ }
+
+ void FBXConverter::ConvertLight(const Light& light, const std::string &orig_name) {
+ lights.push_back(new aiLight());
+ aiLight* const out_light = lights.back();
+
+ out_light->mName.Set(orig_name);
+
+ const float intensity = light.Intensity() / 100.0f;
+ const aiVector3D& col = light.Color();
+
+ out_light->mColorDiffuse = aiColor3D(col.x, col.y, col.z);
+ out_light->mColorDiffuse.r *= intensity;
+ out_light->mColorDiffuse.g *= intensity;
+ out_light->mColorDiffuse.b *= intensity;
+
+ out_light->mColorSpecular = out_light->mColorDiffuse;
+
+ //lights are defined along negative y direction
+ out_light->mPosition = aiVector3D(0.0f);
+ out_light->mDirection = aiVector3D(0.0f, -1.0f, 0.0f);
+ out_light->mUp = aiVector3D(0.0f, 0.0f, -1.0f);
+
+ switch (light.LightType())
+ {
+ case Light::Type_Point:
+ out_light->mType = aiLightSource_POINT;
+ break;
+
+ case Light::Type_Directional:
+ out_light->mType = aiLightSource_DIRECTIONAL;
+ break;
+
+ case Light::Type_Spot:
+ out_light->mType = aiLightSource_SPOT;
+ out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle());
+ out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle());
+ break;
+
+ case Light::Type_Area:
+ FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED");
+ out_light->mType = aiLightSource_UNDEFINED;
+ break;
+
+ case Light::Type_Volume:
+ FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED");
+ out_light->mType = aiLightSource_UNDEFINED;
+ break;
+ default:
+ ai_assert(false);
+ }
+
+ float decay = light.DecayStart();
+ switch (light.DecayType())
+ {
+ case Light::Decay_None:
+ out_light->mAttenuationConstant = decay;
+ out_light->mAttenuationLinear = 0.0f;
+ out_light->mAttenuationQuadratic = 0.0f;
+ break;
+ case Light::Decay_Linear:
+ out_light->mAttenuationConstant = 0.0f;
+ out_light->mAttenuationLinear = 2.0f / decay;
+ out_light->mAttenuationQuadratic = 0.0f;
+ break;
+ case Light::Decay_Quadratic:
+ out_light->mAttenuationConstant = 0.0f;
+ out_light->mAttenuationLinear = 0.0f;
+ out_light->mAttenuationQuadratic = 2.0f / (decay * decay);
+ break;
+ case Light::Decay_Cubic:
+ FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic");
+ out_light->mAttenuationQuadratic = 1.0f;
+ break;
+ default:
+ ai_assert(false);
+ }
+ }
+
+ void FBXConverter::ConvertCamera(const Camera& cam, const std::string &orig_name)
+ {
+ cameras.push_back(new aiCamera());
+ aiCamera* const out_camera = cameras.back();
+
+ out_camera->mName.Set(orig_name);
+
+ out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
+
+ //cameras are defined along positive x direction
+ /*out_camera->mPosition = cam.Position();
+ out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize();
+ out_camera->mUp = cam.UpVector();*/
+
+ out_camera->mPosition = aiVector3D(0.0f);
+ out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
+ out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
+
+ out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
+
+ out_camera->mClipPlaneNear = cam.NearPlane();
+ out_camera->mClipPlaneFar = cam.FarPlane();
+
+ out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
+ out_camera->mClipPlaneNear = cam.NearPlane();
+ out_camera->mClipPlaneFar = cam.FarPlane();
+ }
+
+ void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueName)
+ {
+ uniqueName = name;
+ int i = 0;
+ auto it = mNodeNameInstances.find(name); // duplicate node name instance count
+ if (it != mNodeNameInstances.end())
+ {
+ i = it->second;
+ while (mNodeNames.find(uniqueName) != mNodeNames.end())
+ {
+ i++;
+ std::stringstream ext;
+ ext << name << std::setfill('0') << std::setw(3) << i;
+ uniqueName = ext.str();
+ }
+ }
+ mNodeNameInstances[name] = i;
+ mNodeNames.insert(uniqueName);
+ }
+
+ const char* FBXConverter::NameTransformationComp(TransformationComp comp) {
+ switch (comp) {
+ case TransformationComp_Translation:
+ return "Translation";
+ case TransformationComp_RotationOffset:
+ return "RotationOffset";
+ case TransformationComp_RotationPivot:
+ return "RotationPivot";
+ case TransformationComp_PreRotation:
+ return "PreRotation";
+ case TransformationComp_Rotation:
+ return "Rotation";
+ case TransformationComp_PostRotation:
+ return "PostRotation";
+ case TransformationComp_RotationPivotInverse:
+ return "RotationPivotInverse";
+ case TransformationComp_ScalingOffset:
+ return "ScalingOffset";
+ case TransformationComp_ScalingPivot:
+ return "ScalingPivot";
+ case TransformationComp_Scaling:
+ return "Scaling";
+ case TransformationComp_ScalingPivotInverse:
+ return "ScalingPivotInverse";
+ case TransformationComp_GeometricScaling:
+ return "GeometricScaling";
+ case TransformationComp_GeometricRotation:
+ return "GeometricRotation";
+ case TransformationComp_GeometricTranslation:
+ return "GeometricTranslation";
+ case TransformationComp_GeometricScalingInverse:
+ return "GeometricScalingInverse";
+ case TransformationComp_GeometricRotationInverse:
+ return "GeometricRotationInverse";
+ case TransformationComp_GeometricTranslationInverse:
+ return "GeometricTranslationInverse";
+ case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+ default:
+ break;
+ }
+
+ ai_assert(false);
+
+ return nullptr;
+ }
+
+ const char* FBXConverter::NameTransformationCompProperty(TransformationComp comp) {
+ switch (comp) {
+ case TransformationComp_Translation:
+ return "Lcl Translation";
+ case TransformationComp_RotationOffset:
+ return "RotationOffset";
+ case TransformationComp_RotationPivot:
+ return "RotationPivot";
+ case TransformationComp_PreRotation:
+ return "PreRotation";
+ case TransformationComp_Rotation:
+ return "Lcl Rotation";
+ case TransformationComp_PostRotation:
+ return "PostRotation";
+ case TransformationComp_RotationPivotInverse:
+ return "RotationPivotInverse";
+ case TransformationComp_ScalingOffset:
+ return "ScalingOffset";
+ case TransformationComp_ScalingPivot:
+ return "ScalingPivot";
+ case TransformationComp_Scaling:
+ return "Lcl Scaling";
+ case TransformationComp_ScalingPivotInverse:
+ return "ScalingPivotInverse";
+ case TransformationComp_GeometricScaling:
+ return "GeometricScaling";
+ case TransformationComp_GeometricRotation:
+ return "GeometricRotation";
+ case TransformationComp_GeometricTranslation:
+ return "GeometricTranslation";
+ case TransformationComp_GeometricScalingInverse:
+ return "GeometricScalingInverse";
+ case TransformationComp_GeometricRotationInverse:
+ return "GeometricRotationInverse";
+ case TransformationComp_GeometricTranslationInverse:
+ return "GeometricTranslationInverse";
+ case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+ break;
+ }
+
+ ai_assert(false);
+
+ return nullptr;
+ }
+
+ aiVector3D FBXConverter::TransformationCompDefaultValue(TransformationComp comp)
+ {
+ // XXX a neat way to solve the never-ending special cases for scaling
+ // would be to do everything in log space!
+ return comp == TransformationComp_Scaling ? aiVector3D(1.f, 1.f, 1.f) : aiVector3D();
+ }
+
+ void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out)
+ {
+ if (mode == Model::RotOrder_SphericXYZ) {
+ FBXImporter::LogError("Unsupported RotationMode: SphericXYZ");
+ out = aiMatrix4x4();
+ return;
+ }
+
+ const float angle_epsilon = 1e-6f;
+
+ out = aiMatrix4x4();
+
+ bool is_id[3] = { true, true, true };
+
+ aiMatrix4x4 temp[3];
+ if (std::fabs(rotation.z) > angle_epsilon) {
+ aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z), temp[2]);
+ is_id[2] = false;
+ }
+ if (std::fabs(rotation.y) > angle_epsilon) {
+ aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y), temp[1]);
+ is_id[1] = false;
+ }
+ if (std::fabs(rotation.x) > angle_epsilon) {
+ aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x), temp[0]);
+ is_id[0] = false;
+ }
+
+ int order[3] = { -1, -1, -1 };
+
+ // note: rotation order is inverted since we're left multiplying as is usual in assimp
+ switch (mode)
+ {
+ case Model::RotOrder_EulerXYZ:
+ order[0] = 2;
+ order[1] = 1;
+ order[2] = 0;
+ break;
+
+ case Model::RotOrder_EulerXZY:
+ order[0] = 1;
+ order[1] = 2;
+ order[2] = 0;
+ break;
+
+ case Model::RotOrder_EulerYZX:
+ order[0] = 0;
+ order[1] = 2;
+ order[2] = 1;
+ break;
+
+ case Model::RotOrder_EulerYXZ:
+ order[0] = 2;
+ order[1] = 0;
+ order[2] = 1;
+ break;
+
+ case Model::RotOrder_EulerZXY:
+ order[0] = 1;
+ order[1] = 0;
+ order[2] = 2;
+ break;
+
+ case Model::RotOrder_EulerZYX:
+ order[0] = 0;
+ order[1] = 1;
+ order[2] = 2;
+ break;
+
+ default:
+ ai_assert(false);
+ break;
+ }
+
+ ai_assert(order[0] >= 0);
+ ai_assert(order[0] <= 2);
+ ai_assert(order[1] >= 0);
+ ai_assert(order[1] <= 2);
+ ai_assert(order[2] >= 0);
+ ai_assert(order[2] <= 2);
+
+ if (!is_id[order[0]]) {
+ out = temp[order[0]];
+ }
+
+ if (!is_id[order[1]]) {
+ out = out * temp[order[1]];
+ }
+
+ if (!is_id[order[2]]) {
+ out = out * temp[order[2]];
+ }
+ }
+
+ bool FBXConverter::NeedsComplexTransformationChain(const Model& model)
+ {
+ const PropertyTable& props = model.Props();
+ bool ok;
+
+ const float zero_epsilon = 1e-6f;
+ const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
+ for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+ const TransformationComp comp = static_cast<TransformationComp>(i);
+
+ if (comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation) {
+ continue;
+ }
+
+ bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling);
+
+ const aiVector3D& v = PropertyGet<aiVector3D>(props, NameTransformationCompProperty(comp), ok);
+ if (ok && scale_compare) {
+ if ((v - all_ones).SquareLength() > zero_epsilon) {
+ return true;
+ }
+ }
+ else if (ok) {
+ if (v.SquareLength() > zero_epsilon) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ std::string FBXConverter::NameTransformationChainNode(const std::string& name, TransformationComp comp)
+ {
+ return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
+ }
+
+ void FBXConverter::GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes,
+ std::vector<aiNode*>& post_output_nodes) {
+ const PropertyTable& props = model.Props();
+ const Model::RotOrder rot = model.RotationOrder();
+
+ bool ok;
+
+ aiMatrix4x4 chain[TransformationComp_MAXIMUM];
+ std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
+
+ // generate transformation matrices for all the different transformation components
+ const float zero_epsilon = 1e-6f;
+ const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
+ bool is_complex = false;
+
+ const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
+ if (ok && PreRotation.SquareLength() > zero_epsilon) {
+ is_complex = true;
+
+ GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]);
+ }
+
+ const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
+ if (ok && PostRotation.SquareLength() > zero_epsilon) {
+ is_complex = true;
+
+ GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]);
+ }
+
+ const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok);
+ if (ok && RotationPivot.SquareLength() > zero_epsilon) {
+ is_complex = true;
+
+ aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]);
+ aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]);
+ }
+
+ const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok);
+ if (ok && RotationOffset.SquareLength() > zero_epsilon) {
+ is_complex = true;
+
+ aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]);
+ }
+
+ const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok);
+ if (ok && ScalingOffset.SquareLength() > zero_epsilon) {
+ is_complex = true;
+
+ aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]);
+ }
+
+ const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok);
+ if (ok && ScalingPivot.SquareLength() > zero_epsilon) {
+ is_complex = true;
+
+ aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]);
+ aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]);
+ }
+
+ const aiVector3D& Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok);
+ if (ok && Translation.SquareLength() > zero_epsilon) {
+ aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]);
+ }
+
+ const aiVector3D& Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok);
+ if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) {
+ aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]);
+ }
+
+ const aiVector3D& Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok);
+ if (ok && Rotation.SquareLength() > zero_epsilon) {
+ GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
+ }
+
+ const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
+ if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) {
+ is_complex = true;
+ aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
+ aiVector3D GeometricScalingInverse = GeometricScaling;
+ bool canscale = true;
+ for (unsigned int i = 0; i < 3; ++i) {
+ if (std::fabs(GeometricScalingInverse[i]) > zero_epsilon) {
+ GeometricScalingInverse[i] = 1.0f / GeometricScaling[i];
+ }
+ else {
+ FBXImporter::LogError("cannot invert geometric scaling matrix with a 0.0 scale component");
+ canscale = false;
+ break;
+ }
+ }
+ if (canscale) {
+ aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]);
+ }
+ }
+
+ const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
+ if (ok && GeometricRotation.SquareLength() > zero_epsilon) {
+ is_complex = true;
+ GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]);
+ GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]);
+ chain[TransformationComp_GeometricRotationInverse].Inverse();
+ }
+
+ const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
+ if (ok && GeometricTranslation.SquareLength() > zero_epsilon) {
+ is_complex = true;
+ aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]);
+ aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]);
+ }
+
+ // is_complex needs to be consistent with NeedsComplexTransformationChain()
+ // or the interplay between this code and the animation converter would
+ // not be guaranteed.
+ ai_assert(NeedsComplexTransformationChain(model) == is_complex);
+
+ std::string name = FixNodeName(model.Name());
+
+ // now, if we have more than just Translation, Scaling and Rotation,
+ // we need to generate a full node chain to accommodate for assimp's
+ // lack to express pivots and offsets.
+ if (is_complex && doc.Settings().preservePivots) {
+ FBXImporter::LogInfo("generating full transformation chain for node: " + name);
+
+ // query the anim_chain_bits dictionary to find out which chain elements
+ // have associated node animation channels. These can not be dropped
+ // even if they have identity transform in bind pose.
+ NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name);
+ const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second);
+
+ unsigned int bit = 0x1;
+ for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
+ const TransformationComp comp = static_cast<TransformationComp>(i);
+
+ if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
+ continue;
+ }
+
+ if (comp == TransformationComp_PostRotation) {
+ chain[i] = chain[i].Inverse();
+ }
+
+ aiNode* nd = new aiNode();
+ nd->mName.Set(NameTransformationChainNode(name, comp));
+ nd->mTransformation = chain[i];
+
+ // geometric inverses go in a post-node chain
+ if (comp == TransformationComp_GeometricScalingInverse ||
+ comp == TransformationComp_GeometricRotationInverse ||
+ comp == TransformationComp_GeometricTranslationInverse
+ ) {
+ post_output_nodes.push_back(nd);
+ }
+ else {
+ output_nodes.push_back(nd);
+ }
+ }
+
+ ai_assert(output_nodes.size());
+ return;
+ }
+
+ // else, we can just multiply the matrices together
+ aiNode* nd = new aiNode();
+ output_nodes.push_back(nd);
+ std::string uniqueName;
+ GetUniqueName(name, uniqueName);
+
+ nd->mName.Set(uniqueName);
+
+ for (const auto &transform : chain) {
+ nd->mTransformation = nd->mTransformation * transform;
+ }
+ }
+
+ void FBXConverter::SetupNodeMetadata(const Model& model, aiNode& nd)
+ {
+ const PropertyTable& props = model.Props();
+ DirectPropertyMap unparsedProperties = props.GetUnparsedProperties();
+
+ // create metadata on node
+ const std::size_t numStaticMetaData = 2;
+ aiMetadata* data = aiMetadata::Alloc(static_cast<unsigned int>(unparsedProperties.size() + numStaticMetaData));
+ nd.mMetaData = data;
+ int index = 0;
+
+ // find user defined properties (3ds Max)
+ data->Set(index++, "UserProperties", aiString(PropertyGet<std::string>(props, "UDP3DSMAX", "")));
+ // preserve the info that a node was marked as Null node in the original file.
+ data->Set(index++, "IsNull", model.IsNull() ? true : false);
+
+ // add unparsed properties to the node's metadata
+ for (const DirectPropertyMap::value_type& prop : unparsedProperties) {
+ // Interpret the property as a concrete type
+ if (const TypedProperty<bool>* interpreted = prop.second->As<TypedProperty<bool> >()) {
+ data->Set(index++, prop.first, interpreted->Value());
+ }
+ else if (const TypedProperty<int>* interpreted = prop.second->As<TypedProperty<int> >()) {
+ data->Set(index++, prop.first, interpreted->Value());
+ }
+ else if (const TypedProperty<uint64_t>* interpreted = prop.second->As<TypedProperty<uint64_t> >()) {
+ data->Set(index++, prop.first, interpreted->Value());
+ }
+ else if (const TypedProperty<float>* interpreted = prop.second->As<TypedProperty<float> >()) {
+ data->Set(index++, prop.first, interpreted->Value());
+ }
+ else if (const TypedProperty<std::string>* interpreted = prop.second->As<TypedProperty<std::string> >()) {
+ data->Set(index++, prop.first, aiString(interpreted->Value()));
+ }
+ else if (const TypedProperty<aiVector3D>* interpreted = prop.second->As<TypedProperty<aiVector3D> >()) {
+ data->Set(index++, prop.first, interpreted->Value());
+ }
+ else {
+ ai_assert(false);
+ }
+ }
+ }
+
+ void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform)
+ {
+ const std::vector<const Geometry*>& geos = model.GetGeometry();
+
+ std::vector<unsigned int> meshes;
+ meshes.reserve(geos.size());
+
+ for (const Geometry* geo : geos) {
+
+ const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
+ const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo);
+ if (mesh) {
+ const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform, nd);
+ std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
+ }
+ else if (line) {
+ const std::vector<unsigned int>& indices = ConvertLine(*line, model, node_global_transform, nd);
+ std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
+ }
+ else {
+ FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
+ }
+ }
+
+ if (meshes.size()) {
+ nd.mMeshes = new unsigned int[meshes.size()]();
+ nd.mNumMeshes = static_cast<unsigned int>(meshes.size());
+
+ std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes);
+ }
+ }
+
+ std::vector<unsigned int> FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd)
+ {
+ std::vector<unsigned int> temp;
+
+ MeshMap::const_iterator it = meshes_converted.find(&mesh);
+ if (it != meshes_converted.end()) {
+ std::copy((*it).second.begin(), (*it).second.end(), std::back_inserter(temp));
+ return temp;
+ }
+
+ const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+ const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+ if (vertices.empty() || faces.empty()) {
+ FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
+ return temp;
+ }
+
+ // one material per mesh maps easily to aiMesh. Multiple material
+ // meshes need to be split.
+ const MatIndexArray& mindices = mesh.GetMaterialIndices();
+ if (doc.Settings().readMaterials && !mindices.empty()) {
+ const MatIndexArray::value_type base = mindices[0];
+ for (MatIndexArray::value_type index : mindices) {
+ if (index != base) {
+ return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd);
+ }
+ }
+ }
+
+ // faster code-path, just copy the data
+ temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd));
+ return temp;
+ }
+
+ std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry& line, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd)
+ {
+ std::vector<unsigned int> temp;
+
+ const std::vector<aiVector3D>& vertices = line.GetVertices();
+ const std::vector<int>& indices = line.GetIndices();
+ if (vertices.empty() || indices.empty()) {
+ FBXImporter::LogWarn("ignoring empty line: " + line.Name());
+ return temp;
+ }
+
+ aiMesh* const out_mesh = SetupEmptyMesh(line, nd);
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+
+ // copy vertices
+ out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+ out_mesh->mVertices = new aiVector3D[out_mesh->mNumVertices];
+ std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices);
+
+ //Number of line segments (faces) is "Number of Points - Number of Endpoints"
+ //N.B.: Endpoints in FbxLine are denoted by negative indices.
+ //If such an Index is encountered, add 1 and multiply by -1 to get the real index.
+ unsigned int epcount = 0;
+ for (unsigned i = 0; i < indices.size(); i++)
+ {
+ if (indices[i] < 0) epcount++;
+ }
+ unsigned int pcount = static_cast<unsigned int>( indices.size() );
+ unsigned int scount = out_mesh->mNumFaces = pcount - epcount;
+
+ aiFace* fac = out_mesh->mFaces = new aiFace[scount]();
+ for (unsigned int i = 0; i < pcount; ++i) {
+ if (indices[i] < 0) continue;
+ aiFace& f = *fac++;
+ f.mNumIndices = 2; //2 == aiPrimitiveType_LINE
+ f.mIndices = new unsigned int[2];
+ f.mIndices[0] = indices[i];
+ int segid = indices[(i + 1 == pcount ? 0 : i + 1)]; //If we have reached he last point, wrap around
+ f.mIndices[1] = (segid < 0 ? (segid + 1)*-1 : segid); //Convert EndPoint Index to normal Index
+ }
+ temp.push_back(static_cast<unsigned int>(meshes.size() - 1));
+ return temp;
+ }
+
+ aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd)
+ {
+ aiMesh* const out_mesh = new aiMesh();
+ meshes.push_back(out_mesh);
+ meshes_converted[&mesh].push_back(static_cast<unsigned int>(meshes.size() - 1));
+
+ // set name
+ std::string name = mesh.Name();
+ if (name.substr(0, 10) == "Geometry::") {
+ name = name.substr(10);
+ }
+
+ if (name.length()) {
+ out_mesh->mName.Set(name);
+ }
+ else
+ {
+ out_mesh->mName = nd.mName;
+ }
+
+ return out_mesh;
+ }
+
+ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd)
+ {
+ const MatIndexArray& mindices = mesh.GetMaterialIndices();
+ aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
+
+ const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+ const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+
+ // copy vertices
+ out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+ out_mesh->mVertices = new aiVector3D[vertices.size()];
+
+ std::copy(vertices.begin(), vertices.end(), out_mesh->mVertices);
+
+ // generate dummy faces
+ out_mesh->mNumFaces = static_cast<unsigned int>(faces.size());
+ aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()]();
+
+ unsigned int cursor = 0;
+ for (unsigned int pcount : faces) {
+ aiFace& f = *fac++;
+ f.mNumIndices = pcount;
+ f.mIndices = new unsigned int[pcount];
+ switch (pcount)
+ {
+ case 1:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ break;
+ }
+ for (unsigned int i = 0; i < pcount; ++i) {
+ f.mIndices[i] = cursor++;
+ }
+ }
+
+ // copy normals
+ const std::vector<aiVector3D>& normals = mesh.GetNormals();
+ if (normals.size()) {
+ ai_assert(normals.size() == vertices.size());
+
+ out_mesh->mNormals = new aiVector3D[vertices.size()];
+ std::copy(normals.begin(), normals.end(), out_mesh->mNormals);
+ }
+
+ // copy tangents - assimp requires both tangents and bitangents (binormals)
+ // to be present, or neither of them. Compute binormals from normals
+ // and tangents if needed.
+ const std::vector<aiVector3D>& tangents = mesh.GetTangents();
+ const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
+
+ if (tangents.size()) {
+ std::vector<aiVector3D> tempBinormals;
+ if (!binormals->size()) {
+ if (normals.size()) {
+ tempBinormals.resize(normals.size());
+ for (unsigned int i = 0; i < tangents.size(); ++i) {
+ tempBinormals[i] = normals[i] ^ tangents[i];
+ }
+
+ binormals = &tempBinormals;
+ }
+ else {
+ binormals = NULL;
+ }
+ }
+
+ if (binormals) {
+ ai_assert(tangents.size() == vertices.size());
+ ai_assert(binormals->size() == vertices.size());
+
+ out_mesh->mTangents = new aiVector3D[vertices.size()];
+ std::copy(tangents.begin(), tangents.end(), out_mesh->mTangents);
+
+ out_mesh->mBitangents = new aiVector3D[vertices.size()];
+ std::copy(binormals->begin(), binormals->end(), out_mesh->mBitangents);
+ }
+ }
+
+ // copy texture coords
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
+ if (uvs.empty()) {
+ break;
+ }
+
+ aiVector3D* out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+ for (const aiVector2D& v : uvs) {
+ *out_uv++ = aiVector3D(v.x, v.y, 0.0f);
+ }
+
+ out_mesh->mNumUVComponents[i] = 2;
+ }
+
+ // copy vertex colors
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
+ if (colors.empty()) {
+ break;
+ }
+
+ out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+ std::copy(colors.begin(), colors.end(), out_mesh->mColors[i]);
+ }
+
+ if (!doc.Settings().readMaterials || mindices.empty()) {
+ FBXImporter::LogError("no material assigned to mesh, setting default material");
+ out_mesh->mMaterialIndex = GetDefaultMaterial();
+ }
+ else {
+ ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]);
+ }
+
+ if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
+ ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION);
+ }
+
+ std::vector<aiAnimMesh*> animMeshes;
+ for (const BlendShape* blendShape : mesh.GetBlendShapes()) {
+ for (const BlendShapeChannel* blendShapeChannel : blendShape->BlendShapeChannels()) {
+ const std::vector<const ShapeGeometry*>& shapeGeometries = blendShapeChannel->GetShapeGeometries();
+ for (size_t i = 0; i < shapeGeometries.size(); i++) {
+ aiAnimMesh *animMesh = aiCreateAnimMesh(out_mesh);
+ const ShapeGeometry* shapeGeometry = shapeGeometries.at(i);
+ const std::vector<aiVector3D>& vertices = shapeGeometry->GetVertices();
+ const std::vector<aiVector3D>& normals = shapeGeometry->GetNormals();
+ const std::vector<unsigned int>& indices = shapeGeometry->GetIndices();
+ animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name()));
+ for (size_t j = 0; j < indices.size(); j++) {
+ unsigned int index = indices.at(j);
+ aiVector3D vertex = vertices.at(j);
+ aiVector3D normal = normals.at(j);
+ unsigned int count = 0;
+ const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count);
+ for (unsigned int k = 0; k < count; k++) {
+ unsigned int index = outIndices[k];
+ animMesh->mVertices[index] += vertex;
+ if (animMesh->mNormals != nullptr) {
+ animMesh->mNormals[index] += normal;
+ animMesh->mNormals[index].NormalizeSafe();
+ }
+ }
+ }
+ animMesh->mWeight = shapeGeometries.size() > 1 ? blendShapeChannel->DeformPercent() / 100.0f : 1.0f;
+ animMeshes.push_back(animMesh);
+ }
+ }
+ }
+ const size_t numAnimMeshes = animMeshes.size();
+ if (numAnimMeshes > 0) {
+ out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes);
+ out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes];
+ for (size_t i = 0; i < numAnimMeshes; i++) {
+ out_mesh->mAnimMeshes[i] = animMeshes.at(i);
+ }
+ }
+ return static_cast<unsigned int>(meshes.size() - 1);
+ }
+
+ std::vector<unsigned int> FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd)
+ {
+ const MatIndexArray& mindices = mesh.GetMaterialIndices();
+ ai_assert(mindices.size());
+
+ std::set<MatIndexArray::value_type> had;
+ std::vector<unsigned int> indices;
+
+ for (MatIndexArray::value_type index : mindices) {
+ if (had.find(index) == had.end()) {
+
+ indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd));
+ had.insert(index);
+ }
+ }
+
+ return indices;
+ }
+
+ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
+ MatIndexArray::value_type index,
+ const aiMatrix4x4& node_global_transform,
+ aiNode& nd)
+ {
+ aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
+
+ const MatIndexArray& mindices = mesh.GetMaterialIndices();
+ const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+ const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+
+ const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL;
+
+ unsigned int count_faces = 0;
+ unsigned int count_vertices = 0;
+
+ // count faces
+ std::vector<unsigned int>::const_iterator itf = faces.begin();
+ for (MatIndexArray::const_iterator it = mindices.begin(),
+ end = mindices.end(); it != end; ++it, ++itf)
+ {
+ if ((*it) != index) {
+ continue;
+ }
+ ++count_faces;
+ count_vertices += *itf;
+ }
+
+ ai_assert(count_faces);
+ ai_assert(count_vertices);
+
+ // mapping from output indices to DOM indexing, needed to resolve weights
+ std::vector<unsigned int> reverseMapping;
+
+ if (process_weights) {
+ reverseMapping.resize(count_vertices);
+ }
+
+ // allocate output data arrays, but don't fill them yet
+ out_mesh->mNumVertices = count_vertices;
+ out_mesh->mVertices = new aiVector3D[count_vertices];
+
+ out_mesh->mNumFaces = count_faces;
+ aiFace* fac = out_mesh->mFaces = new aiFace[count_faces]();
+
+
+ // allocate normals
+ const std::vector<aiVector3D>& normals = mesh.GetNormals();
+ if (normals.size()) {
+ ai_assert(normals.size() == vertices.size());
+ out_mesh->mNormals = new aiVector3D[vertices.size()];
+ }
+
+ // allocate tangents, binormals.
+ const std::vector<aiVector3D>& tangents = mesh.GetTangents();
+ const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
+ std::vector<aiVector3D> tempBinormals;
+
+ if (tangents.size()) {
+ if (!binormals->size()) {
+ if (normals.size()) {
+ // XXX this computes the binormals for the entire mesh, not only
+ // the part for which we need them.
+ tempBinormals.resize(normals.size());
+ for (unsigned int i = 0; i < tangents.size(); ++i) {
+ tempBinormals[i] = normals[i] ^ tangents[i];
+ }
+
+ binormals = &tempBinormals;
+ }
+ else {
+ binormals = NULL;
+ }
+ }
+
+ if (binormals) {
+ ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
+
+ out_mesh->mTangents = new aiVector3D[vertices.size()];
+ out_mesh->mBitangents = new aiVector3D[vertices.size()];
+ }
+ }
+
+ // allocate texture coords
+ unsigned int num_uvs = 0;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) {
+ const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
+ if (uvs.empty()) {
+ break;
+ }
+
+ out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+ out_mesh->mNumUVComponents[i] = 2;
+ }
+
+ // allocate vertex colors
+ unsigned int num_vcs = 0;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) {
+ const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
+ if (colors.empty()) {
+ break;
+ }
+
+ out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+ }
+
+ unsigned int cursor = 0, in_cursor = 0;
+
+ itf = faces.begin();
+ for (MatIndexArray::const_iterator it = mindices.begin(), end = mindices.end(); it != end; ++it, ++itf)
+ {
+ const unsigned int pcount = *itf;
+ if ((*it) != index) {
+ in_cursor += pcount;
+ continue;
+ }
+
+ aiFace& f = *fac++;
+
+ f.mNumIndices = pcount;
+ f.mIndices = new unsigned int[pcount];
+ switch (pcount)
+ {
+ case 1:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ break;
+ }
+ for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) {
+ f.mIndices[i] = cursor;
+
+ if (reverseMapping.size()) {
+ reverseMapping[cursor] = in_cursor;
+ }
+
+ out_mesh->mVertices[cursor] = vertices[in_cursor];
+
+ if (out_mesh->mNormals) {
+ out_mesh->mNormals[cursor] = normals[in_cursor];
+ }
+
+ if (out_mesh->mTangents) {
+ out_mesh->mTangents[cursor] = tangents[in_cursor];
+ out_mesh->mBitangents[cursor] = (*binormals)[in_cursor];
+ }
+
+ for (unsigned int j = 0; j < num_uvs; ++j) {
+ const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(j);
+ out_mesh->mTextureCoords[j][cursor] = aiVector3D(uvs[in_cursor].x, uvs[in_cursor].y, 0.0f);
+ }
+
+ for (unsigned int j = 0; j < num_vcs; ++j) {
+ const std::vector<aiColor4D>& cols = mesh.GetVertexColors(j);
+ out_mesh->mColors[j][cursor] = cols[in_cursor];
+ }
+ }
+ }
+
+ ConvertMaterialForMesh(out_mesh, model, mesh, index);
+
+ if (process_weights) {
+ ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping);
+ }
+
+ return static_cast<unsigned int>(meshes.size() - 1);
+ }
+
+ void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
+ const aiMatrix4x4& node_global_transform,
+ unsigned int materialIndex,
+ std::vector<unsigned int>* outputVertStartIndices)
+ {
+ ai_assert(geo.DeformerSkin());
+
+ std::vector<size_t> out_indices;
+ std::vector<size_t> index_out_indices;
+ std::vector<size_t> count_out_indices;
+
+ const Skin& sk = *geo.DeformerSkin();
+
+ std::vector<aiBone*> bones;
+ bones.reserve(sk.Clusters().size());
+
+ const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
+ ai_assert(no_mat_check || outputVertStartIndices);
+
+ try {
+
+ for (const Cluster* cluster : sk.Clusters()) {
+ ai_assert(cluster);
+
+ const WeightIndexArray& indices = cluster->GetIndices();
+
+ if (indices.empty()) {
+ continue;
+ }
+
+ const MatIndexArray& mats = geo.GetMaterialIndices();
+
+ bool ok = false;
+
+ const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
+
+ count_out_indices.clear();
+ index_out_indices.clear();
+ out_indices.clear();
+
+ // now check if *any* of these weights is contained in the output mesh,
+ // taking notes so we don't need to do it twice.
+ for (WeightIndexArray::value_type index : indices) {
+
+ unsigned int count = 0;
+ const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
+ // ToOutputVertexIndex only returns NULL if index is out of bounds
+ // which should never happen
+ ai_assert(out_idx != NULL);
+
+ index_out_indices.push_back(no_index_sentinel);
+ count_out_indices.push_back(0);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ if (no_mat_check || static_cast<size_t>(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) {
+
+ if (index_out_indices.back() == no_index_sentinel) {
+ index_out_indices.back() = out_indices.size();
+
+ }
+
+ if (no_mat_check) {
+ out_indices.push_back(out_idx[i]);
+ }
+ else {
+ // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
+ const std::vector<unsigned int>::iterator it = std::lower_bound(
+ outputVertStartIndices->begin(),
+ outputVertStartIndices->end(),
+ out_idx[i]
+ );
+
+ out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
+ }
+
+ ++count_out_indices.back();
+ ok = true;
+ }
+ }
+ }
+
+ // if we found at least one, generate the output bones
+ // XXX this could be heavily simplified by collecting the bone
+ // data in a single step.
+ if (ok) {
+ ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
+ count_out_indices, node_global_transform);
+ }
+ }
+ }
+ catch (std::exception&) {
+ std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>());
+ throw;
+ }
+
+ if (bones.empty()) {
+ return;
+ }
+
+ out->mBones = new aiBone*[bones.size()]();
+ out->mNumBones = static_cast<unsigned int>(bones.size());
+
+ std::swap_ranges(bones.begin(), bones.end(), out->mBones);
+ }
+
+ void FBXConverter::ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
+ std::vector<size_t>& out_indices,
+ std::vector<size_t>& index_out_indices,
+ std::vector<size_t>& count_out_indices,
+ const aiMatrix4x4& node_global_transform)
+ {
+
+ aiBone* const bone = new aiBone();
+ bones.push_back(bone);
+
+ bone->mName = FixNodeName(cl.TargetNode()->Name());
+
+ bone->mOffsetMatrix = cl.TransformLink();
+ bone->mOffsetMatrix.Inverse();
+
+ bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform;
+
+ bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
+ aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
+
+ const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
+ const WeightArray& weights = cl.GetWeights();
+
+ const size_t c = index_out_indices.size();
+ for (size_t i = 0; i < c; ++i) {
+ const size_t index_index = index_out_indices[i];
+
+ if (index_index == no_index_sentinel) {
+ continue;
+ }
+
+ const size_t cc = count_out_indices[i];
+ for (size_t j = 0; j < cc; ++j) {
+ aiVertexWeight& out_weight = *cursor++;
+
+ out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
+ out_weight.mWeight = weights[i];
+ }
+ }
+ }
+
+ void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
+ MatIndexArray::value_type materialIndex)
+ {
+ // locate source materials for this mesh
+ const std::vector<const Material*>& mats = model.GetMaterials();
+ if (static_cast<unsigned int>(materialIndex) >= mats.size() || materialIndex < 0) {
+ FBXImporter::LogError("material index out of bounds, setting default material");
+ out->mMaterialIndex = GetDefaultMaterial();
+ return;
+ }
+
+ const Material* const mat = mats[materialIndex];
+ MaterialMap::const_iterator it = materials_converted.find(mat);
+ if (it != materials_converted.end()) {
+ out->mMaterialIndex = (*it).second;
+ return;
+ }
+
+ out->mMaterialIndex = ConvertMaterial(*mat, &geo);
+ materials_converted[mat] = out->mMaterialIndex;
+ }
+
+ unsigned int FBXConverter::GetDefaultMaterial()
+ {
+ if (defaultMaterialIndex) {
+ return defaultMaterialIndex - 1;
+ }
+
+ aiMaterial* out_mat = new aiMaterial();
+ materials.push_back(out_mat);
+
+ const aiColor3D diffuse = aiColor3D(0.8f, 0.8f, 0.8f);
+ out_mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+
+ out_mat->AddProperty(&s, AI_MATKEY_NAME);
+
+ defaultMaterialIndex = static_cast<unsigned int>(materials.size());
+ return defaultMaterialIndex - 1;
+ }
+
+
+ unsigned int FBXConverter::ConvertMaterial(const Material& material, const MeshGeometry* const mesh)
+ {
+ const PropertyTable& props = material.Props();
+
+ // generate empty output material
+ aiMaterial* out_mat = new aiMaterial();
+ materials_converted[&material] = static_cast<unsigned int>(materials.size());
+
+ materials.push_back(out_mat);
+
+ aiString str;
+
+ // strip Material:: prefix
+ std::string name = material.Name();
+ if (name.substr(0, 10) == "Material::") {
+ name = name.substr(10);
+ }
+
+ // set material name if not empty - this could happen
+ // and there should be no key for it in this case.
+ if (name.length()) {
+ str.Set(name);
+ out_mat->AddProperty(&str, AI_MATKEY_NAME);
+ }
+
+ // shading stuff and colors
+ SetShadingPropertiesCommon(out_mat, props);
+ SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh );
+
+ // texture assignments
+ SetTextureProperties(out_mat, material.Textures(), mesh);
+ SetTextureProperties(out_mat, material.LayeredTextures(), mesh);
+
+ return static_cast<unsigned int>(materials.size() - 1);
+ }
+
+ unsigned int FBXConverter::ConvertVideo(const Video& video)
+ {
+ // generate empty output texture
+ aiTexture* out_tex = new aiTexture();
+ textures.push_back(out_tex);
+
+ // assuming the texture is compressed
+ out_tex->mWidth = static_cast<unsigned int>(video.ContentLength()); // total data size
+ out_tex->mHeight = 0; // fixed to 0
+
+ // steal the data from the Video to avoid an additional copy
+ out_tex->pcData = reinterpret_cast<aiTexel*>(const_cast<Video&>(video).RelinquishContent());
+
+ // try to extract a hint from the file extension
+ const std::string& filename = video.FileName().empty() ? video.RelativeFilename() : video.FileName();
+ std::string ext = BaseImporter::GetExtension(filename);
+
+ if (ext == "jpeg") {
+ ext = "jpg";
+ }
+
+ if (ext.size() <= 3) {
+ memcpy(out_tex->achFormatHint, ext.c_str(), ext.size());
+ }
+
+ out_tex->mFilename.Set(video.FileName().c_str());
+
+ return static_cast<unsigned int>(textures.size() - 1);
+ }
+
+ aiString FBXConverter::GetTexturePath(const Texture* tex)
+ {
+ aiString path;
+ path.Set(tex->RelativeFilename());
+
+ const Video* media = tex->Media();
+ if (media != nullptr) {
+ bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
+ unsigned int index;
+
+ VideoMap::const_iterator it = textures_converted.find(media);
+ if (it != textures_converted.end()) {
+ index = (*it).second;
+ textureReady = true;
+ }
+ else {
+ if (media->ContentLength() > 0) {
+ index = ConvertVideo(*media);
+ textures_converted[media] = index;
+ textureReady = true;
+ }
+ }
+
+ // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture), if the texture is ready
+ if (doc.Settings().useLegacyEmbeddedTextureNaming) {
+ if (textureReady) {
+ // TODO: check the possibility of using the flag "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
+ // In FBX files textures are now stored internally by Assimp with their filename included
+ // Now Assimp can lookup through the loaded textures after all data is processed
+ // We need to load all textures before referencing them, as FBX file format order may reference a texture before loading it
+ // This may occur on this case too, it has to be studied
+ path.data[0] = '*';
+ path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+ }
+ }
+ }
+
+ return path;
+ }
+
+ void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
+ const std::string& propName,
+ aiTextureType target, const MeshGeometry* const mesh)
+ {
+ TextureMap::const_iterator it = textures.find(propName);
+ if (it == textures.end()) {
+ return;
+ }
+
+ const Texture* const tex = (*it).second;
+ if (tex != 0)
+ {
+ aiString path = GetTexturePath(tex);
+ out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, 0);
+
+ aiUVTransform uvTrafo;
+ // XXX handle all kinds of UV transformations
+ uvTrafo.mScaling = tex->UVScaling();
+ uvTrafo.mTranslation = tex->UVTranslation();
+ out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0);
+
+ const PropertyTable& props = tex->Props();
+
+ int uvIndex = 0;
+
+ bool ok;
+ const std::string& uvSet = PropertyGet<std::string>(props, "UVSet", ok);
+ if (ok) {
+ // "default" is the name which usually appears in the FbxFileTexture template
+ if (uvSet != "default" && uvSet.length()) {
+ // this is a bit awkward - we need to find a mesh that uses this
+ // material and scan its UV channels for the given UV name because
+ // assimp references UV channels by index, not by name.
+
+ // XXX: the case that UV channels may appear in different orders
+ // in meshes is unhandled. A possible solution would be to sort
+ // the UV channels alphabetically, but this would have the side
+ // effect that the primary (first) UV channel would sometimes
+ // be moved, causing trouble when users read only the first
+ // UV channel and ignore UV channel assignments altogether.
+
+ const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(),
+ std::find(materials.begin(), materials.end(), out_mat)
+ ));
+
+
+ uvIndex = -1;
+ if (!mesh)
+ {
+ for (const MeshMap::value_type& v : meshes_converted) {
+ const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first);
+ if (!meshGeom) {
+ continue;
+ }
+
+ const MatIndexArray& mats = meshGeom->GetMaterialIndices();
+ if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
+ continue;
+ }
+
+ int index = -1;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ if (meshGeom->GetTextureCoords(i).empty()) {
+ break;
+ }
+ const std::string& name = meshGeom->GetTextureCoordChannelName(i);
+ if (name == uvSet) {
+ index = static_cast<int>(i);
+ break;
+ }
+ }
+ if (index == -1) {
+ FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+ continue;
+ }
+
+ if (uvIndex == -1) {
+ uvIndex = index;
+ }
+ else {
+ FBXImporter::LogWarn("the UV channel named " + uvSet +
+ " appears at different positions in meshes, results will be wrong");
+ }
+ }
+ }
+ else
+ {
+ int index = -1;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ if (mesh->GetTextureCoords(i).empty()) {
+ break;
+ }
+ const std::string& name = mesh->GetTextureCoordChannelName(i);
+ if (name == uvSet) {
+ index = static_cast<int>(i);
+ break;
+ }
+ }
+ if (index == -1) {
+ FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+ }
+
+ if (uvIndex == -1) {
+ uvIndex = index;
+ }
+ }
+
+ if (uvIndex == -1) {
+ FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+ uvIndex = 0;
+ }
+ }
+ }
+
+ out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, 0);
+ }
+ }
+
+ void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures,
+ const std::string& propName,
+ aiTextureType target, const MeshGeometry* const mesh) {
+ LayeredTextureMap::const_iterator it = layeredTextures.find(propName);
+ if (it == layeredTextures.end()) {
+ return;
+ }
+
+ int texCount = (*it).second->textureCount();
+
+ // Set the blend mode for layered textures
+ int blendmode = (*it).second->GetBlendMode();
+ out_mat->AddProperty(&blendmode, 1, _AI_MATKEY_TEXOP_BASE, target, 0);
+
+ for (int texIndex = 0; texIndex < texCount; texIndex++) {
+
+ const Texture* const tex = (*it).second->getTexture(texIndex);
+
+ aiString path = GetTexturePath(tex);
+ out_mat->AddProperty(&path, _AI_MATKEY_TEXTURE_BASE, target, texIndex);
+
+ aiUVTransform uvTrafo;
+ // XXX handle all kinds of UV transformations
+ uvTrafo.mScaling = tex->UVScaling();
+ uvTrafo.mTranslation = tex->UVTranslation();
+ out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex);
+
+ const PropertyTable& props = tex->Props();
+
+ int uvIndex = 0;
+
+ bool ok;
+ const std::string& uvSet = PropertyGet<std::string>(props, "UVSet", ok);
+ if (ok) {
+ // "default" is the name which usually appears in the FbxFileTexture template
+ if (uvSet != "default" && uvSet.length()) {
+ // this is a bit awkward - we need to find a mesh that uses this
+ // material and scan its UV channels for the given UV name because
+ // assimp references UV channels by index, not by name.
+
+ // XXX: the case that UV channels may appear in different orders
+ // in meshes is unhandled. A possible solution would be to sort
+ // the UV channels alphabetically, but this would have the side
+ // effect that the primary (first) UV channel would sometimes
+ // be moved, causing trouble when users read only the first
+ // UV channel and ignore UV channel assignments altogether.
+
+ const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(),
+ std::find(materials.begin(), materials.end(), out_mat)
+ ));
+
+ uvIndex = -1;
+ if (!mesh)
+ {
+ for (const MeshMap::value_type& v : meshes_converted) {
+ const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first);
+ if (!meshGeom) {
+ continue;
+ }
+
+ const MatIndexArray& mats = meshGeom->GetMaterialIndices();
+ if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
+ continue;
+ }
+
+ int index = -1;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ if (meshGeom->GetTextureCoords(i).empty()) {
+ break;
+ }
+ const std::string& name = meshGeom->GetTextureCoordChannelName(i);
+ if (name == uvSet) {
+ index = static_cast<int>(i);
+ break;
+ }
+ }
+ if (index == -1) {
+ FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+ continue;
+ }
+
+ if (uvIndex == -1) {
+ uvIndex = index;
+ }
+ else {
+ FBXImporter::LogWarn("the UV channel named " + uvSet +
+ " appears at different positions in meshes, results will be wrong");
+ }
+ }
+ }
+ else
+ {
+ int index = -1;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ if (mesh->GetTextureCoords(i).empty()) {
+ break;
+ }
+ const std::string& name = mesh->GetTextureCoordChannelName(i);
+ if (name == uvSet) {
+ index = static_cast<int>(i);
+ break;
+ }
+ }
+ if (index == -1) {
+ FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+ }
+
+ if (uvIndex == -1) {
+ uvIndex = index;
+ }
+ }
+
+ if (uvIndex == -1) {
+ FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+ uvIndex = 0;
+ }
+ }
+ }
+
+ out_mat->AddProperty(&uvIndex, 1, _AI_MATKEY_UVWSRC_BASE, target, texIndex);
+ }
+ }
+
+ void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh)
+ {
+ TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh);
+ TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT, mesh);
+ TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE, mesh);
+ TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR, mesh);
+ TrySetTextureProperties(out_mat, textures, "SpecularFactor", aiTextureType_SPECULAR, mesh);
+ TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY, mesh);
+ TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION, mesh);
+ TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh);
+ TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS, mesh);
+ TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT, mesh);
+ TrySetTextureProperties(out_mat, textures, "ShininessExponent", aiTextureType_SHININESS, mesh);
+ TrySetTextureProperties( out_mat, textures, "TransparencyFactor", aiTextureType_OPACITY, mesh );
+ TrySetTextureProperties( out_mat, textures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh );
+ //Maya counterparts
+ TrySetTextureProperties(out_mat, textures, "Maya|DiffuseTexture", aiTextureType_DIFFUSE, mesh);
+ TrySetTextureProperties(out_mat, textures, "Maya|NormalTexture", aiTextureType_NORMALS, mesh);
+ TrySetTextureProperties(out_mat, textures, "Maya|SpecularTexture", aiTextureType_SPECULAR, mesh);
+ TrySetTextureProperties(out_mat, textures, "Maya|FalloffTexture", aiTextureType_OPACITY, mesh);
+ TrySetTextureProperties(out_mat, textures, "Maya|ReflectionMapTexture", aiTextureType_REFLECTION, mesh);
+ }
+
+ void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh)
+ {
+ TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "SpecularFactor", aiTextureType_SPECULAR, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT, mesh);
+ TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh);
+ TrySetTextureProperties( out_mat, layeredTextures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh );
+ TrySetTextureProperties( out_mat, layeredTextures, "TransparencyFactor", aiTextureType_OPACITY, mesh );
+ }
+
+ aiColor3D FBXConverter::GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName,
+ const std::string& factorName, bool& result, bool useTemplate)
+ {
+ result = true;
+
+ bool ok;
+ aiVector3D BaseColor = PropertyGet<aiVector3D>(props, colorName, ok, useTemplate);
+ if (!ok) {
+ result = false;
+ return aiColor3D(0.0f, 0.0f, 0.0f);
+ }
+
+ // if no factor name, return the colour as is
+ if (factorName.empty()) {
+ return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z);
+ }
+
+ // otherwise it should be multiplied by the factor, if found.
+ float factor = PropertyGet<float>(props, factorName, ok, useTemplate);
+ if (ok) {
+ BaseColor *= factor;
+ }
+ return aiColor3D(BaseColor.x, BaseColor.y, BaseColor.z);
+ }
+
+ aiColor3D FBXConverter::GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName,
+ bool& result)
+ {
+ return GetColorPropertyFactored(props, baseName + "Color", baseName + "Factor", result, true);
+ }
+
+ aiColor3D FBXConverter::GetColorProperty(const PropertyTable& props, const std::string& colorName,
+ bool& result, bool useTemplate)
+ {
+ result = true;
+ bool ok;
+ const aiVector3D& ColorVec = PropertyGet<aiVector3D>(props, colorName, ok, useTemplate);
+ if (!ok) {
+ result = false;
+ return aiColor3D(0.0f, 0.0f, 0.0f);
+ }
+ return aiColor3D(ColorVec.x, ColorVec.y, ColorVec.z);
+ }
+
+ void FBXConverter::SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props)
+ {
+ // Set shading properties.
+ // Modern FBX Files have two separate systems for defining these,
+ // with only the more comprehensive one described in the property template.
+ // Likely the other values are a legacy system,
+ // which is still always exported by the official FBX SDK.
+ //
+ // Blender's FBX import and export mostly ignore this legacy system,
+ // and as we only support recent versions of FBX anyway, we can do the same.
+ bool ok;
+
+ const aiColor3D& Diffuse = GetColorPropertyFromMaterial(props, "Diffuse", ok);
+ if (ok) {
+ out_mat->AddProperty(&Diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ }
+
+ const aiColor3D& Emissive = GetColorPropertyFromMaterial(props, "Emissive", ok);
+ if (ok) {
+ out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+ }
+
+ const aiColor3D& Ambient = GetColorPropertyFromMaterial(props, "Ambient", ok);
+ if (ok) {
+ out_mat->AddProperty(&Ambient, 1, AI_MATKEY_COLOR_AMBIENT);
+ }
+
+ // we store specular factor as SHININESS_STRENGTH, so just get the color
+ const aiColor3D& Specular = GetColorProperty(props, "SpecularColor", ok, true);
+ if (ok) {
+ out_mat->AddProperty(&Specular, 1, AI_MATKEY_COLOR_SPECULAR);
+ }
+
+ // and also try to get SHININESS_STRENGTH
+ const float SpecularFactor = PropertyGet<float>(props, "SpecularFactor", ok, true);
+ if (ok) {
+ out_mat->AddProperty(&SpecularFactor, 1, AI_MATKEY_SHININESS_STRENGTH);
+ }
+
+ // and the specular exponent
+ const float ShininessExponent = PropertyGet<float>(props, "ShininessExponent", ok);
+ if (ok) {
+ out_mat->AddProperty(&ShininessExponent, 1, AI_MATKEY_SHININESS);
+ }
+
+ // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes:
+ const aiColor3D& Transparent = GetColorPropertyFactored(props, "TransparentColor", "TransparencyFactor", ok);
+ float CalculatedOpacity = 1.0f;
+ if (ok) {
+ out_mat->AddProperty(&Transparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
+ // as calculated by FBX SDK 2017:
+ CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f);
+ }
+
+ // try to get the transparency factor
+ const float TransparencyFactor = PropertyGet<float>(props, "TransparencyFactor", ok);
+ if (ok) {
+ out_mat->AddProperty(&TransparencyFactor, 1, AI_MATKEY_TRANSPARENCYFACTOR);
+ }
+
+ // use of TransparencyFactor is inconsistent.
+ // Maya always stores it as 1.0,
+ // so we can't use it to set AI_MATKEY_OPACITY.
+ // Blender is more sensible and stores it as the alpha value.
+ // However both the FBX SDK and Blender always write an additional
+ // legacy "Opacity" field, so we can try to use that.
+ //
+ // If we can't find it,
+ // we can fall back to the value which the FBX SDK calculates
+ // from transparency colour (RGB) and factor (F) as
+ // 1.0 - F*((R+G+B)/3).
+ //
+ // There's no consistent way to interpret this opacity value,
+ // so it's up to clients to do the correct thing.
+ const float Opacity = PropertyGet<float>(props, "Opacity", ok);
+ if (ok) {
+ out_mat->AddProperty(&Opacity, 1, AI_MATKEY_OPACITY);
+ }
+ else if (CalculatedOpacity != 1.0) {
+ out_mat->AddProperty(&CalculatedOpacity, 1, AI_MATKEY_OPACITY);
+ }
+
+ // reflection color and factor are stored separately
+ const aiColor3D& Reflection = GetColorProperty(props, "ReflectionColor", ok, true);
+ if (ok) {
+ out_mat->AddProperty(&Reflection, 1, AI_MATKEY_COLOR_REFLECTIVE);
+ }
+
+ float ReflectionFactor = PropertyGet<float>(props, "ReflectionFactor", ok, true);
+ if (ok) {
+ out_mat->AddProperty(&ReflectionFactor, 1, AI_MATKEY_REFLECTIVITY);
+ }
+
+ const float BumpFactor = PropertyGet<float>(props, "BumpFactor", ok);
+ if (ok) {
+ out_mat->AddProperty(&BumpFactor, 1, AI_MATKEY_BUMPSCALING);
+ }
+
+ const float DispFactor = PropertyGet<float>(props, "DisplacementFactor", ok);
+ if (ok) {
+ out_mat->AddProperty(&DispFactor, 1, "$mat.displacementscaling", 0, 0);
+ }
+}
+
+
+void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh)
+{
+ // Add all the unparsed properties with a "$raw." prefix
+
+ const std::string prefix = "$raw.";
+
+ for (const DirectPropertyMap::value_type& prop : props.GetUnparsedProperties()) {
+
+ std::string name = prefix + prop.first;
+
+ if (const TypedProperty<aiVector3D>* interpreted = prop.second->As<TypedProperty<aiVector3D> >())
+ {
+ out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0);
+ }
+ else if (const TypedProperty<aiColor3D>* interpreted = prop.second->As<TypedProperty<aiColor3D> >())
+ {
+ out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0);
+ }
+ else if (const TypedProperty<aiColor4D>* interpreted = prop.second->As<TypedProperty<aiColor4D> >())
+ {
+ out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0);
+ }
+ else if (const TypedProperty<float>* interpreted = prop.second->As<TypedProperty<float> >())
+ {
+ out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0);
+ }
+ else if (const TypedProperty<int>* interpreted = prop.second->As<TypedProperty<int> >())
+ {
+ out_mat->AddProperty(&interpreted->Value(), 1, name.c_str(), 0, 0);
+ }
+ else if (const TypedProperty<bool>* interpreted = prop.second->As<TypedProperty<bool> >())
+ {
+ int value = interpreted->Value() ? 1 : 0;
+ out_mat->AddProperty(&value, 1, name.c_str(), 0, 0);
+ }
+ else if (const TypedProperty<std::string>* interpreted = prop.second->As<TypedProperty<std::string> >())
+ {
+ const aiString value = aiString(interpreted->Value());
+ out_mat->AddProperty(&value, name.c_str(), 0, 0);
+ }
+ }
+
+ // Add the textures' properties
+
+ for (TextureMap::const_iterator it = textures.begin(); it != textures.end(); it++) {
+
+ std::string name = prefix + it->first;
+
+ const Texture* const tex = (*it).second;
+ if (tex != nullptr)
+ {
+ aiString path;
+ path.Set(tex->RelativeFilename());
+
+ const Video* media = tex->Media();
+ if (media != nullptr && media->ContentLength() > 0) {
+ unsigned int index;
+
+ VideoMap::const_iterator it = textures_converted.find(media);
+ if (it != textures_converted.end()) {
+ index = (*it).second;
+ }
+ else {
+ index = ConvertVideo(*media);
+ textures_converted[media] = index;
+ }
+
+ // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
+ path.data[0] = '*';
+ path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+ }
+
+ out_mat->AddProperty(&path, (name + "|file").c_str(), aiTextureType_UNKNOWN, 0);
+
+ aiUVTransform uvTrafo;
+ // XXX handle all kinds of UV transformations
+ uvTrafo.mScaling = tex->UVScaling();
+ uvTrafo.mTranslation = tex->UVTranslation();
+ out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0);
+
+ int uvIndex = 0;
+
+ bool uvFound = false;
+ const std::string& uvSet = PropertyGet<std::string>(tex->Props(), "UVSet", uvFound);
+ if (uvFound) {
+ // "default" is the name which usually appears in the FbxFileTexture template
+ if (uvSet != "default" && uvSet.length()) {
+ // this is a bit awkward - we need to find a mesh that uses this
+ // material and scan its UV channels for the given UV name because
+ // assimp references UV channels by index, not by name.
+
+ // XXX: the case that UV channels may appear in different orders
+ // in meshes is unhandled. A possible solution would be to sort
+ // the UV channels alphabetically, but this would have the side
+ // effect that the primary (first) UV channel would sometimes
+ // be moved, causing trouble when users read only the first
+ // UV channel and ignore UV channel assignments altogether.
+
+ std::vector<aiMaterial*>::iterator materialIt = std::find(materials.begin(), materials.end(), out_mat);
+ const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), materialIt));
+
+ uvIndex = -1;
+ if (!mesh)
+ {
+ for (const MeshMap::value_type& v : meshes_converted) {
+ const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*>(v.first);
+ if (!meshGeom) {
+ continue;
+ }
+
+ const MatIndexArray& mats = meshGeom->GetMaterialIndices();
+ if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
+ continue;
+ }
+
+ int index = -1;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ if (meshGeom->GetTextureCoords(i).empty()) {
+ break;
+ }
+ const std::string& name = meshGeom->GetTextureCoordChannelName(i);
+ if (name == uvSet) {
+ index = static_cast<int>(i);
+ break;
+ }
+ }
+ if (index == -1) {
+ FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+ continue;
+ }
+
+ if (uvIndex == -1) {
+ uvIndex = index;
+ }
+ else {
+ FBXImporter::LogWarn("the UV channel named " + uvSet + " appears at different positions in meshes, results will be wrong");
+ }
+ }
+ }
+ else
+ {
+ int index = -1;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ if (mesh->GetTextureCoords(i).empty()) {
+ break;
+ }
+ const std::string& name = mesh->GetTextureCoordChannelName(i);
+ if (name == uvSet) {
+ index = static_cast<int>(i);
+ break;
+ }
+ }
+ if (index == -1) {
+ FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+ }
+
+ if (uvIndex == -1) {
+ uvIndex = index;
+ }
+ }
+
+ if (uvIndex == -1) {
+ FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+ uvIndex = 0;
+ }
+ }
+ }
+
+ out_mat->AddProperty(&uvIndex, 1, (name + "|uvwsrc").c_str(), aiTextureType_UNKNOWN, 0);
+ }
+ }
+ }
+
+
+ double FBXConverter::FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal) {
+ switch (fp) {
+ case FileGlobalSettings::FrameRate_DEFAULT:
+ return 1.0;
+
+ case FileGlobalSettings::FrameRate_120:
+ return 120.0;
+
+ case FileGlobalSettings::FrameRate_100:
+ return 100.0;
+
+ case FileGlobalSettings::FrameRate_60:
+ return 60.0;
+
+ case FileGlobalSettings::FrameRate_50:
+ return 50.0;
+
+ case FileGlobalSettings::FrameRate_48:
+ return 48.0;
+
+ case FileGlobalSettings::FrameRate_30:
+ case FileGlobalSettings::FrameRate_30_DROP:
+ return 30.0;
+
+ case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME:
+ case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME:
+ return 29.9700262;
+
+ case FileGlobalSettings::FrameRate_PAL:
+ return 25.0;
+
+ case FileGlobalSettings::FrameRate_CINEMA:
+ return 24.0;
+
+ case FileGlobalSettings::FrameRate_1000:
+ return 1000.0;
+
+ case FileGlobalSettings::FrameRate_CINEMA_ND:
+ return 23.976;
+
+ case FileGlobalSettings::FrameRate_CUSTOM:
+ return customFPSVal;
+
+ case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings
+ break;
+ }
+
+ ai_assert(false);
+
+ return -1.0f;
+ }
+
+
+ void FBXConverter::ConvertAnimations()
+ {
+ // first of all determine framerate
+ const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode();
+ const float custom = doc.GlobalSettings().CustomFrameRate();
+ anim_fps = FrameRateToDouble(fps, custom);
+
+ const std::vector<const AnimationStack*>& animations = doc.AnimationStacks();
+ for (const AnimationStack* stack : animations) {
+ ConvertAnimationStack(*stack);
+ }
+ }
+
+ std::string FBXConverter::FixNodeName(const std::string& name) {
+ // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if
+ // this causes ambiguities, well possible between empty identifiers,
+ // such as "Model::" and ""). Make sure the behaviour is consistent
+ // across multiple calls to FixNodeName().
+ if (name.substr(0, 7) == "Model::") {
+ std::string temp = name.substr(7);
+ return temp;
+ }
+
+ return name;
+ }
+
+ std::string FBXConverter::FixAnimMeshName(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";
+ }
+
+ void FBXConverter::ConvertAnimationStack(const AnimationStack& st)
+ {
+ const AnimationLayerList& layers = st.Layers();
+ if (layers.empty()) {
+ return;
+ }
+
+ aiAnimation* const anim = new aiAnimation();
+ animations.push_back(anim);
+
+ // strip AnimationStack:: prefix
+ std::string name = st.Name();
+ if (name.substr(0, 16) == "AnimationStack::") {
+ name = name.substr(16);
+ }
+ else if (name.substr(0, 11) == "AnimStack::") {
+ name = name.substr(11);
+ }
+
+ anim->mName.Set(name);
+
+ // need to find all nodes for which we need to generate node animations -
+ // it may happen that we need to merge multiple layers, though.
+ NodeMap node_map;
+
+ // reverse mapping from curves to layers, much faster than querying
+ // the FBX DOM for it.
+ LayerMap layer_map;
+
+ const char* prop_whitelist[] = {
+ "Lcl Scaling",
+ "Lcl Rotation",
+ "Lcl Translation",
+ "DeformPercent"
+ };
+
+ std::map<std::string, morphAnimData*> morphAnimDatas;
+
+ for (const AnimationLayer* layer : layers) {
+ ai_assert(layer);
+ const AnimationCurveNodeList& nodes = layer->Nodes(prop_whitelist, 4);
+ for (const AnimationCurveNode* node : nodes) {
+ ai_assert(node);
+ const Model* const model = dynamic_cast<const Model*>(node->Target());
+ if (model) {
+ const std::string& name = FixNodeName(model->Name());
+ node_map[name].push_back(node);
+ layer_map[node] = layer;
+ continue;
+ }
+ const BlendShapeChannel* const bsc = dynamic_cast<const BlendShapeChannel*>(node->Target());
+ if (bsc) {
+ ProcessMorphAnimDatas(&morphAnimDatas, bsc, node);
+ }
+ }
+ }
+
+ // generate node animations
+ std::vector<aiNodeAnim*> node_anims;
+
+ double min_time = 1e10;
+ double max_time = -1e10;
+
+ int64_t start_time = st.LocalStart();
+ int64_t stop_time = st.LocalStop();
+ bool has_local_startstop = start_time != 0 || stop_time != 0;
+ if (!has_local_startstop) {
+ // no time range given, so accept every keyframe and use the actual min/max time
+ // the numbers are INT64_MIN/MAX, the 20000 is for safety because GenerateNodeAnimations uses an epsilon of 10000
+ start_time = -9223372036854775807ll + 20000;
+ stop_time = 9223372036854775807ll - 20000;
+ }
+
+ try {
+ for (const NodeMap::value_type& kv : node_map) {
+ GenerateNodeAnimations(node_anims,
+ kv.first,
+ kv.second,
+ layer_map,
+ start_time, stop_time,
+ max_time,
+ min_time);
+ }
+ }
+ catch (std::exception&) {
+ std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>());
+ throw;
+ }
+
+ if (node_anims.size() || morphAnimDatas.size()) {
+ if (node_anims.size()) {
+ anim->mChannels = new aiNodeAnim*[node_anims.size()]();
+ anim->mNumChannels = static_cast<unsigned int>(node_anims.size());
+ std::swap_ranges(node_anims.begin(), node_anims.end(), anim->mChannels);
+ }
+ if (morphAnimDatas.size()) {
+ unsigned int numMorphMeshChannels = static_cast<unsigned int>(morphAnimDatas.size());
+ anim->mMorphMeshChannels = new aiMeshMorphAnim*[numMorphMeshChannels];
+ anim->mNumMorphMeshChannels = numMorphMeshChannels;
+ unsigned int i = 0;
+ for (auto morphAnimIt : morphAnimDatas) {
+ morphAnimData* animData = morphAnimIt.second;
+ unsigned int numKeys = static_cast<unsigned int>(animData->size());
+ aiMeshMorphAnim* meshMorphAnim = new aiMeshMorphAnim();
+ meshMorphAnim->mName.Set(morphAnimIt.first);
+ meshMorphAnim->mNumKeys = numKeys;
+ meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys];
+ unsigned int j = 0;
+ for (auto animIt : *animData) {
+ morphKeyData* keyData = animIt.second;
+ unsigned int numValuesAndWeights = static_cast<unsigned int>(keyData->values.size());
+ meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights;
+ meshMorphAnim->mKeys[j].mValues = new unsigned int[numValuesAndWeights];
+ meshMorphAnim->mKeys[j].mWeights = new double[numValuesAndWeights];
+ meshMorphAnim->mKeys[j].mTime = CONVERT_FBX_TIME(animIt.first) * anim_fps;
+ for (unsigned int k = 0; k < numValuesAndWeights; k++) {
+ meshMorphAnim->mKeys[j].mValues[k] = keyData->values.at(k);
+ meshMorphAnim->mKeys[j].mWeights[k] = keyData->weights.at(k);
+ }
+ j++;
+ }
+ anim->mMorphMeshChannels[i++] = meshMorphAnim;
+ }
+ }
+ }
+ else {
+ // empty animations would fail validation, so drop them
+ delete anim;
+ animations.pop_back();
+ FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name);
+ return;
+ }
+
+ double start_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(start_time) * anim_fps) : min_time;
+ double stop_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(stop_time) * anim_fps) : max_time;
+
+ // adjust relative timing for animation
+ for (unsigned int c = 0; c < anim->mNumChannels; c++) {
+ aiNodeAnim* channel = anim->mChannels[c];
+ for (uint32_t i = 0; i < channel->mNumPositionKeys; i++) {
+ channel->mPositionKeys[i].mTime -= start_time_fps;
+ }
+ for (uint32_t i = 0; i < channel->mNumRotationKeys; i++) {
+ channel->mRotationKeys[i].mTime -= start_time_fps;
+ }
+ for (uint32_t i = 0; i < channel->mNumScalingKeys; i++) {
+ channel->mScalingKeys[i].mTime -= start_time_fps;
+ }
+ }
+ for (unsigned int c = 0; c < anim->mNumMorphMeshChannels; c++) {
+ aiMeshMorphAnim* channel = anim->mMorphMeshChannels[c];
+ for (uint32_t i = 0; i < channel->mNumKeys; i++) {
+ channel->mKeys[i].mTime -= start_time_fps;
+ }
+ }
+
+ // for some mysterious reason, mDuration is simply the maximum key -- the
+ // validator always assumes animations to start at zero.
+ anim->mDuration = stop_time_fps - start_time_fps;
+ anim->mTicksPerSecond = anim_fps;
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ void FBXConverter::ProcessMorphAnimDatas(std::map<std::string, morphAnimData*>* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node) {
+ std::vector<const Connection*> bscConnections = doc.GetConnectionsBySourceSequenced(bsc->ID(), "Deformer");
+ for (const Connection* bscConnection : bscConnections) {
+ auto bs = dynamic_cast<const BlendShape*>(bscConnection->DestinationObject());
+ if (bs) {
+ auto channelIt = std::find(bs->BlendShapeChannels().begin(), bs->BlendShapeChannels().end(), bsc);
+ if (channelIt != bs->BlendShapeChannels().end()) {
+ auto channelIndex = static_cast<unsigned int>(std::distance(bs->BlendShapeChannels().begin(), channelIt));
+ std::vector<const Connection*> bsConnections = doc.GetConnectionsBySourceSequenced(bs->ID(), "Geometry");
+ for (const Connection* bsConnection : bsConnections) {
+ auto geo = dynamic_cast<const Geometry*>(bsConnection->DestinationObject());
+ if (geo) {
+ std::vector<const Connection*> geoConnections = doc.GetConnectionsBySourceSequenced(geo->ID(), "Model");
+ for (const Connection* geoConnection : geoConnections) {
+ auto model = dynamic_cast<const Model*>(geoConnection->DestinationObject());
+ if (model) {
+ auto geoIt = std::find(model->GetGeometry().begin(), model->GetGeometry().end(), geo);
+ auto geoIndex = static_cast<unsigned int>(std::distance(model->GetGeometry().begin(), geoIt));
+ auto name = aiString(FixNodeName(model->Name() + "*"));
+ name.length = 1 + ASSIMP_itoa10(name.data + name.length, MAXLEN - 1, geoIndex);
+ morphAnimData* animData;
+ auto animIt = morphAnimDatas->find(name.C_Str());
+ if (animIt == morphAnimDatas->end()) {
+ animData = new morphAnimData();
+ morphAnimDatas->insert(std::make_pair(name.C_Str(), animData));
+ }
+ else {
+ animData = animIt->second;
+ }
+ for (std::pair<std::string, const AnimationCurve*> curvesIt : node->Curves()) {
+ if (curvesIt.first == "d|DeformPercent") {
+ const AnimationCurve* animationCurve = curvesIt.second;
+ const KeyTimeList& keys = animationCurve->GetKeys();
+ const KeyValueList& values = animationCurve->GetValues();
+ unsigned int k = 0;
+ for (auto key : keys) {
+ morphKeyData* keyData;
+ auto keyIt = animData->find(key);
+ if (keyIt == animData->end()) {
+ keyData = new morphKeyData();
+ animData->insert(std::make_pair(key, keyData));
+ }
+ else {
+ keyData = keyIt->second;
+ }
+ keyData->values.push_back(channelIndex);
+ keyData->weights.push_back(values.at(k) / 100.0f);
+ k++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+#ifdef ASSIMP_BUILD_DEBUG
+ // ------------------------------------------------------------------------------------------------
+ // sanity check whether the input is ok
+ static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode*>& curves,
+ bool strictMode) {
+ const Object* target(NULL);
+ for (const AnimationCurveNode* node : curves) {
+ if (!target) {
+ target = node->Target();
+ }
+ if (node->Target() != target) {
+ FBXImporter::LogWarn("Node target is nullptr type.");
+ }
+ if (strictMode) {
+ ai_assert(node->Target() == target);
+ }
+ }
+ }
+#endif // ASSIMP_BUILD_DEBUG
+
+ // ------------------------------------------------------------------------------------------------
+ void FBXConverter::GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims,
+ const std::string& fixed_name,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time)
+ {
+
+ NodeMap node_property_map;
+ ai_assert(curves.size());
+
+#ifdef ASSIMP_BUILD_DEBUG
+ validateAnimCurveNodes(curves, doc.Settings().strictMode);
+#endif
+ const AnimationCurveNode* curve_node = NULL;
+ for (const AnimationCurveNode* node : curves) {
+ ai_assert(node);
+
+ if (node->TargetProperty().empty()) {
+ FBXImporter::LogWarn("target property for animation curve not set: " + node->Name());
+ continue;
+ }
+
+ curve_node = node;
+ if (node->Curves().empty()) {
+ FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name());
+ continue;
+ }
+
+ node_property_map[node->TargetProperty()].push_back(node);
+ }
+
+ ai_assert(curve_node);
+ ai_assert(curve_node->TargetAsModel());
+
+ const Model& target = *curve_node->TargetAsModel();
+
+ // check for all possible transformation components
+ NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
+
+ bool has_any = false;
+ bool has_complex = false;
+
+ for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+ const TransformationComp comp = static_cast<TransformationComp>(i);
+
+ // inverse pivots don't exist in the input, we just generate them
+ if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) {
+ chain[i] = node_property_map.end();
+ continue;
+ }
+
+ chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
+ if (chain[i] != node_property_map.end()) {
+
+ // check if this curves contains redundant information by looking
+ // up the corresponding node's transformation chain.
+ if (doc.Settings().optimizeEmptyAnimationCurves &&
+ IsRedundantAnimationData(target, comp, (*chain[i]).second)) {
+
+ FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name());
+ continue;
+ }
+
+ has_any = true;
+
+ if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation)
+ {
+ has_complex = true;
+ }
+ }
+ }
+
+ if (!has_any) {
+ FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
+ return;
+ }
+
+ // this needs to play nicely with GenerateTransformationNodeChain() which will
+ // be invoked _later_ (animations come first). If this node has only rotation,
+ // scaling and translation _and_ there are no animated other components either,
+ // we can use a single node and also a single node animation channel.
+ if (!has_complex && !NeedsComplexTransformationChain(target)) {
+
+ aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain,
+ node_property_map.end(),
+ layer_map,
+ start, stop,
+ max_time,
+ min_time,
+ true // input is TRS order, assimp is SRT
+ );
+
+ ai_assert(nd);
+ if (nd->mNumPositionKeys == 0 && nd->mNumRotationKeys == 0 && nd->mNumScalingKeys == 0) {
+ delete nd;
+ }
+ else {
+ node_anims.push_back(nd);
+ }
+ return;
+ }
+
+ // otherwise, things get gruesome and we need separate animation channels
+ // for each part of the transformation chain. Remember which channels
+ // we generated and pass this information to the node conversion
+ // code to avoid nodes that have identity transform, but non-identity
+ // animations, being dropped.
+ unsigned int flags = 0, bit = 0x1;
+ for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
+ const TransformationComp comp = static_cast<TransformationComp>(i);
+
+ if (chain[i] != node_property_map.end()) {
+ flags |= bit;
+
+ ai_assert(comp != TransformationComp_RotationPivotInverse);
+ ai_assert(comp != TransformationComp_ScalingPivotInverse);
+
+ const std::string& chain_name = NameTransformationChainNode(fixed_name, comp);
+
+ aiNodeAnim* na = nullptr;
+ switch (comp)
+ {
+ case TransformationComp_Rotation:
+ case TransformationComp_PreRotation:
+ case TransformationComp_PostRotation:
+ case TransformationComp_GeometricRotation:
+ na = GenerateRotationNodeAnim(chain_name,
+ target,
+ (*chain[i]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time);
+
+ break;
+
+ case TransformationComp_RotationOffset:
+ case TransformationComp_RotationPivot:
+ case TransformationComp_ScalingOffset:
+ case TransformationComp_ScalingPivot:
+ case TransformationComp_Translation:
+ case TransformationComp_GeometricTranslation:
+ na = GenerateTranslationNodeAnim(chain_name,
+ target,
+ (*chain[i]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time);
+
+ // pivoting requires us to generate an implicit inverse channel to undo the pivot translation
+ if (comp == TransformationComp_RotationPivot) {
+ const std::string& invName = NameTransformationChainNode(fixed_name,
+ TransformationComp_RotationPivotInverse);
+
+ aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
+ target,
+ (*chain[i]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time,
+ true);
+
+ ai_assert(inv);
+ if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) {
+ delete inv;
+ }
+ else {
+ node_anims.push_back(inv);
+ }
+
+ ai_assert(TransformationComp_RotationPivotInverse > i);
+ flags |= bit << (TransformationComp_RotationPivotInverse - i);
+ }
+ else if (comp == TransformationComp_ScalingPivot) {
+ const std::string& invName = NameTransformationChainNode(fixed_name,
+ TransformationComp_ScalingPivotInverse);
+
+ aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
+ target,
+ (*chain[i]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time,
+ true);
+
+ ai_assert(inv);
+ if (inv->mNumPositionKeys == 0 && inv->mNumRotationKeys == 0 && inv->mNumScalingKeys == 0) {
+ delete inv;
+ }
+ else {
+ node_anims.push_back(inv);
+ }
+
+ ai_assert(TransformationComp_RotationPivotInverse > i);
+ flags |= bit << (TransformationComp_RotationPivotInverse - i);
+ }
+
+ break;
+
+ case TransformationComp_Scaling:
+ case TransformationComp_GeometricScaling:
+ na = GenerateScalingNodeAnim(chain_name,
+ target,
+ (*chain[i]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time);
+
+ break;
+
+ default:
+ ai_assert(false);
+ }
+
+ ai_assert(na);
+ if (na->mNumPositionKeys == 0 && na->mNumRotationKeys == 0 && na->mNumScalingKeys == 0) {
+ delete na;
+ }
+ else {
+ node_anims.push_back(na);
+ }
+ continue;
+ }
+ }
+
+ node_anim_chain_bits[fixed_name] = flags;
+ }
+
+
+ bool FBXConverter::IsRedundantAnimationData(const Model& target,
+ TransformationComp comp,
+ const std::vector<const AnimationCurveNode*>& curves) {
+ ai_assert(curves.size());
+
+ // look for animation nodes with
+ // * sub channels for all relevant components set
+ // * one key/value pair per component
+ // * combined values match up the corresponding value in the bind pose node transformation
+ // only such nodes are 'redundant' for this function.
+
+ if (curves.size() > 1) {
+ return false;
+ }
+
+ const AnimationCurveNode& nd = *curves.front();
+ const AnimationCurveMap& sub_curves = nd.Curves();
+
+ const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X");
+ const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y");
+ const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z");
+
+ if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) {
+ return false;
+ }
+
+ const KeyValueList& vx = (*dx).second->GetValues();
+ const KeyValueList& vy = (*dy).second->GetValues();
+ const KeyValueList& vz = (*dz).second->GetValues();
+
+ if (vx.size() != 1 || vy.size() != 1 || vz.size() != 1) {
+ return false;
+ }
+
+ const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]);
+ const aiVector3D& static_val = PropertyGet<aiVector3D>(target.Props(),
+ NameTransformationCompProperty(comp),
+ TransformationCompDefaultValue(comp)
+ );
+
+ const float epsilon = 1e-6f;
+ return (dyn_val - static_val).SquareLength() < epsilon;
+ }
+
+
+ aiNodeAnim* FBXConverter::GenerateRotationNodeAnim(const std::string& name,
+ const Model& target,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time)
+ {
+ std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+ na->mNodeName.Set(name);
+
+ ConvertRotationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time, target.RotationOrder());
+
+ // dummy scaling key
+ na->mScalingKeys = new aiVectorKey[1];
+ na->mNumScalingKeys = 1;
+
+ na->mScalingKeys[0].mTime = 0.;
+ na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f);
+
+ // dummy position key
+ na->mPositionKeys = new aiVectorKey[1];
+ na->mNumPositionKeys = 1;
+
+ na->mPositionKeys[0].mTime = 0.;
+ na->mPositionKeys[0].mValue = aiVector3D();
+
+ return na.release();
+ }
+
+ aiNodeAnim* FBXConverter::GenerateScalingNodeAnim(const std::string& name,
+ const Model& /*target*/,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time)
+ {
+ std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+ na->mNodeName.Set(name);
+
+ ConvertScaleKeys(na.get(), curves, layer_map, start, stop, max_time, min_time);
+
+ // dummy rotation key
+ na->mRotationKeys = new aiQuatKey[1];
+ na->mNumRotationKeys = 1;
+
+ na->mRotationKeys[0].mTime = 0.;
+ na->mRotationKeys[0].mValue = aiQuaternion();
+
+ // dummy position key
+ na->mPositionKeys = new aiVectorKey[1];
+ na->mNumPositionKeys = 1;
+
+ na->mPositionKeys[0].mTime = 0.;
+ na->mPositionKeys[0].mValue = aiVector3D();
+
+ return na.release();
+ }
+
+ aiNodeAnim* FBXConverter::GenerateTranslationNodeAnim(const std::string& name,
+ const Model& /*target*/,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time,
+ bool inverse) {
+ std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+ na->mNodeName.Set(name);
+
+ ConvertTranslationKeys(na.get(), curves, layer_map, start, stop, max_time, min_time);
+
+ if (inverse) {
+ for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) {
+ na->mPositionKeys[i].mValue *= -1.0f;
+ }
+ }
+
+ // dummy scaling key
+ na->mScalingKeys = new aiVectorKey[1];
+ na->mNumScalingKeys = 1;
+
+ na->mScalingKeys[0].mTime = 0.;
+ na->mScalingKeys[0].mValue = aiVector3D(1.0f, 1.0f, 1.0f);
+
+ // dummy rotation key
+ na->mRotationKeys = new aiQuatKey[1];
+ na->mNumRotationKeys = 1;
+
+ na->mRotationKeys[0].mTime = 0.;
+ na->mRotationKeys[0].mValue = aiQuaternion();
+
+ return na.release();
+ }
+
+ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
+ const Model& target,
+ NodeMap::const_iterator chain[TransformationComp_MAXIMUM],
+ NodeMap::const_iterator iter_end,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time,
+ bool reverse_order)
+
+ {
+ std::unique_ptr<aiNodeAnim> na(new aiNodeAnim());
+ na->mNodeName.Set(name);
+
+ const PropertyTable& props = target.Props();
+
+ // need to convert from TRS order to SRT?
+ if (reverse_order) {
+
+ aiVector3D def_scale = PropertyGet(props, "Lcl Scaling", aiVector3D(1.f, 1.f, 1.f));
+ aiVector3D def_translate = PropertyGet(props, "Lcl Translation", aiVector3D(0.f, 0.f, 0.f));
+ aiVector3D def_rot = PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f));
+
+ KeyFrameListList scaling;
+ KeyFrameListList translation;
+ KeyFrameListList rotation;
+
+ if (chain[TransformationComp_Scaling] != iter_end) {
+ scaling = GetKeyframeList((*chain[TransformationComp_Scaling]).second, start, stop);
+ }
+
+ if (chain[TransformationComp_Translation] != iter_end) {
+ translation = GetKeyframeList((*chain[TransformationComp_Translation]).second, start, stop);
+ }
+
+ if (chain[TransformationComp_Rotation] != iter_end) {
+ rotation = GetKeyframeList((*chain[TransformationComp_Rotation]).second, start, stop);
+ }
+
+ KeyFrameListList joined;
+ joined.insert(joined.end(), scaling.begin(), scaling.end());
+ joined.insert(joined.end(), translation.begin(), translation.end());
+ joined.insert(joined.end(), rotation.begin(), rotation.end());
+
+ const KeyTimeList& times = GetKeyTimeList(joined);
+
+ aiQuatKey* out_quat = new aiQuatKey[times.size()];
+ aiVectorKey* out_scale = new aiVectorKey[times.size()];
+ aiVectorKey* out_translation = new aiVectorKey[times.size()];
+
+ if (times.size())
+ {
+ ConvertTransformOrder_TRStoSRT(out_quat, out_scale, out_translation,
+ scaling,
+ translation,
+ rotation,
+ times,
+ max_time,
+ min_time,
+ target.RotationOrder(),
+ def_scale,
+ def_translate,
+ def_rot);
+ }
+
+ // XXX remove duplicates / redundant keys which this operation did
+ // likely produce if not all three channels were equally dense.
+
+ na->mNumScalingKeys = static_cast<unsigned int>(times.size());
+ na->mNumRotationKeys = na->mNumScalingKeys;
+ na->mNumPositionKeys = na->mNumScalingKeys;
+
+ na->mScalingKeys = out_scale;
+ na->mRotationKeys = out_quat;
+ na->mPositionKeys = out_translation;
+ }
+ else {
+
+ // if a particular transformation is not given, grab it from
+ // the corresponding node to meet the semantics of aiNodeAnim,
+ // which requires all of rotation, scaling and translation
+ // to be set.
+ if (chain[TransformationComp_Scaling] != iter_end) {
+ ConvertScaleKeys(na.get(), (*chain[TransformationComp_Scaling]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time);
+ }
+ else {
+ na->mScalingKeys = new aiVectorKey[1];
+ na->mNumScalingKeys = 1;
+
+ na->mScalingKeys[0].mTime = 0.;
+ na->mScalingKeys[0].mValue = PropertyGet(props, "Lcl Scaling",
+ aiVector3D(1.f, 1.f, 1.f));
+ }
+
+ if (chain[TransformationComp_Rotation] != iter_end) {
+ ConvertRotationKeys(na.get(), (*chain[TransformationComp_Rotation]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time,
+ target.RotationOrder());
+ }
+ else {
+ na->mRotationKeys = new aiQuatKey[1];
+ na->mNumRotationKeys = 1;
+
+ na->mRotationKeys[0].mTime = 0.;
+ na->mRotationKeys[0].mValue = EulerToQuaternion(
+ PropertyGet(props, "Lcl Rotation", aiVector3D(0.f, 0.f, 0.f)),
+ target.RotationOrder());
+ }
+
+ if (chain[TransformationComp_Translation] != iter_end) {
+ ConvertTranslationKeys(na.get(), (*chain[TransformationComp_Translation]).second,
+ layer_map,
+ start, stop,
+ max_time,
+ min_time);
+ }
+ else {
+ na->mPositionKeys = new aiVectorKey[1];
+ na->mNumPositionKeys = 1;
+
+ na->mPositionKeys[0].mTime = 0.;
+ na->mPositionKeys[0].mValue = PropertyGet(props, "Lcl Translation",
+ aiVector3D(0.f, 0.f, 0.f));
+ }
+
+ }
+ return na.release();
+ }
+
+ FBXConverter::KeyFrameListList FBXConverter::GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop)
+ {
+ KeyFrameListList inputs;
+ inputs.reserve(nodes.size() * 3);
+
+ //give some breathing room for rounding errors
+ int64_t adj_start = start - 10000;
+ int64_t adj_stop = stop + 10000;
+
+ for (const AnimationCurveNode* node : nodes) {
+ ai_assert(node);
+
+ const AnimationCurveMap& curves = node->Curves();
+ for (const AnimationCurveMap::value_type& kv : curves) {
+
+ unsigned int mapto;
+ if (kv.first == "d|X") {
+ mapto = 0;
+ }
+ else if (kv.first == "d|Y") {
+ mapto = 1;
+ }
+ else if (kv.first == "d|Z") {
+ mapto = 2;
+ }
+ else {
+ FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component");
+ continue;
+ }
+
+ const AnimationCurve* const curve = kv.second;
+ ai_assert(curve->GetKeys().size() == curve->GetValues().size() && curve->GetKeys().size());
+
+ //get values within the start/stop time window
+ std::shared_ptr<KeyTimeList> Keys(new KeyTimeList());
+ std::shared_ptr<KeyValueList> Values(new KeyValueList());
+ const size_t count = curve->GetKeys().size();
+ Keys->reserve(count);
+ Values->reserve(count);
+ for (size_t n = 0; n < count; n++)
+ {
+ int64_t k = curve->GetKeys().at(n);
+ if (k >= adj_start && k <= adj_stop)
+ {
+ Keys->push_back(k);
+ Values->push_back(curve->GetValues().at(n));
+ }
+ }
+
+ inputs.push_back(std::make_tuple(Keys, Values, mapto));
+ }
+ }
+ return inputs; // pray for NRVO :-)
+ }
+
+
+ KeyTimeList FBXConverter::GetKeyTimeList(const KeyFrameListList& inputs) {
+ ai_assert(!inputs.empty());
+
+ // reserve some space upfront - it is likely that the key-frame lists
+ // have matching time values, so max(of all key-frame lists) should
+ // be a good estimate.
+ KeyTimeList keys;
+
+ size_t estimate = 0;
+ for (const KeyFrameList& kfl : inputs) {
+ estimate = std::max(estimate, std::get<0>(kfl)->size());
+ }
+
+ keys.reserve(estimate);
+
+ std::vector<unsigned int> next_pos;
+ next_pos.resize(inputs.size(), 0);
+
+ const size_t count = inputs.size();
+ while (true) {
+
+ int64_t min_tick = std::numeric_limits<int64_t>::max();
+ for (size_t i = 0; i < count; ++i) {
+ const KeyFrameList& kfl = inputs[i];
+
+ if (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) < min_tick) {
+ min_tick = std::get<0>(kfl)->at(next_pos[i]);
+ }
+ }
+
+ if (min_tick == std::numeric_limits<int64_t>::max()) {
+ break;
+ }
+ keys.push_back(min_tick);
+
+ for (size_t i = 0; i < count; ++i) {
+ const KeyFrameList& kfl = inputs[i];
+
+
+ while (std::get<0>(kfl)->size() > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == min_tick) {
+ ++next_pos[i];
+ }
+ }
+ }
+
+ return keys;
+ }
+
+ void FBXConverter::InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
+ const aiVector3D& def_value,
+ double& max_time,
+ double& min_time) {
+ ai_assert(!keys.empty());
+ ai_assert(nullptr != valOut);
+
+ std::vector<unsigned int> next_pos;
+ const size_t count(inputs.size());
+
+ next_pos.resize(inputs.size(), 0);
+
+ for (KeyTimeList::value_type time : keys) {
+ ai_real result[3] = { def_value.x, def_value.y, def_value.z };
+
+ for (size_t i = 0; i < count; ++i) {
+ const KeyFrameList& kfl = inputs[i];
+
+ const size_t ksize = std::get<0>(kfl)->size();
+ if (ksize == 0) {
+ continue;
+ }
+ if (ksize > next_pos[i] && std::get<0>(kfl)->at(next_pos[i]) == time) {
+ ++next_pos[i];
+ }
+
+ const size_t id0 = next_pos[i] > 0 ? next_pos[i] - 1 : 0;
+ const size_t id1 = next_pos[i] == ksize ? ksize - 1 : next_pos[i];
+
+ // use lerp for interpolation
+ const KeyValueList::value_type valueA = std::get<1>(kfl)->at(id0);
+ const KeyValueList::value_type valueB = std::get<1>(kfl)->at(id1);
+
+ const KeyTimeList::value_type timeA = std::get<0>(kfl)->at(id0);
+ const KeyTimeList::value_type timeB = std::get<0>(kfl)->at(id1);
+
+ const ai_real factor = timeB == timeA ? ai_real(0.) : static_cast<ai_real>((time - timeA)) / (timeB - timeA);
+ const ai_real interpValue = static_cast<ai_real>(valueA + (valueB - valueA) * factor);
+
+ result[std::get<2>(kfl)] = interpValue;
+ }
+
+ // magic value to convert fbx times to seconds
+ valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps;
+
+ min_time = std::min(min_time, valOut->mTime);
+ max_time = std::max(max_time, valOut->mTime);
+
+ valOut->mValue.x = result[0];
+ valOut->mValue.y = result[1];
+ valOut->mValue.z = result[2];
+
+ ++valOut;
+ }
+ }
+
+ void FBXConverter::InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
+ const aiVector3D& def_value,
+ double& maxTime,
+ double& minTime,
+ Model::RotOrder order)
+ {
+ ai_assert(!keys.empty());
+ ai_assert(nullptr != valOut);
+
+ std::unique_ptr<aiVectorKey[]> temp(new aiVectorKey[keys.size()]);
+ InterpolateKeys(temp.get(), keys, inputs, def_value, maxTime, minTime);
+
+ aiMatrix4x4 m;
+
+ aiQuaternion lastq;
+
+ for (size_t i = 0, c = keys.size(); i < c; ++i) {
+
+ valOut[i].mTime = temp[i].mTime;
+
+ GetRotationMatrix(order, temp[i].mValue, m);
+ aiQuaternion quat = aiQuaternion(aiMatrix3x3(m));
+
+ // take shortest path by checking the inner product
+ // http://www.3dkingdoms.com/weekly/weekly.php?a=36
+ if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0)
+ {
+ quat.x = -quat.x;
+ quat.y = -quat.y;
+ quat.z = -quat.z;
+ quat.w = -quat.w;
+ }
+ lastq = quat;
+
+ valOut[i].mValue = quat;
+ }
+ }
+
+ void FBXConverter::ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale,
+ aiVectorKey* out_translation,
+ const KeyFrameListList& scaling,
+ const KeyFrameListList& translation,
+ const KeyFrameListList& rotation,
+ const KeyTimeList& times,
+ double& maxTime,
+ double& minTime,
+ Model::RotOrder order,
+ const aiVector3D& def_scale,
+ const aiVector3D& def_translate,
+ const aiVector3D& def_rotation)
+ {
+ if (rotation.size()) {
+ InterpolateKeys(out_quat, times, rotation, def_rotation, maxTime, minTime, order);
+ }
+ else {
+ for (size_t i = 0; i < times.size(); ++i) {
+ out_quat[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
+ out_quat[i].mValue = EulerToQuaternion(def_rotation, order);
+ }
+ }
+
+ if (scaling.size()) {
+ InterpolateKeys(out_scale, times, scaling, def_scale, maxTime, minTime);
+ }
+ else {
+ for (size_t i = 0; i < times.size(); ++i) {
+ out_scale[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
+ out_scale[i].mValue = def_scale;
+ }
+ }
+
+ if (translation.size()) {
+ InterpolateKeys(out_translation, times, translation, def_translate, maxTime, minTime);
+ }
+ else {
+ for (size_t i = 0; i < times.size(); ++i) {
+ out_translation[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
+ out_translation[i].mValue = def_translate;
+ }
+ }
+
+ const size_t count = times.size();
+ for (size_t i = 0; i < count; ++i) {
+ aiQuaternion& r = out_quat[i].mValue;
+ aiVector3D& s = out_scale[i].mValue;
+ aiVector3D& t = out_translation[i].mValue;
+
+ aiMatrix4x4 mat, temp;
+ aiMatrix4x4::Translation(t, mat);
+ mat *= aiMatrix4x4(r.GetMatrix());
+ mat *= aiMatrix4x4::Scaling(s, temp);
+
+ mat.Decompose(s, r, t);
+ }
+ }
+
+ aiQuaternion FBXConverter::EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order)
+ {
+ aiMatrix4x4 m;
+ GetRotationMatrix(order, rot, m);
+
+ return aiQuaternion(aiMatrix3x3(m));
+ }
+
+ void FBXConverter::ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/,
+ int64_t start, int64_t stop,
+ double& maxTime,
+ double& minTime)
+ {
+ ai_assert(nodes.size());
+
+ // XXX for now, assume scale should be blended geometrically (i.e. two
+ // layers should be multiplied with each other). There is a FBX
+ // property in the layer to specify the behaviour, though.
+
+ const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop);
+ const KeyTimeList& keys = GetKeyTimeList(inputs);
+
+ na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
+ na->mScalingKeys = new aiVectorKey[keys.size()];
+ if (keys.size() > 0)
+ InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime);
+ }
+
+ void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
+ const LayerMap& /*layers*/,
+ int64_t start, int64_t stop,
+ double& maxTime,
+ double& minTime)
+ {
+ ai_assert(nodes.size());
+
+ // XXX see notes in ConvertScaleKeys()
+ const KeyFrameListList& inputs = GetKeyframeList(nodes, start, stop);
+ const KeyTimeList& keys = GetKeyTimeList(inputs);
+
+ na->mNumPositionKeys = static_cast<unsigned int>(keys.size());
+ na->mPositionKeys = new aiVectorKey[keys.size()];
+ if (keys.size() > 0)
+ InterpolateKeys(na->mPositionKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime);
+ }
+
+ void FBXConverter::ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
+ const LayerMap& /*layers*/,
+ int64_t start, int64_t stop,
+ double& maxTime,
+ double& minTime,
+ Model::RotOrder order)
+ {
+ ai_assert(nodes.size());
+
+ // XXX see notes in ConvertScaleKeys()
+ const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes, start, stop);
+ const KeyTimeList& keys = GetKeyTimeList(inputs);
+
+ na->mNumRotationKeys = static_cast<unsigned int>(keys.size());
+ na->mRotationKeys = new aiQuatKey[keys.size()];
+ if (!keys.empty()) {
+ InterpolateKeys(na->mRotationKeys, keys, inputs, aiVector3D(0.0f, 0.0f, 0.0f), maxTime, minTime, order);
+ }
+ }
+
+ void FBXConverter::ConvertGlobalSettings() {
+ if (nullptr == out) {
+ return;
+ }
+
+ out->mMetaData = aiMetadata::Alloc(15);
+ out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis());
+ out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign());
+ out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis());
+ out->mMetaData->Set(3, "FrontAxisSign", doc.GlobalSettings().FrontAxisSign());
+ out->mMetaData->Set(4, "CoordAxis", doc.GlobalSettings().CoordAxis());
+ out->mMetaData->Set(5, "CoordAxisSign", doc.GlobalSettings().CoordAxisSign());
+ out->mMetaData->Set(6, "OriginalUpAxis", doc.GlobalSettings().OriginalUpAxis());
+ out->mMetaData->Set(7, "OriginalUpAxisSign", doc.GlobalSettings().OriginalUpAxisSign());
+ out->mMetaData->Set(8, "UnitScaleFactor", (double)doc.GlobalSettings().UnitScaleFactor());
+ out->mMetaData->Set(9, "OriginalUnitScaleFactor", doc.GlobalSettings().OriginalUnitScaleFactor());
+ out->mMetaData->Set(10, "AmbientColor", doc.GlobalSettings().AmbientColor());
+ out->mMetaData->Set(11, "FrameRate", (int)doc.GlobalSettings().TimeMode());
+ out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart());
+ out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop());
+ out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
+ }
+
+ void FBXConverter::TransferDataToScene()
+ {
+ ai_assert(!out->mMeshes);
+ ai_assert(!out->mNumMeshes);
+
+ // note: the trailing () ensures initialization with NULL - not
+ // many C++ users seem to know this, so pointing it out to avoid
+ // confusion why this code works.
+
+ if (meshes.size()) {
+ out->mMeshes = new aiMesh*[meshes.size()]();
+ out->mNumMeshes = static_cast<unsigned int>(meshes.size());
+
+ std::swap_ranges(meshes.begin(), meshes.end(), out->mMeshes);
+ }
+
+ if (materials.size()) {
+ out->mMaterials = new aiMaterial*[materials.size()]();
+ out->mNumMaterials = static_cast<unsigned int>(materials.size());
+
+ std::swap_ranges(materials.begin(), materials.end(), out->mMaterials);
+ }
+
+ if (animations.size()) {
+ out->mAnimations = new aiAnimation*[animations.size()]();
+ out->mNumAnimations = static_cast<unsigned int>(animations.size());
+
+ std::swap_ranges(animations.begin(), animations.end(), out->mAnimations);
+ }
+
+ if (lights.size()) {
+ out->mLights = new aiLight*[lights.size()]();
+ out->mNumLights = static_cast<unsigned int>(lights.size());
+
+ std::swap_ranges(lights.begin(), lights.end(), out->mLights);
+ }
+
+ if (cameras.size()) {
+ out->mCameras = new aiCamera*[cameras.size()]();
+ out->mNumCameras = static_cast<unsigned int>(cameras.size());
+
+ std::swap_ranges(cameras.begin(), cameras.end(), out->mCameras);
+ }
+
+ if (textures.size()) {
+ out->mTextures = new aiTexture*[textures.size()]();
+ out->mNumTextures = static_cast<unsigned int>(textures.size());
+
+ std::swap_ranges(textures.begin(), textures.end(), out->mTextures);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertToAssimpScene(aiScene* out, const Document& doc)
+ {
+ FBXConverter converter(out, doc);
+ }
+
+ } // !FBX
+} // !Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXConverter.h b/thirdparty/assimp/code/FBXConverter.h
new file mode 100644
index 0000000000..50637468b9
--- /dev/null
+++ b/thirdparty/assimp/code/FBXConverter.h
@@ -0,0 +1,461 @@
+/*
+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 FBXDConverter.h
+ * @brief FBX DOM to aiScene conversion
+ */
+#ifndef INCLUDED_AI_FBX_CONVERTER_H
+#define INCLUDED_AI_FBX_CONVERTER_H
+
+#include "FBXParser.h"
+#include "FBXMeshGeometry.h"
+#include "FBXDocument.h"
+#include "FBXUtil.h"
+#include "FBXProperties.h"
+#include "FBXImporter.h"
+#include <assimp/anim.h>
+#include <assimp/material.h>
+#include <assimp/light.h>
+#include <assimp/texture.h>
+#include <assimp/camera.h>
+#include <assimp/StringComparison.h>
+#include <unordered_map>
+#include <unordered_set>
+
+struct aiScene;
+struct aiNode;
+struct aiMaterial;
+
+struct morphKeyData {
+ std::vector<unsigned int> values;
+ std::vector<float> weights;
+};
+typedef std::map<int64_t, morphKeyData*> morphAnimData;
+
+namespace Assimp {
+namespace FBX {
+
+class Document;
+
+/**
+ * Convert a FBX #Document to #aiScene
+ * @param out Empty scene to be populated
+ * @param doc Parsed FBX document
+ */
+void ConvertToAssimpScene(aiScene* out, const Document& doc);
+
+/** Dummy class to encapsulate the conversion process */
+class FBXConverter {
+public:
+ /**
+ * The different parts that make up the final local transformation of a fbx-node
+ */
+ enum TransformationComp {
+ TransformationComp_GeometricScalingInverse = 0,
+ TransformationComp_GeometricRotationInverse,
+ TransformationComp_GeometricTranslationInverse,
+ TransformationComp_Translation,
+ TransformationComp_RotationOffset,
+ TransformationComp_RotationPivot,
+ TransformationComp_PreRotation,
+ TransformationComp_Rotation,
+ TransformationComp_PostRotation,
+ TransformationComp_RotationPivotInverse,
+ TransformationComp_ScalingOffset,
+ TransformationComp_ScalingPivot,
+ TransformationComp_Scaling,
+ TransformationComp_ScalingPivotInverse,
+ TransformationComp_GeometricTranslation,
+ TransformationComp_GeometricRotation,
+ TransformationComp_GeometricScaling,
+
+ TransformationComp_MAXIMUM
+ };
+
+public:
+ FBXConverter(aiScene* out, const Document& doc);
+ ~FBXConverter();
+
+private:
+ // ------------------------------------------------------------------------------------------------
+ // find scene root and trigger recursive scene conversion
+ void ConvertRootNode();
+
+ // ------------------------------------------------------------------------------------------------
+ // collect and assign child nodes
+ void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4());
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertLights(const Model& model, const std::string &orig_name );
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertCameras(const Model& model, const std::string &orig_name );
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertLight( const Light& light, const std::string &orig_name );
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertCamera( const Camera& cam, const std::string &orig_name );
+
+ // ------------------------------------------------------------------------------------------------
+ void GetUniqueName( const std::string &name, std::string& uniqueName );
+
+ // ------------------------------------------------------------------------------------------------
+ // this returns unified names usable within assimp identifiers (i.e. no space characters -
+ // while these would be allowed, they are a potential trouble spot so better not use them).
+ const char* NameTransformationComp(TransformationComp comp);
+
+ // ------------------------------------------------------------------------------------------------
+ // note: this returns the REAL fbx property names
+ const char* NameTransformationCompProperty(TransformationComp comp);
+
+ // ------------------------------------------------------------------------------------------------
+ aiVector3D TransformationCompDefaultValue(TransformationComp comp);
+
+ // ------------------------------------------------------------------------------------------------
+ void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out);
+ // ------------------------------------------------------------------------------------------------
+ /**
+ * checks if a node has more than just scaling, rotation and translation components
+ */
+ bool NeedsComplexTransformationChain(const Model& model);
+
+ // ------------------------------------------------------------------------------------------------
+ // note: name must be a FixNodeName() result
+ std::string NameTransformationChainNode(const std::string& name, TransformationComp comp);
+
+ // ------------------------------------------------------------------------------------------------
+ /**
+ * note: memory for output_nodes will be managed by the caller
+ */
+ void GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes);
+
+ // ------------------------------------------------------------------------------------------------
+ void SetupNodeMetadata(const Model& model, aiNode& nd);
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform);
+
+ // ------------------------------------------------------------------------------------------------
+ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
+ std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd);
+
+ // ------------------------------------------------------------------------------------------------
+ std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd);
+
+ // ------------------------------------------------------------------------------------------------
+ aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd);
+
+ // ------------------------------------------------------------------------------------------------
+ unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd);
+
+ // ------------------------------------------------------------------------------------------------
+ std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
+ const aiMatrix4x4& node_global_transform, aiNode& nd);
+
+ // ------------------------------------------------------------------------------------------------
+ unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
+ MatIndexArray::value_type index,
+ const aiMatrix4x4& node_global_transform, aiNode& nd);
+
+ // ------------------------------------------------------------------------------------------------
+ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
+ static_cast<unsigned int>(-1);
+
+ // ------------------------------------------------------------------------------------------------
+ /**
+ * - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into
+ * account when determining which weights to include.
+ * - outputVertStartIndices is only used when a material index is specified, it gives for
+ * each output vertex the DOM index it maps to.
+ */
+ void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
+ const aiMatrix4x4& node_global_transform = aiMatrix4x4(),
+ unsigned int materialIndex = NO_MATERIAL_SEPARATION,
+ std::vector<unsigned int>* outputVertStartIndices = NULL);
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
+ std::vector<size_t>& out_indices,
+ std::vector<size_t>& index_out_indices,
+ std::vector<size_t>& count_out_indices,
+ const aiMatrix4x4& node_global_transform);
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
+ MatIndexArray::value_type materialIndex);
+
+ // ------------------------------------------------------------------------------------------------
+ unsigned int GetDefaultMaterial();
+
+ // ------------------------------------------------------------------------------------------------
+ // Material -> aiMaterial
+ unsigned int ConvertMaterial(const Material& material, const MeshGeometry* const mesh);
+
+ // ------------------------------------------------------------------------------------------------
+ // Video -> aiTexture
+ unsigned int ConvertVideo(const Video& video);
+
+ // ------------------------------------------------------------------------------------------------
+ // convert embedded texture if necessary and return actual texture path
+ aiString GetTexturePath(const Texture* tex);
+
+ // ------------------------------------------------------------------------------------------------
+ void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
+ const std::string& propName,
+ aiTextureType target, const MeshGeometry* const mesh);
+
+ // ------------------------------------------------------------------------------------------------
+ void TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures,
+ const std::string& propName,
+ aiTextureType target, const MeshGeometry* const mesh);
+
+ // ------------------------------------------------------------------------------------------------
+ void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh);
+
+ // ------------------------------------------------------------------------------------------------
+ void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh);
+
+ // ------------------------------------------------------------------------------------------------
+ aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName,
+ bool& result);
+ aiColor3D GetColorPropertyFactored(const PropertyTable& props, const std::string& colorName,
+ const std::string& factorName, bool& result, bool useTemplate = true);
+ aiColor3D GetColorProperty(const PropertyTable& props, const std::string& colorName,
+ bool& result, bool useTemplate = true);
+
+ // ------------------------------------------------------------------------------------------------
+ void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props);
+ void SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTable& props, const TextureMap& textures, const MeshGeometry* const mesh);
+
+ // ------------------------------------------------------------------------------------------------
+ // get the number of fps for a FrameRate enumerated value
+ static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0);
+
+ // ------------------------------------------------------------------------------------------------
+ // convert animation data to aiAnimation et al
+ void ConvertAnimations();
+
+ // ------------------------------------------------------------------------------------------------
+ // takes a fbx node name and returns the identifier to be used in the assimp output scene.
+ // the function is guaranteed to provide consistent results over multiple invocations
+ // UNLESS RenameNode() is called for a particular node name.
+ std::string FixNodeName(const std::string& name);
+ std::string FixAnimMeshName(const std::string& name);
+
+ typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap;
+
+ // XXX: better use multi_map ..
+ typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap;
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertAnimationStack(const AnimationStack& st);
+
+ // ------------------------------------------------------------------------------------------------
+ void ProcessMorphAnimDatas(std::map<std::string, morphAnimData*>* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node);
+
+ // ------------------------------------------------------------------------------------------------
+ void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims,
+ const std::string& fixed_name,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time);
+
+ // ------------------------------------------------------------------------------------------------
+ bool IsRedundantAnimationData(const Model& target,
+ TransformationComp comp,
+ const std::vector<const AnimationCurveNode*>& curves);
+
+ // ------------------------------------------------------------------------------------------------
+ aiNodeAnim* GenerateRotationNodeAnim(const std::string& name,
+ const Model& target,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time);
+
+ // ------------------------------------------------------------------------------------------------
+ aiNodeAnim* GenerateScalingNodeAnim(const std::string& name,
+ const Model& /*target*/,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time);
+
+ // ------------------------------------------------------------------------------------------------
+ aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name,
+ const Model& /*target*/,
+ const std::vector<const AnimationCurveNode*>& curves,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time,
+ bool inverse = false);
+
+ // ------------------------------------------------------------------------------------------------
+ // generate node anim, extracting only Rotation, Scaling and Translation from the given chain
+ aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name,
+ const Model& target,
+ NodeMap::const_iterator chain[TransformationComp_MAXIMUM],
+ NodeMap::const_iterator iter_end,
+ const LayerMap& layer_map,
+ int64_t start, int64_t stop,
+ double& max_time,
+ double& min_time,
+ bool reverse_order = false);
+
+ // key (time), value, mapto (component index)
+ typedef std::tuple<std::shared_ptr<KeyTimeList>, std::shared_ptr<KeyValueList>, unsigned int > KeyFrameList;
+ typedef std::vector<KeyFrameList> KeyFrameListList;
+
+ // ------------------------------------------------------------------------------------------------
+ KeyFrameListList GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop);
+
+ // ------------------------------------------------------------------------------------------------
+ KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs);
+
+ // ------------------------------------------------------------------------------------------------
+ void InterpolateKeys(aiVectorKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
+ const aiVector3D& def_value,
+ double& max_time,
+ double& min_time);
+
+ // ------------------------------------------------------------------------------------------------
+ void InterpolateKeys(aiQuatKey* valOut, const KeyTimeList& keys, const KeyFrameListList& inputs,
+ const aiVector3D& def_value,
+ double& maxTime,
+ double& minTime,
+ Model::RotOrder order);
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale,
+ aiVectorKey* out_translation,
+ const KeyFrameListList& scaling,
+ const KeyFrameListList& translation,
+ const KeyFrameListList& rotation,
+ const KeyTimeList& times,
+ double& maxTime,
+ double& minTime,
+ Model::RotOrder order,
+ const aiVector3D& def_scale,
+ const aiVector3D& def_translate,
+ const aiVector3D& def_rotation);
+
+ // ------------------------------------------------------------------------------------------------
+ // euler xyz -> quat
+ aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order);
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/,
+ int64_t start, int64_t stop,
+ double& maxTime,
+ double& minTime);
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
+ const LayerMap& /*layers*/,
+ int64_t start, int64_t stop,
+ double& maxTime,
+ double& minTime);
+
+ // ------------------------------------------------------------------------------------------------
+ void ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
+ const LayerMap& /*layers*/,
+ int64_t start, int64_t stop,
+ double& maxTime,
+ double& minTime,
+ Model::RotOrder order);
+
+ void ConvertGlobalSettings();
+
+ // ------------------------------------------------------------------------------------------------
+ // copy generated meshes, animations, lights, cameras and textures to the output scene
+ void TransferDataToScene();
+
+private:
+ // 0: not assigned yet, others: index is value - 1
+ unsigned int defaultMaterialIndex;
+
+ std::vector<aiMesh*> meshes;
+ std::vector<aiMaterial*> materials;
+ std::vector<aiAnimation*> animations;
+ std::vector<aiLight*> lights;
+ std::vector<aiCamera*> cameras;
+ std::vector<aiTexture*> textures;
+
+ using MaterialMap = std::map<const Material*, unsigned int>;
+ MaterialMap materials_converted;
+
+ using VideoMap = std::map<const Video*, unsigned int>;
+ VideoMap textures_converted;
+
+ using MeshMap = std::map<const Geometry*, std::vector<unsigned int> >;
+ MeshMap meshes_converted;
+
+ // fixed node name -> which trafo chain components have animations?
+ using NodeAnimBitMap = std::map<std::string, unsigned int> ;
+ NodeAnimBitMap node_anim_chain_bits;
+
+ // number of nodes with the same name
+ using NodeAnimNameMap = std::unordered_map<std::string, unsigned int>;
+ NodeAnimNameMap mNodeNameInstances;
+
+ using NodeNameCache = std::unordered_set<std::string>;
+ NodeNameCache mNodeNames;
+
+ double anim_fps;
+
+ aiScene* const out;
+ const FBX::Document& doc;
+};
+
+}
+}
+
+#endif // INCLUDED_AI_FBX_CONVERTER_H
diff --git a/thirdparty/assimp/code/FBXDeformer.cpp b/thirdparty/assimp/code/FBXDeformer.cpp
new file mode 100644
index 0000000000..6927553450
--- /dev/null
+++ b/thirdparty/assimp/code/FBXDeformer.cpp
@@ -0,0 +1,213 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXMeshGeometry.h"
+#include "FBXImporter.h"
+#include "FBXDocumentUtil.h"
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+ : Object(id,element,name)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2));
+ props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Deformer::~Deformer()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Deformer(id,element,doc,name)
+, node()
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ const Element* const Indexes = sc["Indexes"];
+ const Element* const Weights = sc["Weights"];
+
+ const Element& Transform = GetRequiredElement(sc,"Transform",&element);
+ const Element& TransformLink = GetRequiredElement(sc,"TransformLink",&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* const mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
+ if(mod) {
+ node = mod;
+ break;
+ }
+ }
+
+ if (!node) {
+ DOMError("failed to read target Node for Cluster",&element);
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Cluster::~Cluster()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Deformer(id,element,doc,name)
+, accuracy( 0.0f ) {
+ const Scope& sc = GetRequiredScope(element);
+
+ const Element* const Link_DeformAcuracy = sc["Link_DeformAcuracy"];
+ if(Link_DeformAcuracy) {
+ accuracy = ParseTokenAsFloat(GetRequiredToken(*Link_DeformAcuracy,0));
+ }
+
+ // resolve assigned clusters
+ const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
+
+ clusters.reserve(conns.size());
+ for(const Connection* con : conns) {
+
+ const Cluster* const cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element);
+ if(cluster) {
+ clusters.push_back(cluster);
+ continue;
+ }
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Skin::~Skin()
+{
+
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::BlendShape(uint64_t id, const Element& 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* const bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
+ if (bspc) {
+ blendShapeChannels.push_back(bspc);
+ continue;
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::~BlendShape()
+{
+
+}
+// ------------------------------------------------------------------------------------------------
+BlendShapeChannel::BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+ : Deformer(id, element, doc, name)
+{
+ const Scope& sc = GetRequiredScope(element);
+ const Element* const DeformPercent = sc["DeformPercent"];
+ if (DeformPercent) {
+ percent = ParseTokenAsFloat(GetRequiredToken(*DeformPercent, 0));
+ }
+ const Element* const FullWeights = sc["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()
+{
+
+}
+// ------------------------------------------------------------------------------------------------
+}
+}
+#endif
+
diff --git a/thirdparty/assimp/code/FBXDocument.cpp b/thirdparty/assimp/code/FBXDocument.cpp
new file mode 100644
index 0000000000..1af08fe6d8
--- /dev/null
+++ b/thirdparty/assimp/code/FBXDocument.cpp
@@ -0,0 +1,726 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXDocument.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "FBXUtil.h"
+#include "FBXImporter.h"
+#include "FBXImportSettings.h"
+#include "FBXDocumentUtil.h"
+#include "FBXProperties.h"
+
+#include <memory>
+#include <functional>
+#include <map>
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
+: doc(doc)
+, element(element)
+, id(id)
+, flags() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::~LazyObject()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const Object* LazyObject::Get(bool dieOnError)
+{
+ if(IsBeingConstructed() || FailedToConstruct()) {
+ return nullptr;
+ }
+
+ if (object.get()) {
+ return object.get();
+ }
+
+ // if this is the root object, we return a dummy since there
+ // is no root object int he fbx file - it is just referenced
+ // with id 0.
+ if(id == 0L) {
+ object.reset(new Object(id, element, "Model::RootNode"));
+ return object.get();
+ }
+
+ const Token& key = element.KeyToken();
+ const TokenList& tokens = element.Tokens();
+
+ if(tokens.size() < 3) {
+ DOMError("expected at least 3 tokens: id, name and class tag",&element);
+ }
+
+ const char* err;
+ 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;
+
+ try {
+ // 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());
+
+ // For debugging
+ //dumpObjectClassInfo( objtype, classtag );
+
+ 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")) {
+ object.reset(new LimbNode(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 ) ) {
+ // FK and IK effectors are not supported
+ if ( strcmp( classtag.c_str(), "IKEffector" ) && strcmp( classtag.c_str(), "FKEffector" ) ) {
+ 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));
+ }
+ // note: order matters for these two
+ 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));
+ }
+ }
+ catch(std::exception& ex) {
+ flags &= ~BEING_CONSTRUCTED;
+ flags |= FAILED_TO_CONSTRUCT;
+
+ if(dieOnError || doc.Settings().strictMode) {
+ throw;
+ }
+
+ // note: the error message is already formatted, so raw logging is ok
+ if(!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_ERROR(ex.what());
+ }
+ return NULL;
+ }
+
+ if (!object.get()) {
+ //DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
+ }
+
+ flags &= ~BEING_CONSTRUCTED;
+ return object.get();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::Object(uint64_t id, const Element& element, const std::string& name)
+: element(element)
+, name(name)
+, id(id)
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::~Object()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props)
+: props(props)
+, doc(doc)
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::~FileGlobalSettings()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Document::Document(const Parser& parser, const ImportSettings& settings)
+: settings(settings)
+, parser(parser)
+{
+ // Cannot use array default initialization syntax because vc8 fails on it
+ for (auto &timeStamp : creationTimeStamp) {
+ timeStamp = 0;
+ }
+
+ ReadHeader();
+ 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(ObjectMap::value_type& v : objects) {
+ delete v.second;
+ }
+
+ for(ConnectionMap::value_type& v : src_connections) {
+ delete v.second;
+ }
+ // |dest_connections| contain the same Connection objects as the |src_connections|
+}
+
+// ------------------------------------------------------------------------------------------------
+static const unsigned int LowerSupportedVersion = 7100;
+static const unsigned int UpperSupportedVersion = 7400;
+
+void Document::ReadHeader() {
+ // Read ID objects from "Objects" section
+ const Scope& sc = parser.GetRootScope();
+ const Element* const ehead = sc["FBXHeaderExtension"];
+ if(!ehead || !ehead->Compound()) {
+ DOMError("no FBXHeaderExtension dictionary found");
+ }
+
+ const Scope& 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 ) {
+ DOMError("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013");
+ }
+ if(fbxVersion > UpperSupportedVersion ) {
+ if(Settings().strictMode) {
+ DOMError("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013"
+ " (turn off strict mode to try anyhow) ");
+ }
+ else {
+ DOMWarning("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013,"
+ " trying to read it nevertheless");
+ }
+ }
+
+ const Element* const ecreator = shead["Creator"];
+ if(ecreator) {
+ creator = ParseTokenAsString(GetRequiredToken(*ecreator,0));
+ }
+
+ const Element* const etimestamp = shead["CreationTimeStamp"];
+ if(etimestamp && etimestamp->Compound()) {
+ const Scope& 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));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadGlobalSettings()
+{
+ const Scope& sc = parser.GetRootScope();
+ const Element* const ehead = sc["GlobalSettings"];
+ if ( nullptr == ehead || !ehead->Compound() ) {
+ DOMWarning( "no GlobalSettings dictionary found" );
+ globals.reset(new FileGlobalSettings(*this, std::make_shared<const PropertyTable>()));
+ return;
+ }
+
+ std::shared_ptr<const PropertyTable> props = GetPropertyTable( *this, "", *ehead, *ehead->Compound(), true );
+
+ //double v = PropertyGet<float>( *props.get(), std::string("UnitScaleFactor"), 1.0 );
+
+ if(!props) {
+ DOMError("GlobalSettings dictionary contains no property table");
+ }
+
+ globals.reset(new FileGlobalSettings(*this, props));
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadObjects()
+{
+ // read ID objects from "Objects" section
+ const Scope& sc = parser.GetRootScope();
+ const Element* const eobjects = sc["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 Scope& sobjects = *eobjects->Compound();
+ for(const ElementMap::value_type& el : sobjects.Elements()) {
+
+ // extract ID
+ const TokenList& tok = el.second->Tokens();
+
+ if (tok.empty()) {
+ DOMError("expected ID after object key",el.second);
+ }
+
+ const char* err;
+ const uint64_t id = ParseTokenAsID(*tok[0], err);
+ if(err) {
+ DOMError(err,el.second);
+ }
+
+ // id=0 is normally implicit
+ if(id == 0L) {
+ DOMError("encountered object with implicitly defined id 0",el.second);
+ }
+
+ if(objects.find(id) != objects.end()) {
+ DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
+ }
+
+ objects[id] = new LazyObject(id, *el.second, *this);
+
+ // grab all animation stacks upfront since there is no listing of them
+ if(!strcmp(el.first.c_str(),"AnimationStack")) {
+ animationStacks.push_back(id);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadPropertyTemplates()
+{
+ const Scope& sc = parser.GetRootScope();
+ // read property templates from "Definitions" section
+ const Element* const edefs = sc["Definitions"];
+ if(!edefs || !edefs->Compound()) {
+ DOMWarning("no Definitions dictionary found");
+ return;
+ }
+
+ const Scope& sdefs = *edefs->Compound();
+ const ElementCollection otypes = sdefs.GetCollection("ObjectType");
+ for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
+ const Element& el = *(*it).second;
+ const Scope* sc = el.Compound();
+ if(!sc) {
+ 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->GetCollection("PropertyTemplate");
+ for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) {
+ const Element& el = *(*it).second;
+ const Scope* sc = el.Compound();
+ if(!sc) {
+ DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el);
+ continue;
+ }
+
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ DOMWarning("expected name for PropertyTemplate element, ignoring",&el);
+ continue;
+ }
+
+ const std::string& pname = ParseTokenAsString(*tok[0]);
+
+ const Element* Properties70 = (*sc)["Properties70"];
+ if(Properties70) {
+ std::shared_ptr<const PropertyTable> props = std::make_shared<const PropertyTable>(
+ *Properties70,std::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL))
+ );
+
+ templates[oname+"."+pname] = props;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadConnections()
+{
+ const Scope& sc = parser.GetRootScope();
+ // read property templates from "Definitions" section
+ const Element* const econns = sc["Connections"];
+ if(!econns || !econns->Compound()) {
+ DOMError("no Connections dictionary found");
+ }
+
+ uint64_t insertionOrder = 0l;
+ const Scope& sconns = *econns->Compound();
+ const ElementCollection conns = sconns.GetCollection("C");
+ for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
+ const Element& 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* const lazy = GetObject(id);
+ const AnimationStack* stack;
+ if(!lazy || !(stack = lazy->Get<AnimationStack>())) {
+ DOMWarning("failed to read AnimationStack object");
+ continue;
+ }
+ 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
+
+{
+ ai_assert(classnames);
+ ai_assert( count != 0 );
+ ai_assert( count <= MAX_CLASSNAMES);
+
+ 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) {
+ const Token& 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)
+{
+ ai_assert(doc.Objects().find(src) != doc.Objects().end());
+ // dest may be 0 (root node)
+ ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
+}
+
+// ------------------------------------------------------------------------------------------------
+Connection::~Connection()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject& Connection::LazySourceObject() const
+{
+ LazyObject* const lazy = doc.GetObject(src);
+ ai_assert(lazy);
+ return *lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject& Connection::LazyDestinationObject() const
+{
+ LazyObject* const lazy = doc.GetObject(dest);
+ ai_assert(lazy);
+ return *lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Object* Connection::SourceObject() const
+{
+ LazyObject* const lazy = doc.GetObject(src);
+ ai_assert(lazy);
+ return lazy->Get();
+}
+
+// ------------------------------------------------------------------------------------------------
+const Object* Connection::DestinationObject() const
+{
+ LazyObject* const lazy = doc.GetObject(dest);
+ ai_assert(lazy);
+ return lazy->Get();
+}
+
+} // !FBX
+} // !Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXDocument.h b/thirdparty/assimp/code/FBXDocument.h
new file mode 100644
index 0000000000..c849defdcd
--- /dev/null
+++ b/thirdparty/assimp/code/FBXDocument.h
@@ -0,0 +1,1180 @@
+/*
+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.h
+ * @brief FBX DOM
+ */
+#ifndef INCLUDED_AI_FBX_DOCUMENT_H
+#define INCLUDED_AI_FBX_DOCUMENT_H
+
+#include <numeric>
+#include <stdint.h>
+#include <assimp/mesh.h>
+#include "FBXProperties.h"
+#include "FBXParser.h"
+
+#define _AI_CONCAT(a,b) a ## b
+#define AI_CONCAT(a,b) _AI_CONCAT(a,b)
+
+namespace Assimp {
+namespace FBX {
+
+class Parser;
+class Object;
+struct ImportSettings;
+
+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;
+
+
+/** 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 Element& element, const Document& doc);
+
+ ~LazyObject();
+
+ const Object* Get(bool dieOnError = false);
+
+ template <typename T>
+ const T* Get(bool dieOnError = false) {
+ const Object* const ob = Get(dieOnError);
+ return ob ? dynamic_cast<const T*>(ob) : NULL;
+ }
+
+ uint64_t ID() const {
+ return id;
+ }
+
+ bool IsBeingConstructed() const {
+ return (flags & BEING_CONSTRUCTED) != 0;
+ }
+
+ bool FailedToConstruct() const {
+ return (flags & FAILED_TO_CONSTRUCT) != 0;
+ }
+
+ const Element& GetElement() const {
+ return element;
+ }
+
+ const Document& GetDocument() const {
+ return doc;
+ }
+
+private:
+ const Document& doc;
+ const Element& element;
+ std::unique_ptr<const Object> object;
+
+ const uint64_t id;
+
+ enum Flags {
+ BEING_CONSTRUCTED = 0x1,
+ FAILED_TO_CONSTRUCT = 0x2
+ };
+
+ unsigned int flags;
+};
+
+/** Base class for in-memory (DOM) representations of FBX objects */
+class Object {
+public:
+ Object(uint64_t id, const Element& element, const std::string& name);
+
+ virtual ~Object();
+
+ const Element& SourceElement() const {
+ return element;
+ }
+
+ const std::string& Name() const {
+ return name;
+ }
+
+ uint64_t ID() const {
+ return id;
+ }
+
+protected:
+ const Element& element;
+ const std::string name;
+ const uint64_t id;
+};
+
+/** 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 Element& element, const Document& doc, const std::string& name);
+
+ virtual ~NodeAttribute();
+
+ const PropertyTable& Props() const {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+private:
+ std::shared_ptr<const PropertyTable> props;
+};
+
+/** DOM base class for FBX camera settings attached to a node */
+class CameraSwitcher : public NodeAttribute {
+public:
+ CameraSwitcher(uint64_t id, const Element& 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)) { \
+ ai_assert(static_cast<int>(default_value) >= 0 && static_cast<int>(default_value) < AI_CONCAT(type, _MAX)); \
+ return static_cast<type>(default_value); \
+ } \
+ return static_cast<type>(ival); \
+}
+
+
+/** DOM base class for FBX cameras attached to a node */
+class Camera : public NodeAttribute {
+public:
+ Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
+ virtual ~Camera();
+
+ fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0))
+ fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0))
+ fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(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 Element& 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 Element& 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 Element& 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, aiVector3D, aiVector3D(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, aiVector3D, aiVector3D(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)
+};
+
+/** 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
+ };
+
+ enum TransformInheritance {
+ TransformInheritance_RrSs = 0,
+ TransformInheritance_RSrs,
+ TransformInheritance_Rrs,
+
+ TransformInheritance_MAX // end-of-enum sentinel
+ };
+
+ Model(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
+ virtual ~Model();
+
+ fbx_simple_property(QuaternionInterpolate, int, 0)
+
+ fbx_simple_property(RotationOffset, aiVector3D, aiVector3D())
+ fbx_simple_property(RotationPivot, aiVector3D, aiVector3D())
+ fbx_simple_property(ScalingOffset, aiVector3D, aiVector3D())
+ fbx_simple_property(ScalingPivot, aiVector3D, aiVector3D())
+ fbx_simple_property(TranslationActive, bool, false)
+
+ fbx_simple_property(TranslationMin, aiVector3D, aiVector3D())
+ fbx_simple_property(TranslationMax, aiVector3D, aiVector3D())
+
+ 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, aiVector3D, aiVector3D())
+ fbx_simple_property(PostRotation, aiVector3D, aiVector3D())
+ fbx_simple_property(RotationActive, bool, false)
+
+ fbx_simple_property(RotationMin, aiVector3D, aiVector3D())
+ fbx_simple_property(RotationMax, aiVector3D, aiVector3D())
+
+ 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, aiVector3D, aiVector3D())
+ fbx_simple_property(ScalingMax, aiVector3D, aiVector3D(1.f,1.f,1.f))
+ 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, aiVector3D, aiVector3D())
+ fbx_simple_property(GeometricRotation, aiVector3D, aiVector3D())
+ fbx_simple_property(GeometricScaling, aiVector3D, aiVector3D(1.f, 1.f, 1.f))
+
+ 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 {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+ /** 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 Element& 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;
+ std::shared_ptr<const PropertyTable> props;
+};
+
+/** DOM class for generic FBX textures */
+class Texture : public Object {
+public:
+ Texture(uint64_t id, const Element& 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 aiVector2D& UVTranslation() const {
+ return uvTrans;
+ }
+
+ const aiVector2D& UVScaling() const {
+ return uvScaling;
+ }
+
+ const PropertyTable& Props() const {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+ // return a 4-tuple
+ const unsigned int* Crop() const {
+ return crop;
+ }
+
+ const Video* Media() const {
+ return media;
+ }
+
+private:
+ aiVector2D uvTrans;
+ aiVector2D uvScaling;
+
+ std::string type;
+ std::string relativeFileName;
+ std::string fileName;
+ std::string alphaSource;
+ std::shared_ptr<const PropertyTable> props;
+
+ unsigned int crop[4];
+
+ const Video* media;
+};
+
+/** DOM class for layered FBX textures */
+class LayeredTexture : public Object {
+public:
+ LayeredTexture(uint64_t id, const Element& 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::fbx_unordered_map<std::string, const Texture*> TextureMap;
+typedef std::fbx_unordered_map<std::string, const LayeredTexture*> LayeredTextureMap;
+
+
+/** DOM class for generic FBX videos */
+class Video : public Object {
+public:
+ Video(uint64_t id, const Element& 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 {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+ const uint8_t* Content() const {
+ ai_assert(content);
+ return content;
+ }
+
+ uint32_t ContentLength() const {
+ return contentLength;
+ }
+
+ uint8_t* RelinquishContent() {
+ uint8_t* ptr = content;
+ content = 0;
+ return ptr;
+ }
+
+private:
+ std::string type;
+ std::string relativeFileName;
+ std::string fileName;
+ std::shared_ptr<const PropertyTable> props;
+
+ uint32_t contentLength;
+ uint8_t* content;
+};
+
+/** DOM class for generic FBX materials */
+class Material : public Object {
+public:
+ Material(uint64_t id, const Element& 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 {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+ const TextureMap& Textures() const {
+ return textures;
+ }
+
+ const LayeredTextureMap& LayeredTextures() const {
+ return layeredTextures;
+ }
+
+private:
+ std::string shading;
+ bool multilayer;
+ std::shared_ptr<const PropertyTable> props;
+
+ TextureMap textures;
+ LayeredTextureMap layeredTextures;
+};
+
+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 Element& 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::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::vector<unsigned int> flags;
+};
+
+// property-name -> animation curve
+typedef std::map<std::string, const AnimationCurve*> AnimationCurveMap;
+
+/** 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 Element& element, const std::string& name, const Document& doc,
+ const char* const * target_prop_whitelist = NULL, size_t whitelist_size = 0);
+
+ virtual ~AnimationCurveNode();
+
+ const PropertyTable& Props() const {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+
+ const AnimationCurveMap& 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.*/
+ const Object* Target() const {
+ return target;
+ }
+
+ const Model* TargetAsModel() const {
+ return dynamic_cast<const Model*>(target);
+ }
+
+ const NodeAttribute* TargetAsNodeAttribute() const {
+ return dynamic_cast<const NodeAttribute*>(target);
+ }
+
+ /** Property of Target() that is being animated*/
+ const std::string& TargetProperty() const {
+ return prop;
+ }
+
+private:
+ const Object* target;
+ std::shared_ptr<const PropertyTable> props;
+ mutable AnimationCurveMap curves;
+
+ std::string prop;
+ const Document& doc;
+};
+
+typedef std::vector<const AnimationCurveNode*> AnimationCurveNodeList;
+
+/** Represents a FBX animation layer (i.e. a list of node animations) */
+class AnimationLayer : public Object {
+public:
+ AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc);
+ virtual ~AnimationLayer();
+
+ const PropertyTable& Props() const {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+ /* 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. */
+ AnimationCurveNodeList Nodes(const char* const * target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
+
+private:
+ std::shared_ptr<const PropertyTable> props;
+ const Document& doc;
+};
+
+typedef std::vector<const AnimationLayer*> AnimationLayerList;
+
+/** Represents a FBX animation stack (i.e. a list of animation layers) */
+class AnimationStack : public Object {
+public:
+ AnimationStack(uint64_t id, const Element& 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 {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+ const AnimationLayerList& Layers() const {
+ return layers;
+ }
+
+private:
+ std::shared_ptr<const PropertyTable> props;
+ AnimationLayerList layers;
+};
+
+
+/** DOM class for deformers */
+class Deformer : public Object {
+public:
+ Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+ virtual ~Deformer();
+
+ const PropertyTable& Props() const {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+private:
+ std::shared_ptr<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 Element& 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 Element& 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 Element& 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 WeightArray& 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 WeightIndexArray& GetIndices() const {
+ return indices;
+ }
+
+ /** */
+ const aiMatrix4x4& Transform() const {
+ return transform;
+ }
+
+ const aiMatrix4x4& TransformLink() const {
+ return transformLink;
+ }
+
+ const Model* TargetNode() const {
+ return node;
+ }
+
+private:
+ WeightArray weights;
+ WeightIndexArray indices;
+
+ aiMatrix4x4 transform;
+ aiMatrix4x4 transformLink;
+
+ const Model* node;
+};
+
+/** DOM class for skin deformers */
+class Skin : public Deformer {
+public:
+ Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
+ virtual ~Skin();
+
+ float DeformAccuracy() const {
+ return accuracy;
+ }
+
+ const std::vector<const Cluster*>& Clusters() const {
+ return clusters;
+ }
+
+private:
+ float accuracy;
+ 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.
+ const Object* SourceObject() const;
+ 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::fbx_unordered_map<std::string, std::shared_ptr<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, std::shared_ptr<const PropertyTable> props);
+
+ ~FileGlobalSettings();
+
+ const PropertyTable& Props() const {
+ ai_assert(props.get());
+ return *props.get();
+ }
+
+ 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, aiVector3D, aiVector3D(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:
+ std::shared_ptr<const PropertyTable> props;
+ 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 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& GlobalSettings() const {
+ ai_assert(globals.get());
+ 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;
+
+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;
+ void ReadHeader();
+ void ReadObjects();
+ void ReadPropertyTemplates();
+ void ReadConnections();
+ void ReadGlobalSettings();
+
+private:
+ const ImportSettings& settings;
+
+ ObjectMap objects;
+ const Parser& parser;
+
+ PropertyTemplateMap templates;
+ ConnectionMap src_connections;
+ ConnectionMap dest_connections;
+
+ unsigned int fbxVersion;
+ std::string creator;
+ unsigned int creationTimeStamp[7];
+
+ std::vector<uint64_t> animationStacks;
+ mutable std::vector<const AnimationStack*> animationStacksResolved;
+
+ std::unique_ptr<FileGlobalSettings> globals;
+};
+
+} // Namespace FBX
+} // Namespace Assimp
+
+#endif // INCLUDED_AI_FBX_DOCUMENT_H
diff --git a/thirdparty/assimp/code/FBXDocumentUtil.cpp b/thirdparty/assimp/code/FBXDocumentUtil.cpp
new file mode 100644
index 0000000000..f84691479a
--- /dev/null
+++ b/thirdparty/assimp/code/FBXDocumentUtil.cpp
@@ -0,0 +1,135 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXUtil.h"
+#include "FBXDocumentUtil.h"
+#include "FBXProperties.h"
+
+
+namespace Assimp {
+namespace FBX {
+namespace Util {
+
+// ------------------------------------------------------------------------------------------------
+// signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
+void DOMError(const std::string& message, const Token& token)
+{
+ throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token));
+}
+
+// ------------------------------------------------------------------------------------------------
+void DOMError(const std::string& message, const Element* element /*= NULL*/)
+{
+ if(element) {
+ DOMError(message,element->KeyToken());
+ }
+ throw DeadlyImportError("FBX-DOM " + message);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// print warning, do return
+void DOMWarning(const std::string& message, const Token& token)
+{
+ if(DefaultLogger::get()) {
+ ASSIMP_LOG_WARN(Util::AddTokenText("FBX-DOM",message,&token));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void DOMWarning(const std::string& message, const Element* element /*= NULL*/)
+{
+ if(element) {
+ DOMWarning(message,element->KeyToken());
+ return;
+ }
+ if(DefaultLogger::get()) {
+ ASSIMP_LOG_WARN("FBX-DOM: " + message);
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// fetch a property table and the corresponding property template
+std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
+ const std::string& templateName,
+ const Element &element,
+ const Scope& sc,
+ bool no_warn /*= false*/)
+{
+ const Element* const Properties70 = sc["Properties70"];
+ std::shared_ptr<const PropertyTable> templateProps = std::shared_ptr<const PropertyTable>(
+ static_cast<const PropertyTable*>(NULL));
+
+ 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 std::make_shared<const PropertyTable>();
+ }
+ }
+ return std::make_shared<const PropertyTable>(*Properties70,templateProps);
+}
+} // !Util
+} // !FBX
+} // !Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXDocumentUtil.h b/thirdparty/assimp/code/FBXDocumentUtil.h
new file mode 100644
index 0000000000..2450109e59
--- /dev/null
+++ b/thirdparty/assimp/code/FBXDocumentUtil.h
@@ -0,0 +1,120 @@
+/*
+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 INCLUDED_AI_FBX_DOCUMENT_UTIL_H
+#define INCLUDED_AI_FBX_DOCUMENT_UTIL_H
+
+#include <assimp/defs.h>
+#include <string>
+#include <memory>
+#include "FBXDocument.h"
+
+struct Token;
+struct Element;
+
+namespace Assimp {
+namespace FBX {
+namespace Util {
+
+/* DOM/Parse error reporting - does not return */
+AI_WONT_RETURN void DOMError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
+AI_WONT_RETURN void DOMError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX;
+
+// does return
+void DOMWarning(const std::string& message, const Token& token);
+void DOMWarning(const std::string& message, const Element* element = NULL);
+
+
+// fetch a property table and the corresponding property template
+std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
+ const std::string& templateName,
+ const Element &element,
+ const Scope& sc,
+ bool no_warn = false);
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline
+const T* ProcessSimpleConnection(const Connection& con,
+ bool is_object_property_conn,
+ const char* name,
+ const Element& 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();
+ }
+
+ const Object* const ob = con.SourceObject();
+ if(!ob) {
+ DOMWarning("failed to read source object for incoming " + std::string(name) +
+ " link, ignoring",
+ &element);
+ return nullptr;
+ }
+
+ return dynamic_cast<const T*>(ob);
+}
+
+} //!Util
+} //!FBX
+} //!Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXExportNode.cpp b/thirdparty/assimp/code/FBXExportNode.cpp
new file mode 100644
index 0000000000..e5215466a1
--- /dev/null
+++ b/thirdparty/assimp/code/FBXExportNode.cpp
@@ -0,0 +1,568 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#include "FBXExportNode.h"
+#include "FBXCommon.h"
+
+#include <assimp/StreamWriter.h> // StreamWriterLE
+#include <assimp/Exceptional.h> // DeadlyExportError
+#include <assimp/ai_assert.h>
+#include <assimp/StringUtils.h> // ai_snprintf
+
+#include <string>
+#include <ostream>
+#include <sstream> // ostringstream
+#include <memory> // shared_ptr
+
+// AddP70<type> helpers... there's no usable pattern here,
+// so all are defined as separate functions.
+// Even "animatable" properties are often completely different
+// from the standard (nonanimated) property definition,
+// so they are specified with an 'A' suffix.
+
+void FBX::Node::AddP70int(
+ const std::string& name, int32_t value
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "int", "Integer", "", value);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70bool(
+ const std::string& name, bool value
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "bool", "", "", int32_t(value));
+ AddChild(n);
+}
+
+void FBX::Node::AddP70double(
+ const std::string& name, double value
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "double", "Number", "", value);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70numberA(
+ const std::string& name, double value
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "Number", "", "A", value);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70color(
+ const std::string& name, double r, double g, double b
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "ColorRGB", "Color", "", r, g, b);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70colorA(
+ const std::string& name, double r, double g, double b
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "Color", "", "A", r, g, b);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70vector(
+ const std::string& name, double x, double y, double z
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "Vector3D", "Vector", "", x, y, z);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70vectorA(
+ const std::string& name, double x, double y, double z
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "Vector", "", "A", x, y, z);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70string(
+ const std::string& name, const std::string& value
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "KString", "", "", value);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70enum(
+ const std::string& name, int32_t value
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "enum", "", "", value);
+ AddChild(n);
+}
+
+void FBX::Node::AddP70time(
+ const std::string& name, int64_t value
+) {
+ FBX::Node n("P");
+ n.AddProperties(name, "KTime", "Time", "", value);
+ AddChild(n);
+}
+
+
+// public member functions for writing nodes to stream
+
+void FBX::Node::Dump(
+ std::shared_ptr<Assimp::IOStream> outfile,
+ bool binary, int indent
+) {
+ if (binary) {
+ Assimp::StreamWriterLE outstream(outfile);
+ DumpBinary(outstream);
+ } else {
+ std::ostringstream ss;
+ DumpAscii(ss, indent);
+ std::string s = ss.str();
+ outfile->Write(s.c_str(), s.size(), 1);
+ }
+}
+
+void FBX::Node::Dump(
+ Assimp::StreamWriterLE &outstream,
+ bool binary, int indent
+) {
+ if (binary) {
+ DumpBinary(outstream);
+ } else {
+ std::ostringstream ss;
+ DumpAscii(ss, indent);
+ outstream.PutString(ss.str());
+ }
+}
+
+
+// public member functions for low-level writing
+
+void FBX::Node::Begin(
+ Assimp::StreamWriterLE &s,
+ bool binary, int indent
+) {
+ if (binary) {
+ BeginBinary(s);
+ } else {
+ // assume we're at the correct place to start already
+ (void)indent;
+ std::ostringstream ss;
+ BeginAscii(ss, indent);
+ s.PutString(ss.str());
+ }
+}
+
+void FBX::Node::DumpProperties(
+ Assimp::StreamWriterLE& s,
+ bool binary, int indent
+) {
+ if (binary) {
+ DumpPropertiesBinary(s);
+ } else {
+ std::ostringstream ss;
+ DumpPropertiesAscii(ss, indent);
+ s.PutString(ss.str());
+ }
+}
+
+void FBX::Node::EndProperties(
+ Assimp::StreamWriterLE &s,
+ bool binary, int indent
+) {
+ EndProperties(s, binary, indent, properties.size());
+}
+
+void FBX::Node::EndProperties(
+ Assimp::StreamWriterLE &s,
+ bool binary, int indent,
+ size_t num_properties
+) {
+ if (binary) {
+ EndPropertiesBinary(s, num_properties);
+ } else {
+ // nothing to do
+ (void)indent;
+ }
+}
+
+void FBX::Node::BeginChildren(
+ Assimp::StreamWriterLE &s,
+ bool binary, int indent
+) {
+ if (binary) {
+ // nothing to do
+ } else {
+ std::ostringstream ss;
+ BeginChildrenAscii(ss, indent);
+ s.PutString(ss.str());
+ }
+}
+
+void FBX::Node::DumpChildren(
+ Assimp::StreamWriterLE& s,
+ bool binary, int indent
+) {
+ if (binary) {
+ DumpChildrenBinary(s);
+ } else {
+ std::ostringstream ss;
+ DumpChildrenAscii(ss, indent);
+ s.PutString(ss.str());
+ }
+}
+
+void FBX::Node::End(
+ Assimp::StreamWriterLE &s,
+ bool binary, int indent,
+ bool has_children
+) {
+ if (binary) {
+ EndBinary(s, has_children);
+ } else {
+ std::ostringstream ss;
+ EndAscii(ss, indent, has_children);
+ s.PutString(ss.str());
+ }
+}
+
+
+// public member functions for writing to binary fbx
+
+void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s)
+{
+ // write header section (with placeholders for some things)
+ BeginBinary(s);
+
+ // write properties
+ DumpPropertiesBinary(s);
+
+ // go back and fill in property related placeholders
+ EndPropertiesBinary(s, properties.size());
+
+ // write children
+ DumpChildrenBinary(s);
+
+ // finish, filling in end offset placeholder
+ EndBinary(s, force_has_children || !children.empty());
+}
+
+
+// public member functions for writing to ascii fbx
+
+void FBX::Node::DumpAscii(std::ostream &s, int indent)
+{
+ // write name
+ BeginAscii(s, indent);
+
+ // write properties
+ DumpPropertiesAscii(s, indent);
+
+ if (force_has_children || !children.empty()) {
+ // begin children (with a '{')
+ BeginChildrenAscii(s, indent + 1);
+ // write children
+ DumpChildrenAscii(s, indent + 1);
+ }
+
+ // finish (also closing the children bracket '}')
+ EndAscii(s, indent, force_has_children || !children.empty());
+}
+
+
+// private member functions for low-level writing to fbx
+
+void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s)
+{
+ // remember start pos so we can come back and write the end pos
+ this->start_pos = s.Tell();
+
+ // placeholders for end pos and property section info
+ s.PutU4(0); // end pos
+ s.PutU4(0); // number of properties
+ s.PutU4(0); // total property section length
+
+ // node name
+ s.PutU1(uint8_t(name.size())); // length of node name
+ s.PutString(name); // node name as raw bytes
+
+ // property data comes after here
+ this->property_start = s.Tell();
+}
+
+void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s)
+{
+ for (auto &p : properties) {
+ p.DumpBinary(s);
+ }
+}
+
+void FBX::Node::EndPropertiesBinary(
+ Assimp::StreamWriterLE &s,
+ size_t num_properties
+) {
+ if (num_properties == 0) { return; }
+ size_t pos = s.Tell();
+ ai_assert(pos > property_start);
+ size_t property_section_size = pos - property_start;
+ s.Seek(start_pos + 4);
+ s.PutU4(uint32_t(num_properties));
+ s.PutU4(uint32_t(property_section_size));
+ s.Seek(pos);
+}
+
+void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s)
+{
+ for (FBX::Node& child : children) {
+ child.DumpBinary(s);
+ }
+}
+
+void FBX::Node::EndBinary(
+ Assimp::StreamWriterLE &s,
+ bool has_children
+) {
+ // if there were children, add a null record
+ if (has_children) { s.PutString(FBX::NULL_RECORD); }
+
+ // now go back and write initial pos
+ this->end_pos = s.Tell();
+ s.Seek(start_pos);
+ s.PutU4(uint32_t(end_pos));
+ s.Seek(end_pos);
+}
+
+
+void FBX::Node::BeginAscii(std::ostream& s, int indent)
+{
+ s << '\n';
+ for (int i = 0; i < indent; ++i) { s << '\t'; }
+ s << name << ": ";
+}
+
+void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent)
+{
+ for (size_t i = 0; i < properties.size(); ++i) {
+ if (i > 0) { s << ", "; }
+ properties[i].DumpAscii(s, indent);
+ }
+}
+
+void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent)
+{
+ // only call this if there are actually children
+ s << " {";
+ (void)indent;
+}
+
+void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent)
+{
+ // children will need a lot of padding and corralling
+ if (children.size() || force_has_children) {
+ for (size_t i = 0; i < children.size(); ++i) {
+ // no compression in ascii files, so skip this node if it exists
+ if (children[i].name == "EncryptionType") { continue; }
+ // the child can dump itself
+ children[i].DumpAscii(s, indent);
+ }
+ }
+}
+
+void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children)
+{
+ if (!has_children) { return; } // nothing to do
+ s << '\n';
+ for (int i = 0; i < indent; ++i) { s << '\t'; }
+ s << "}";
+}
+
+// private helpers for static member functions
+
+// ascii property node from vector of doubles
+void FBX::Node::WritePropertyNodeAscii(
+ const std::string& name,
+ const std::vector<double>& v,
+ Assimp::StreamWriterLE& s,
+ int indent
+){
+ char buffer[32];
+ FBX::Node node(name);
+ node.Begin(s, false, indent);
+ std::string vsize = to_string(v.size());
+ // *<size> {
+ s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
+ // indent + 1
+ for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
+ // a: value,value,value,...
+ s.PutString("a: ");
+ int count = 0;
+ for (size_t i = 0; i < v.size(); ++i) {
+ if (i > 0) { s.PutChar(','); }
+ int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]);
+ count += len;
+ if (count > 2048) { s.PutChar('\n'); count = 0; }
+ if (len < 0 || len > 31) {
+ // this should never happen
+ throw DeadlyExportError("failed to convert double to string");
+ }
+ for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
+ }
+ // }
+ s.PutChar('\n');
+ for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
+ s.PutChar('}'); s.PutChar(' ');
+ node.End(s, false, indent, false);
+}
+
+// ascii property node from vector of int32_t
+void FBX::Node::WritePropertyNodeAscii(
+ const std::string& name,
+ const std::vector<int32_t>& v,
+ Assimp::StreamWriterLE& s,
+ int indent
+){
+ char buffer[32];
+ FBX::Node node(name);
+ node.Begin(s, false, indent);
+ std::string vsize = to_string(v.size());
+ // *<size> {
+ s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
+ // indent + 1
+ for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
+ // a: value,value,value,...
+ s.PutString("a: ");
+ int count = 0;
+ for (size_t i = 0; i < v.size(); ++i) {
+ if (i > 0) { s.PutChar(','); }
+ int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]);
+ count += len;
+ if (count > 2048) { s.PutChar('\n'); count = 0; }
+ if (len < 0 || len > 31) {
+ // this should never happen
+ throw DeadlyExportError("failed to convert double to string");
+ }
+ for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
+ }
+ // }
+ s.PutChar('\n');
+ for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
+ s.PutChar('}'); s.PutChar(' ');
+ node.End(s, false, indent, false);
+}
+
+// binary property node from vector of doubles
+// TODO: optional zip compression!
+void FBX::Node::WritePropertyNodeBinary(
+ const std::string& name,
+ const std::vector<double>& v,
+ Assimp::StreamWriterLE& s
+){
+ FBX::Node node(name);
+ node.BeginBinary(s);
+ s.PutU1('d');
+ s.PutU4(uint32_t(v.size())); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ s.PutU4(uint32_t(v.size()) * 8); // data size
+ for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); }
+ node.EndPropertiesBinary(s, 1);
+ node.EndBinary(s, false);
+}
+
+// binary property node from vector of int32_t
+// TODO: optional zip compression!
+void FBX::Node::WritePropertyNodeBinary(
+ const std::string& name,
+ const std::vector<int32_t>& v,
+ Assimp::StreamWriterLE& s
+){
+ FBX::Node node(name);
+ node.BeginBinary(s);
+ s.PutU1('i');
+ s.PutU4(uint32_t(v.size())); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ s.PutU4(uint32_t(v.size()) * 4); // data size
+ for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); }
+ node.EndPropertiesBinary(s, 1);
+ node.EndBinary(s, false);
+}
+
+// public static member functions
+
+// convenience function to create and write a property node,
+// holding a single property which is an array of values.
+// does not copy the data, so is efficient for large arrays.
+void FBX::Node::WritePropertyNode(
+ const std::string& name,
+ const std::vector<double>& v,
+ Assimp::StreamWriterLE& s,
+ bool binary, int indent
+){
+ if (binary) {
+ FBX::Node::WritePropertyNodeBinary(name, v, s);
+ } else {
+ FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
+ }
+}
+
+// convenience function to create and write a property node,
+// holding a single property which is an array of values.
+// does not copy the data, so is efficient for large arrays.
+void FBX::Node::WritePropertyNode(
+ const std::string& name,
+ const std::vector<int32_t>& v,
+ Assimp::StreamWriterLE& s,
+ bool binary, int indent
+){
+ if (binary) {
+ FBX::Node::WritePropertyNodeBinary(name, v, s);
+ } else {
+ FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
+ }
+}
+
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
diff --git a/thirdparty/assimp/code/FBXExportNode.h b/thirdparty/assimp/code/FBXExportNode.h
new file mode 100644
index 0000000000..e1ebc36969
--- /dev/null
+++ b/thirdparty/assimp/code/FBXExportNode.h
@@ -0,0 +1,271 @@
+/*
+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 FBXExportNode.h
+* Declares the FBX::Node helper class for fbx export.
+*/
+#ifndef AI_FBXEXPORTNODE_H_INC
+#define AI_FBXEXPORTNODE_H_INC
+
+#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#include "FBXExportProperty.h"
+
+#include <assimp/StreamWriter.h> // StreamWriterLE
+
+#include <string>
+#include <vector>
+
+namespace FBX {
+ class Node;
+}
+
+class FBX::Node
+{
+public: // public data members
+ // TODO: accessors
+ std::string name; // node name
+ std::vector<FBX::Property> properties; // node properties
+ std::vector<FBX::Node> children; // child nodes
+
+ // some nodes always pretend they have children...
+ bool force_has_children = false;
+
+public: // constructors
+ /// The default class constructor.
+ Node() = default;
+
+ /// The class constructor with the name.
+ Node(const std::string& n)
+ : name(n)
+ , properties()
+ , children()
+ , force_has_children( false ) {
+ // empty
+ }
+
+ // convenience template to construct with properties directly
+ template <typename... More>
+ Node(const std::string& n, const More... more)
+ : name(n)
+ , properties()
+ , children()
+ , force_has_children(false) {
+ AddProperties(more...);
+ }
+
+public: // functions to add properties or children
+ // add a single property to the node
+ template <typename T>
+ void AddProperty(T value) {
+ properties.emplace_back(value);
+ }
+
+ // convenience function to add multiple properties at once
+ template <typename T, typename... More>
+ void AddProperties(T value, More... more) {
+ properties.emplace_back(value);
+ AddProperties(more...);
+ }
+ void AddProperties() {}
+
+ // add a child node directly
+ void AddChild(const Node& node) { children.push_back(node); }
+
+ // convenience function to add a child node with a single property
+ template <typename... More>
+ void AddChild(
+ const std::string& name,
+ More... more
+ ) {
+ FBX::Node c(name);
+ c.AddProperties(more...);
+ children.push_back(c);
+ }
+
+public: // support specifically for dealing with Properties70 nodes
+
+ // it really is simpler to make these all separate functions.
+ // the versions with 'A' suffixes are for animatable properties.
+ // those often follow a completely different format internally in FBX.
+ void AddP70int(const std::string& name, int32_t value);
+ void AddP70bool(const std::string& name, bool value);
+ void AddP70double(const std::string& name, double value);
+ void AddP70numberA(const std::string& name, double value);
+ void AddP70color(const std::string& name, double r, double g, double b);
+ void AddP70colorA(const std::string& name, double r, double g, double b);
+ void AddP70vector(const std::string& name, double x, double y, double z);
+ void AddP70vectorA(const std::string& name, double x, double y, double z);
+ void AddP70string(const std::string& name, const std::string& value);
+ void AddP70enum(const std::string& name, int32_t value);
+ void AddP70time(const std::string& name, int64_t value);
+
+ // template for custom P70 nodes.
+ // anything that doesn't fit in the above can be created manually.
+ template <typename... More>
+ void AddP70(
+ const std::string& name,
+ const std::string& type,
+ const std::string& type2,
+ const std::string& flags,
+ More... more
+ ) {
+ Node n("P");
+ n.AddProperties(name, type, type2, flags, more...);
+ AddChild(n);
+ }
+
+public: // member functions for writing data to a file or stream
+
+ // write the full node to the given file or stream
+ void Dump(
+ std::shared_ptr<Assimp::IOStream> outfile,
+ bool binary, int indent
+ );
+ void Dump(Assimp::StreamWriterLE &s, bool binary, int indent);
+
+ // these other functions are for writing data piece by piece.
+ // they must be used carefully.
+ // for usage examples see FBXExporter.cpp.
+ void Begin(Assimp::StreamWriterLE &s, bool binary, int indent);
+ void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent);
+ void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent);
+ void EndProperties(
+ Assimp::StreamWriterLE &s, bool binary, int indent,
+ size_t num_properties
+ );
+ void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent);
+ void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent);
+ void End(
+ Assimp::StreamWriterLE &s, bool binary, int indent,
+ bool has_children
+ );
+
+private: // internal functions used for writing
+
+ void DumpBinary(Assimp::StreamWriterLE &s);
+ void DumpAscii(Assimp::StreamWriterLE &s, int indent);
+ void DumpAscii(std::ostream &s, int indent);
+
+ void BeginBinary(Assimp::StreamWriterLE &s);
+ void DumpPropertiesBinary(Assimp::StreamWriterLE& s);
+ void EndPropertiesBinary(Assimp::StreamWriterLE &s);
+ void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties);
+ void DumpChildrenBinary(Assimp::StreamWriterLE& s);
+ void EndBinary(Assimp::StreamWriterLE &s, bool has_children);
+
+ void BeginAscii(std::ostream &s, int indent);
+ void DumpPropertiesAscii(std::ostream &s, int indent);
+ void BeginChildrenAscii(std::ostream &s, int indent);
+ void DumpChildrenAscii(std::ostream &s, int indent);
+ void EndAscii(std::ostream &s, int indent, bool has_children);
+
+private: // data used for binary dumps
+ size_t start_pos; // starting position in stream
+ size_t end_pos; // ending position in stream
+ size_t property_start; // starting position of property section
+
+public: // static member functions
+
+ // convenience function to create a node with a single property,
+ // and write it to the stream.
+ template <typename T>
+ static void WritePropertyNode(
+ const std::string& name,
+ const T value,
+ Assimp::StreamWriterLE& s,
+ bool binary, int indent
+ ) {
+ FBX::Property p(value);
+ FBX::Node node(name, p);
+ node.Dump(s, binary, indent);
+ }
+
+ // convenience function to create and write a property node,
+ // holding a single property which is an array of values.
+ // does not copy the data, so is efficient for large arrays.
+ static void WritePropertyNode(
+ const std::string& name,
+ const std::vector<double>& v,
+ Assimp::StreamWriterLE& s,
+ bool binary, int indent
+ );
+
+ // convenience function to create and write a property node,
+ // holding a single property which is an array of values.
+ // does not copy the data, so is efficient for large arrays.
+ static void WritePropertyNode(
+ const std::string& name,
+ const std::vector<int32_t>& v,
+ Assimp::StreamWriterLE& s,
+ bool binary, int indent
+ );
+
+private: // static helper functions
+ static void WritePropertyNodeAscii(
+ const std::string& name,
+ const std::vector<double>& v,
+ Assimp::StreamWriterLE& s,
+ int indent
+ );
+ static void WritePropertyNodeAscii(
+ const std::string& name,
+ const std::vector<int32_t>& v,
+ Assimp::StreamWriterLE& s,
+ int indent
+ );
+ static void WritePropertyNodeBinary(
+ const std::string& name,
+ const std::vector<double>& v,
+ Assimp::StreamWriterLE& s
+ );
+ static void WritePropertyNodeBinary(
+ const std::string& name,
+ const std::vector<int32_t>& v,
+ Assimp::StreamWriterLE& s
+ );
+
+};
+
+
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#endif // AI_FBXEXPORTNODE_H_INC
diff --git a/thirdparty/assimp/code/FBXExportProperty.cpp b/thirdparty/assimp/code/FBXExportProperty.cpp
new file mode 100644
index 0000000000..9981d6b1c6
--- /dev/null
+++ b/thirdparty/assimp/code/FBXExportProperty.cpp
@@ -0,0 +1,364 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#include "FBXExportProperty.h"
+
+#include <assimp/StreamWriter.h> // StreamWriterLE
+#include <assimp/Exceptional.h> // DeadlyExportError
+
+#include <string>
+#include <vector>
+#include <ostream>
+#include <locale>
+#include <sstream> // ostringstream
+
+
+// constructors for single element properties
+
+FBX::Property::Property(bool v)
+ : type('C'), data(1)
+{
+ data = {uint8_t(v)};
+}
+
+FBX::Property::Property(int16_t v) : type('Y'), data(2)
+{
+ uint8_t* d = data.data();
+ (reinterpret_cast<int16_t*>(d))[0] = v;
+}
+
+FBX::Property::Property(int32_t v) : type('I'), data(4)
+{
+ uint8_t* d = data.data();
+ (reinterpret_cast<int32_t*>(d))[0] = v;
+}
+
+FBX::Property::Property(float v) : type('F'), data(4)
+{
+ uint8_t* d = data.data();
+ (reinterpret_cast<float*>(d))[0] = v;
+}
+
+FBX::Property::Property(double v) : type('D'), data(8)
+{
+ uint8_t* d = data.data();
+ (reinterpret_cast<double*>(d))[0] = v;
+}
+
+FBX::Property::Property(int64_t v) : type('L'), data(8)
+{
+ uint8_t* d = data.data();
+ (reinterpret_cast<int64_t*>(d))[0] = v;
+}
+
+
+// constructors for array-type properties
+
+FBX::Property::Property(const char* c, bool raw)
+ : Property(std::string(c), raw)
+{}
+
+// strings can either be saved as "raw" (R) data, or "string" (S) data
+FBX::Property::Property(const std::string& s, bool raw)
+ : type(raw ? 'R' : 'S'), data(s.size())
+{
+ for (size_t i = 0; i < s.size(); ++i) {
+ data[i] = uint8_t(s[i]);
+ }
+}
+
+FBX::Property::Property(const std::vector<uint8_t>& r)
+ : type('R'), data(r)
+{}
+
+FBX::Property::Property(const std::vector<int32_t>& va)
+ : type('i'), data(4*va.size())
+{
+ int32_t* d = reinterpret_cast<int32_t*>(data.data());
+ for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+}
+
+FBX::Property::Property(const std::vector<int64_t>& va)
+ : type('l'), data(8*va.size())
+{
+ int64_t* d = reinterpret_cast<int64_t*>(data.data());
+ for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+}
+
+FBX::Property::Property(const std::vector<float>& va)
+ : type('f'), data(4*va.size())
+{
+ float* d = reinterpret_cast<float*>(data.data());
+ for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+}
+
+FBX::Property::Property(const std::vector<double>& va)
+ : type('d'), data(8*va.size())
+{
+ double* d = reinterpret_cast<double*>(data.data());
+ for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+}
+
+FBX::Property::Property(const aiMatrix4x4& vm)
+ : type('d'), data(8*16)
+{
+ double* d = reinterpret_cast<double*>(data.data());
+ for (unsigned int c = 0; c < 4; ++c) {
+ for (unsigned int r = 0; r < 4; ++r) {
+ d[4*c+r] = vm[r][c];
+ }
+ }
+}
+
+// public member functions
+
+size_t FBX::Property::size()
+{
+ switch (type) {
+ case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L':
+ return data.size() + 1;
+ case 'S': case 'R':
+ return data.size() + 5;
+ case 'i': case 'd':
+ return data.size() + 13;
+ default:
+ throw DeadlyExportError("Requested size on property of unknown type");
+ }
+}
+
+void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s)
+{
+ s.PutU1(type);
+ uint8_t* d = data.data();
+ size_t N;
+ switch (type) {
+ case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
+ case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
+ case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
+ case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
+ case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
+ case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
+ case 'S':
+ case 'R':
+ s.PutU4(uint32_t(data.size()));
+ for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
+ return;
+ case 'i':
+ N = data.size() / 4;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
+ }
+ return;
+ case 'l':
+ N = data.size() / 8;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
+ }
+ return;
+ case 'f':
+ N = data.size() / 4;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutF4((reinterpret_cast<float*>(d))[i]);
+ }
+ return;
+ case 'd':
+ N = data.size() / 8;
+ s.PutU4(uint32_t(N)); // number of elements
+ s.PutU4(0); // no encoding (1 would be zip-compressed)
+ // TODO: compress if large?
+ s.PutU4(uint32_t(data.size())); // data size
+ for (size_t i = 0; i < N; ++i) {
+ s.PutF8((reinterpret_cast<double*>(d))[i]);
+ }
+ return;
+ default:
+ std::ostringstream err;
+ err << "Tried to dump property with invalid type '";
+ err << type << "'!";
+ throw DeadlyExportError(err.str());
+ }
+}
+
+void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
+{
+ std::ostringstream ss;
+ ss.imbue(std::locale::classic());
+ ss.precision(15); // this seems to match official FBX SDK exports
+ DumpAscii(ss, indent);
+ outstream.PutString(ss.str());
+}
+
+void FBX::Property::DumpAscii(std::ostream& s, int indent)
+{
+ // no writing type... or anything. just shove it into the stream.
+ uint8_t* d = data.data();
+ size_t N;
+ size_t swap = data.size();
+ size_t count = 0;
+ switch (type) {
+ case 'C':
+ if (*(reinterpret_cast<uint8_t*>(d))) { s << 'T'; }
+ else { s << 'F'; }
+ return;
+ case 'Y': s << *(reinterpret_cast<int16_t*>(d)); return;
+ case 'I': s << *(reinterpret_cast<int32_t*>(d)); return;
+ case 'F': s << *(reinterpret_cast<float*>(d)); return;
+ case 'D': s << *(reinterpret_cast<double*>(d)); return;
+ case 'L': s << *(reinterpret_cast<int64_t*>(d)); return;
+ case 'S':
+ // first search to see if it has "\x00\x01" in it -
+ // which separates fields which are reversed in the ascii version.
+ // yeah.
+ // FBX, yeah.
+ for (size_t i = 0; i < data.size(); ++i) {
+ if (data[i] == '\0') {
+ swap = i;
+ break;
+ }
+ }
+ case 'R':
+ s << '"';
+ // we might as well check this now,
+ // probably it will never happen
+ for (size_t i = 0; i < data.size(); ++i) {
+ char c = data[i];
+ if (c == '"') {
+ throw runtime_error("can't handle quotes in property string");
+ }
+ }
+ // first write the SWAPPED member (if any)
+ for (size_t i = swap + 2; i < data.size(); ++i) {
+ char c = data[i];
+ s << c;
+ }
+ // then a separator
+ if (swap != data.size()) {
+ s << "::";
+ }
+ // then the initial member
+ for (size_t i = 0; i < swap; ++i) {
+ char c = data[i];
+ s << c;
+ }
+ s << '"';
+ return;
+ case 'i':
+ N = data.size() / 4; // number of elements
+ s << '*' << N << " {\n";
+ for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
+ s << "a: ";
+ for (size_t i = 0; i < N; ++i) {
+ if (i > 0) { s << ','; }
+ if (count++ > 120) { s << '\n'; count = 0; }
+ s << (reinterpret_cast<int32_t*>(d))[i];
+ }
+ s << '\n';
+ for (int i = 0; i < indent; ++i) { s << '\t'; }
+ s << "} ";
+ return;
+ case 'l':
+ N = data.size() / 8;
+ s << '*' << N << " {\n";
+ for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
+ s << "a: ";
+ for (size_t i = 0; i < N; ++i) {
+ if (i > 0) { s << ','; }
+ if (count++ > 120) { s << '\n'; count = 0; }
+ s << (reinterpret_cast<int64_t*>(d))[i];
+ }
+ s << '\n';
+ for (int i = 0; i < indent; ++i) { s << '\t'; }
+ s << "} ";
+ return;
+ case 'f':
+ N = data.size() / 4;
+ s << '*' << N << " {\n";
+ for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
+ s << "a: ";
+ for (size_t i = 0; i < N; ++i) {
+ if (i > 0) { s << ','; }
+ if (count++ > 120) { s << '\n'; count = 0; }
+ s << (reinterpret_cast<float*>(d))[i];
+ }
+ s << '\n';
+ for (int i = 0; i < indent; ++i) { s << '\t'; }
+ s << "} ";
+ return;
+ case 'd':
+ N = data.size() / 8;
+ s << '*' << N << " {\n";
+ for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
+ s << "a: ";
+ // set precision to something that can handle doubles
+ s.precision(15);
+ for (size_t i = 0; i < N; ++i) {
+ if (i > 0) { s << ','; }
+ if (count++ > 120) { s << '\n'; count = 0; }
+ s << (reinterpret_cast<double*>(d))[i];
+ }
+ s << '\n';
+ for (int i = 0; i < indent; ++i) { s << '\t'; }
+ s << "} ";
+ return;
+ default:
+ std::ostringstream err;
+ err << "Tried to dump property with invalid type '";
+ err << type << "'!";
+ throw runtime_error(err.str());
+ }
+}
+
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
diff --git a/thirdparty/assimp/code/FBXExportProperty.h b/thirdparty/assimp/code/FBXExportProperty.h
new file mode 100644
index 0000000000..9c9d37c362
--- /dev/null
+++ b/thirdparty/assimp/code/FBXExportProperty.h
@@ -0,0 +1,129 @@
+/*
+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 FBXExportProperty.h
+* Declares the FBX::Property helper class for fbx export.
+*/
+#ifndef AI_FBXEXPORTPROPERTY_H_INC
+#define AI_FBXEXPORTPROPERTY_H_INC
+
+#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
+
+
+#include <assimp/types.h> // aiMatrix4x4
+#include <assimp/StreamWriter.h> // StreamWriterLE
+
+#include <string>
+#include <vector>
+#include <ostream>
+#include <type_traits> // is_void
+
+namespace FBX {
+ class Property;
+}
+
+/** FBX::Property
+ *
+ * Holds a value of any of FBX's recognized types,
+ * each represented by a particular one-character code.
+ * C : 1-byte uint8, usually 0x00 or 0x01 to represent boolean false and true
+ * Y : 2-byte int16
+ * I : 4-byte int32
+ * F : 4-byte float
+ * D : 8-byte double
+ * L : 8-byte int64
+ * i : array of int32
+ * f : array of float
+ * d : array of double
+ * l : array of int64
+ * b : array of 1-byte booleans (0x00 or 0x01)
+ * S : string (array of 1-byte char)
+ * R : raw data (array of bytes)
+ */
+class FBX::Property
+{
+public:
+ // constructors for basic types.
+ // all explicit to avoid accidental typecasting
+ explicit Property(bool v);
+ // TODO: determine if there is actually a byte type,
+ // or if this always means <bool>. 'C' seems to imply <char>,
+ // so possibly the above was intended to represent both.
+ explicit Property(int16_t v);
+ explicit Property(int32_t v);
+ explicit Property(float v);
+ explicit Property(double v);
+ explicit Property(int64_t v);
+ // strings can either be stored as 'R' (raw) or 'S' (string) type
+ explicit Property(const char* c, bool raw=false);
+ explicit Property(const std::string& s, bool raw=false);
+ explicit Property(const std::vector<uint8_t>& r);
+ explicit Property(const std::vector<int32_t>& va);
+ explicit Property(const std::vector<int64_t>& va);
+ explicit Property(const std::vector<double>& va);
+ explicit Property(const std::vector<float>& va);
+ explicit Property(const aiMatrix4x4& vm);
+
+ // this will catch any type not defined above,
+ // so that we don't accidentally convert something we don't want.
+ // for example (const char*) --> (bool)... seriously wtf C++
+ template <class T>
+ explicit Property(T v) : type('X') {
+ static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION");
+ } // note: no line wrap so it appears verbatim on the compiler error
+
+ // the size of this property node in a binary file, in bytes
+ size_t size();
+
+ // write this property node as binary data to the given stream
+ void DumpBinary(Assimp::StreamWriterLE &s);
+ void DumpAscii(Assimp::StreamWriterLE &s, int indent=0);
+ void DumpAscii(std::ostream &s, int indent=0);
+ // note: make sure the ostream is in classic "C" locale
+
+private:
+ char type;
+ std::vector<uint8_t> data;
+};
+
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#endif // AI_FBXEXPORTPROPERTY_H_INC
diff --git a/thirdparty/assimp/code/FBXExporter.cpp b/thirdparty/assimp/code/FBXExporter.cpp
new file mode 100644
index 0000000000..acb1227144
--- /dev/null
+++ b/thirdparty/assimp/code/FBXExporter.cpp
@@ -0,0 +1,2480 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#include "FBXExporter.h"
+#include "FBXExportNode.h"
+#include "FBXExportProperty.h"
+#include "FBXCommon.h"
+
+#include <assimp/version.h> // aiGetVersion
+#include <assimp/IOSystem.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/StreamWriter.h> // StreamWriterLE
+#include <assimp/Exceptional.h> // DeadlyExportError
+#include <assimp/material.h> // aiTextureType
+#include <assimp/scene.h>
+#include <assimp/mesh.h>
+
+// Header files, standard library.
+#include <memory> // shared_ptr
+#include <string>
+#include <sstream> // stringstream
+#include <ctime> // localtime, tm_*
+#include <map>
+#include <set>
+#include <vector>
+#include <array>
+#include <unordered_set>
+
+// RESOURCES:
+// https://code.blender.org/2013/08/fbx-binary-file-format-specification/
+// https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure
+
+const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian
+
+// some constants that we'll use for writing metadata
+namespace FBX {
+ const std::string EXPORT_VERSION_STR = "7.4.0";
+ const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015
+ // FBX files have some hashed values that depend on the creation time field,
+ // but for now we don't actually know how to generate these.
+ // what we can do is set them to a known-working version.
+ // this is the data that Blender uses in their FBX export process.
+ const std::string GENERIC_CTIME = "1970-01-01 10:00:00:000";
+ const std::string GENERIC_FILEID =
+ "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1";
+ const std::string GENERIC_FOOTID =
+ "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e";
+ const std::string FOOT_MAGIC =
+ "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b";
+ const std::string COMMENT_UNDERLINE =
+ ";------------------------------------------------------------------";
+}
+
+using namespace Assimp;
+using namespace FBX;
+
+namespace Assimp {
+
+ // ---------------------------------------------------------------------
+ // Worker function for exporting a scene to binary FBX.
+ // Prototyped and registered in Exporter.cpp
+ void ExportSceneFBX (
+ const char* pFile,
+ IOSystem* pIOSystem,
+ const aiScene* pScene,
+ const ExportProperties* pProperties
+ ){
+ // initialize the exporter
+ FBXExporter exporter(pScene, pProperties);
+
+ // perform binary export
+ exporter.ExportBinary(pFile, pIOSystem);
+ }
+
+ // ---------------------------------------------------------------------
+ // Worker function for exporting a scene to ASCII FBX.
+ // Prototyped and registered in Exporter.cpp
+ void ExportSceneFBXA (
+ const char* pFile,
+ IOSystem* pIOSystem,
+ const aiScene* pScene,
+ const ExportProperties* pProperties
+ ){
+ // initialize the exporter
+ FBXExporter exporter(pScene, pProperties);
+
+ // perform ascii export
+ exporter.ExportAscii(pFile, pIOSystem);
+ }
+
+} // end of namespace Assimp
+
+FBXExporter::FBXExporter ( const aiScene* pScene, const ExportProperties* pProperties )
+: binary(false)
+, mScene(pScene)
+, mProperties(pProperties)
+, outfile()
+, connections()
+, mesh_uids()
+, material_uids()
+, node_uids() {
+ // will probably need to determine UIDs, connections, etc here.
+ // basically anything that needs to be known
+ // before we start writing sections to the stream.
+}
+
+void FBXExporter::ExportBinary (
+ const char* pFile,
+ IOSystem* pIOSystem
+){
+ // remember that we're exporting in binary mode
+ binary = true;
+
+ // we're not currently using these preferences,
+ // but clang will cry about it if we never touch it.
+ // TODO: some of these might be relevant to export
+ (void)mProperties;
+
+ // open the indicated file for writing (in binary mode)
+ outfile.reset(pIOSystem->Open(pFile,"wb"));
+ if (!outfile) {
+ throw DeadlyExportError(
+ "could not open output .fbx file: " + std::string(pFile)
+ );
+ }
+
+ // first a binary-specific file header
+ WriteBinaryHeader();
+
+ // the rest of the file is in node entries.
+ // we have to serialize each entry before we write to the output,
+ // as the first thing we write is the byte offset of the _next_ entry.
+ // Either that or we can skip back to write the offset when we finish.
+ WriteAllNodes();
+
+ // finally we have a binary footer to the file
+ WriteBinaryFooter();
+
+ // explicitly release file pointer,
+ // so we don't have to rely on class destruction.
+ outfile.reset();
+}
+
+void FBXExporter::ExportAscii (
+ const char* pFile,
+ IOSystem* pIOSystem
+){
+ // remember that we're exporting in ascii mode
+ binary = false;
+
+ // open the indicated file for writing in text mode
+ outfile.reset(pIOSystem->Open(pFile,"wt"));
+ if (!outfile) {
+ throw DeadlyExportError(
+ "could not open output .fbx file: " + std::string(pFile)
+ );
+ }
+
+ // write the ascii header
+ WriteAsciiHeader();
+
+ // write all the sections
+ WriteAllNodes();
+
+ // make sure the file ends with a newline.
+ // note: if the file is opened in text mode,
+ // this should do the right cross-platform thing.
+ outfile->Write("\n", 1, 1);
+
+ // explicitly release file pointer,
+ // so we don't have to rely on class destruction.
+ outfile.reset();
+}
+
+void FBXExporter::WriteAsciiHeader()
+{
+ // basically just a comment at the top of the file
+ std::stringstream head;
+ head << "; FBX " << EXPORT_VERSION_STR << " project file\n";
+ head << "; Created by the Open Asset Import Library (Assimp)\n";
+ head << "; http://assimp.org\n";
+ head << "; -------------------------------------------------\n";
+ const std::string ascii_header = head.str();
+ outfile->Write(ascii_header.c_str(), ascii_header.size(), 1);
+}
+
+void FBXExporter::WriteAsciiSectionHeader(const std::string& title)
+{
+ StreamWriterLE outstream(outfile);
+ std::stringstream s;
+ s << "\n\n; " << title << '\n';
+ s << FBX::COMMENT_UNDERLINE << "\n";
+ outstream.PutString(s.str());
+}
+
+void FBXExporter::WriteBinaryHeader()
+{
+ // first a specific sequence of 23 bytes, always the same
+ const char binary_header[24] = "Kaydara FBX Binary\x20\x20\x00\x1a\x00";
+ outfile->Write(binary_header, 1, 23);
+
+ // then FBX version number, "multiplied" by 1000, as little-endian uint32.
+ // so 7.3 becomes 7300 == 0x841C0000, 7.4 becomes 7400 == 0xE81C0000, etc
+ {
+ StreamWriterLE outstream(outfile);
+ outstream.PutU4(EXPORT_VERSION_INT);
+ } // StreamWriter destructor writes the data to the file
+
+ // after this the node data starts immediately
+ // (probably with the FBXHEaderExtension node)
+}
+
+void FBXExporter::WriteBinaryFooter()
+{
+ outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1);
+
+ outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1);
+
+ // here some padding is added for alignment to 16 bytes.
+ // if already aligned, the full 16 bytes is added.
+ size_t pos = outfile->Tell();
+ size_t pad = 16 - (pos % 16);
+ for (size_t i = 0; i < pad; ++i) {
+ outfile->Write("\x00", 1, 1);
+ }
+
+ // not sure what this is, but it seems to always be 0 in modern files
+ for (size_t i = 0; i < 4; ++i) {
+ outfile->Write("\x00", 1, 1);
+ }
+
+ // now the file version again
+ {
+ StreamWriterLE outstream(outfile);
+ outstream.PutU4(EXPORT_VERSION_INT);
+ } // StreamWriter destructor writes the data to the file
+
+ // and finally some binary footer added to all files
+ for (size_t i = 0; i < 120; ++i) {
+ outfile->Write("\x00", 1, 1);
+ }
+ outfile->Write(FOOT_MAGIC.c_str(), FOOT_MAGIC.size(), 1);
+}
+
+void FBXExporter::WriteAllNodes ()
+{
+ // header
+ // (and fileid, creation time, creator, if binary)
+ WriteHeaderExtension();
+
+ // global settings
+ WriteGlobalSettings();
+
+ // documents
+ WriteDocuments();
+
+ // references
+ WriteReferences();
+
+ // definitions
+ WriteDefinitions();
+
+ // objects
+ WriteObjects();
+
+ // connections
+ WriteConnections();
+
+ // WriteTakes? (deprecated since at least 2015 (fbx 7.4))
+}
+
+//FBXHeaderExtension top-level node
+void FBXExporter::WriteHeaderExtension ()
+{
+ if (!binary) {
+ // no title, follows directly from the top comment
+ }
+ FBX::Node n("FBXHeaderExtension");
+ StreamWriterLE outstream(outfile);
+ int indent = 0;
+
+ // begin node
+ n.Begin(outstream, binary, indent);
+
+ // write properties
+ // (none)
+
+ // finish properties
+ n.EndProperties(outstream, binary, indent, 0);
+
+ // begin children
+ n.BeginChildren(outstream, binary, indent);
+
+ indent = 1;
+
+ // write child nodes
+ FBX::Node::WritePropertyNode(
+ "FBXHeaderVersion", int32_t(1003), outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent
+ );
+ if (binary) {
+ FBX::Node::WritePropertyNode(
+ "EncryptionType", int32_t(0), outstream, binary, indent
+ );
+ }
+
+ FBX::Node CreationTimeStamp("CreationTimeStamp");
+ time_t rawtime;
+ time(&rawtime);
+ struct tm * now = localtime(&rawtime);
+ CreationTimeStamp.AddChild("Version", int32_t(1000));
+ CreationTimeStamp.AddChild("Year", int32_t(now->tm_year + 1900));
+ CreationTimeStamp.AddChild("Month", int32_t(now->tm_mon + 1));
+ CreationTimeStamp.AddChild("Day", int32_t(now->tm_mday));
+ CreationTimeStamp.AddChild("Hour", int32_t(now->tm_hour));
+ CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min));
+ CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec));
+ CreationTimeStamp.AddChild("Millisecond", int32_t(0));
+ CreationTimeStamp.Dump(outstream, binary, indent);
+
+ std::stringstream creator;
+ creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor()
+ << "." << aiGetVersionMinor() << "." << aiGetVersionRevision();
+ FBX::Node::WritePropertyNode(
+ "Creator", creator.str(), outstream, binary, indent
+ );
+
+ //FBX::Node sceneinfo("SceneInfo");
+ //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo");
+ // not sure if any of this is actually needed,
+ // so just write an empty node for now.
+ //sceneinfo.Dump(outstream, binary, indent);
+
+ indent = 0;
+
+ // finish node
+ n.End(outstream, binary, indent, true);
+
+ // that's it for FBXHeaderExtension...
+ if (!binary) { return; }
+
+ // but binary files also need top-level FileID, CreationTime, Creator:
+ std::vector<uint8_t> raw(GENERIC_FILEID.size());
+ for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) {
+ raw[i] = uint8_t(GENERIC_FILEID[i]);
+ }
+ FBX::Node::WritePropertyNode(
+ "FileId", raw, outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "CreationTime", GENERIC_CTIME, outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "Creator", creator.str(), outstream, binary, indent
+ );
+}
+
+void FBXExporter::WriteGlobalSettings ()
+{
+ if (!binary) {
+ // no title, follows directly from the header extension
+ }
+ FBX::Node gs("GlobalSettings");
+ gs.AddChild("Version", int32_t(1000));
+
+ FBX::Node p("Properties70");
+ p.AddP70int("UpAxis", 1);
+ p.AddP70int("UpAxisSign", 1);
+ p.AddP70int("FrontAxis", 2);
+ p.AddP70int("FrontAxisSign", 1);
+ p.AddP70int("CoordAxis", 0);
+ p.AddP70int("CoordAxisSign", 1);
+ p.AddP70int("OriginalUpAxis", 1);
+ p.AddP70int("OriginalUpAxisSign", 1);
+ p.AddP70double("UnitScaleFactor", 1.0);
+ p.AddP70double("OriginalUnitScaleFactor", 1.0);
+ p.AddP70color("AmbientColor", 0.0, 0.0, 0.0);
+ p.AddP70string("DefaultCamera", "Producer Perspective");
+ p.AddP70enum("TimeMode", 11);
+ p.AddP70enum("TimeProtocol", 2);
+ p.AddP70enum("SnapOnFrameMode", 0);
+ p.AddP70time("TimeSpanStart", 0); // TODO: animation support
+ p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support
+ p.AddP70double("CustomFrameRate", -1.0);
+ p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is
+ p.AddP70int("CurrentTimeMarker", -1);
+ gs.AddChild(p);
+
+ gs.Dump(outfile, binary, 0);
+}
+
+void FBXExporter::WriteDocuments ()
+{
+ if (!binary) {
+ WriteAsciiSectionHeader("Documents Description");
+ }
+
+ // not sure what the use of multiple documents would be,
+ // or whether any end-application supports it
+ FBX::Node docs("Documents");
+ docs.AddChild("Count", int32_t(1));
+ FBX::Node doc("Document");
+
+ // generate uid
+ int64_t uid = generate_uid();
+ doc.AddProperties(uid, "", "Scene");
+ FBX::Node p("Properties70");
+ p.AddP70("SourceObject", "object", "", ""); // what is this even for?
+ p.AddP70string("ActiveAnimStackName", ""); // should do this properly?
+ doc.AddChild(p);
+
+ // UID for root node in scene hierarchy.
+ // always set to 0 in the case of a single document.
+ // not sure what happens if more than one document exists,
+ // but that won't matter to us as we're exporting a single scene.
+ doc.AddChild("RootNode", int64_t(0));
+
+ docs.AddChild(doc);
+ docs.Dump(outfile, binary, 0);
+}
+
+void FBXExporter::WriteReferences ()
+{
+ if (!binary) {
+ WriteAsciiSectionHeader("Document References");
+ }
+ // always empty for now.
+ // not really sure what this is for.
+ FBX::Node n("References");
+ n.force_has_children = true;
+ n.Dump(outfile, binary, 0);
+}
+
+
+// ---------------------------------------------------------------
+// some internal helper functions used for writing the definitions
+// (before any actual data is written)
+// ---------------------------------------------------------------
+
+size_t count_nodes(const aiNode* n) {
+ size_t count = 1;
+ for (size_t i = 0; i < n->mNumChildren; ++i) {
+ count += count_nodes(n->mChildren[i]);
+ }
+ return count;
+}
+
+bool has_phong_mat(const aiScene* scene)
+{
+ // just search for any material with a shininess exponent
+ for (size_t i = 0; i < scene->mNumMaterials; ++i) {
+ aiMaterial* mat = scene->mMaterials[i];
+ float shininess = 0;
+ mat->Get(AI_MATKEY_SHININESS, shininess);
+ if (shininess > 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t count_images(const aiScene* scene) {
+ std::unordered_set<std::string> images;
+ aiString texpath;
+ for (size_t i = 0; i < scene->mNumMaterials; ++i) {
+ aiMaterial* mat = scene->mMaterials[i];
+ for (
+ size_t tt = aiTextureType_DIFFUSE;
+ tt < aiTextureType_UNKNOWN;
+ ++tt
+ ){
+ const aiTextureType textype = static_cast<aiTextureType>(tt);
+ const size_t texcount = mat->GetTextureCount(textype);
+ for (unsigned int j = 0; j < texcount; ++j) {
+ mat->GetTexture(textype, j, &texpath);
+ images.insert(std::string(texpath.C_Str()));
+ }
+ }
+ }
+ return images.size();
+}
+
+size_t count_textures(const aiScene* scene) {
+ size_t count = 0;
+ for (size_t i = 0; i < scene->mNumMaterials; ++i) {
+ aiMaterial* mat = scene->mMaterials[i];
+ for (
+ size_t tt = aiTextureType_DIFFUSE;
+ tt < aiTextureType_UNKNOWN;
+ ++tt
+ ){
+ // TODO: handle layered textures
+ if (mat->GetTextureCount(static_cast<aiTextureType>(tt)) > 0) {
+ count += 1;
+ }
+ }
+ }
+ return count;
+}
+
+size_t count_deformers(const aiScene* scene) {
+ size_t count = 0;
+ for (size_t i = 0; i < scene->mNumMeshes; ++i) {
+ const size_t n = scene->mMeshes[i]->mNumBones;
+ if (n) {
+ // 1 main deformer, 1 subdeformer per bone
+ count += n + 1;
+ }
+ }
+ return count;
+}
+
+void FBXExporter::WriteDefinitions ()
+{
+ // basically this is just bookkeeping:
+ // determining how many of each type of object there are
+ // and specifying the base properties to use when otherwise unspecified.
+
+ // ascii section header
+ if (!binary) {
+ WriteAsciiSectionHeader("Object definitions");
+ }
+
+ // we need to count the objects
+ int32_t count;
+ int32_t total_count = 0;
+
+ // and store them
+ std::vector<FBX::Node> object_nodes;
+ FBX::Node n, pt, p;
+
+ // GlobalSettings
+ // this seems to always be here in Maya exports
+ n = FBX::Node("ObjectType", "GlobalSettings");
+ count = 1;
+ n.AddChild("Count", count);
+ object_nodes.push_back(n);
+ total_count += count;
+
+ // AnimationStack / FbxAnimStack
+ // this seems to always be here in Maya exports,
+ // but no harm seems to come of leaving it out.
+ count = mScene->mNumAnimations;
+ if (count) {
+ n = FBX::Node("ObjectType", "AnimationStack");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FbxAnimStack");
+ p = FBX::Node("Properties70");
+ p.AddP70string("Description", "");
+ p.AddP70time("LocalStart", 0);
+ p.AddP70time("LocalStop", 0);
+ p.AddP70time("ReferenceStart", 0);
+ p.AddP70time("ReferenceStop", 0);
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // AnimationLayer / FbxAnimLayer
+ // this seems to always be here in Maya exports,
+ // but no harm seems to come of leaving it out.
+ // Assimp doesn't support animation layers,
+ // so there will be one per aiAnimation
+ count = mScene->mNumAnimations;
+ if (count) {
+ n = FBX::Node("ObjectType", "AnimationLayer");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FBXAnimLayer");
+ p = FBX::Node("Properties70");
+ p.AddP70("Weight", "Number", "", "A", double(100));
+ p.AddP70bool("Mute", 0);
+ p.AddP70bool("Solo", 0);
+ p.AddP70bool("Lock", 0);
+ p.AddP70color("Color", 0.8, 0.8, 0.8);
+ p.AddP70("BlendMode", "enum", "", "", int32_t(0));
+ p.AddP70("RotationAccumulationMode", "enum", "", "", int32_t(0));
+ p.AddP70("ScaleAccumulationMode", "enum", "", "", int32_t(0));
+ p.AddP70("BlendModeBypass", "ULongLong", "", "", int64_t(0));
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // NodeAttribute
+ // this is completely absurd.
+ // there can only be one "NodeAttribute" template,
+ // but FbxSkeleton, FbxCamera, FbxLight all are "NodeAttributes".
+ // so if only one exists we should set the template for that,
+ // otherwise... we just pick one :/.
+ // the others have to set all their properties every instance,
+ // because there's no template.
+ count = 1; // TODO: select properly
+ if (count) {
+ // FbxSkeleton
+ n = FBX::Node("ObjectType", "NodeAttribute");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FbxSkeleton");
+ p = FBX::Node("Properties70");
+ p.AddP70color("Color", 0.8, 0.8, 0.8);
+ p.AddP70double("Size", 33.333333333333);
+ p.AddP70("LimbLength", "double", "Number", "H", double(1));
+ // note: not sure what the "H" flag is for - hidden?
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // Model / FbxNode
+ // <~~ node hierarchy
+ count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node)
+ if (count) {
+ n = FBX::Node("ObjectType", "Model");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FbxNode");
+ p = FBX::Node("Properties70");
+ p.AddP70enum("QuaternionInterpolate", 0);
+ p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0);
+ p.AddP70vector("RotationPivot", 0.0, 0.0, 0.0);
+ p.AddP70vector("ScalingOffset", 0.0, 0.0, 0.0);
+ p.AddP70vector("ScalingPivot", 0.0, 0.0, 0.0);
+ p.AddP70bool("TranslationActive", 0);
+ p.AddP70vector("TranslationMin", 0.0, 0.0, 0.0);
+ p.AddP70vector("TranslationMax", 0.0, 0.0, 0.0);
+ p.AddP70bool("TranslationMinX", 0);
+ p.AddP70bool("TranslationMinY", 0);
+ p.AddP70bool("TranslationMinZ", 0);
+ p.AddP70bool("TranslationMaxX", 0);
+ p.AddP70bool("TranslationMaxY", 0);
+ p.AddP70bool("TranslationMaxZ", 0);
+ p.AddP70enum("RotationOrder", 0);
+ p.AddP70bool("RotationSpaceForLimitOnly", 0);
+ p.AddP70double("RotationStiffnessX", 0.0);
+ p.AddP70double("RotationStiffnessY", 0.0);
+ p.AddP70double("RotationStiffnessZ", 0.0);
+ p.AddP70double("AxisLen", 10.0);
+ p.AddP70vector("PreRotation", 0.0, 0.0, 0.0);
+ p.AddP70vector("PostRotation", 0.0, 0.0, 0.0);
+ p.AddP70bool("RotationActive", 0);
+ p.AddP70vector("RotationMin", 0.0, 0.0, 0.0);
+ p.AddP70vector("RotationMax", 0.0, 0.0, 0.0);
+ p.AddP70bool("RotationMinX", 0);
+ p.AddP70bool("RotationMinY", 0);
+ p.AddP70bool("RotationMinZ", 0);
+ p.AddP70bool("RotationMaxX", 0);
+ p.AddP70bool("RotationMaxY", 0);
+ p.AddP70bool("RotationMaxZ", 0);
+ p.AddP70enum("InheritType", 0);
+ p.AddP70bool("ScalingActive", 0);
+ p.AddP70vector("ScalingMin", 0.0, 0.0, 0.0);
+ p.AddP70vector("ScalingMax", 1.0, 1.0, 1.0);
+ p.AddP70bool("ScalingMinX", 0);
+ p.AddP70bool("ScalingMinY", 0);
+ p.AddP70bool("ScalingMinZ", 0);
+ p.AddP70bool("ScalingMaxX", 0);
+ p.AddP70bool("ScalingMaxY", 0);
+ p.AddP70bool("ScalingMaxZ", 0);
+ p.AddP70vector("GeometricTranslation", 0.0, 0.0, 0.0);
+ p.AddP70vector("GeometricRotation", 0.0, 0.0, 0.0);
+ p.AddP70vector("GeometricScaling", 1.0, 1.0, 1.0);
+ p.AddP70double("MinDampRangeX", 0.0);
+ p.AddP70double("MinDampRangeY", 0.0);
+ p.AddP70double("MinDampRangeZ", 0.0);
+ p.AddP70double("MaxDampRangeX", 0.0);
+ p.AddP70double("MaxDampRangeY", 0.0);
+ p.AddP70double("MaxDampRangeZ", 0.0);
+ p.AddP70double("MinDampStrengthX", 0.0);
+ p.AddP70double("MinDampStrengthY", 0.0);
+ p.AddP70double("MinDampStrengthZ", 0.0);
+ p.AddP70double("MaxDampStrengthX", 0.0);
+ p.AddP70double("MaxDampStrengthY", 0.0);
+ p.AddP70double("MaxDampStrengthZ", 0.0);
+ p.AddP70double("PreferedAngleX", 0.0);
+ p.AddP70double("PreferedAngleY", 0.0);
+ p.AddP70double("PreferedAngleZ", 0.0);
+ p.AddP70("LookAtProperty", "object", "", "");
+ p.AddP70("UpVectorProperty", "object", "", "");
+ p.AddP70bool("Show", 1);
+ p.AddP70bool("NegativePercentShapeSupport", 1);
+ p.AddP70int("DefaultAttributeIndex", -1);
+ p.AddP70bool("Freeze", 0);
+ p.AddP70bool("LODBox", 0);
+ p.AddP70(
+ "Lcl Translation", "Lcl Translation", "", "A",
+ double(0), double(0), double(0)
+ );
+ p.AddP70(
+ "Lcl Rotation", "Lcl Rotation", "", "A",
+ double(0), double(0), double(0)
+ );
+ p.AddP70(
+ "Lcl Scaling", "Lcl Scaling", "", "A",
+ double(1), double(1), double(1)
+ );
+ p.AddP70("Visibility", "Visibility", "", "A", double(1));
+ p.AddP70(
+ "Visibility Inheritance", "Visibility Inheritance", "", "",
+ int32_t(1)
+ );
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // Geometry / FbxMesh
+ // <~~ aiMesh
+ count = mScene->mNumMeshes;
+ if (count) {
+ n = FBX::Node("ObjectType", "Geometry");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FbxMesh");
+ p = FBX::Node("Properties70");
+ p.AddP70color("Color", 0, 0, 0);
+ p.AddP70vector("BBoxMin", 0, 0, 0);
+ p.AddP70vector("BBoxMax", 0, 0, 0);
+ p.AddP70bool("Primary Visibility", 1);
+ p.AddP70bool("Casts Shadows", 1);
+ p.AddP70bool("Receive Shadows", 1);
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // Material / FbxSurfacePhong, FbxSurfaceLambert, FbxSurfaceMaterial
+ // <~~ aiMaterial
+ // basically if there's any phong material this is defined as phong,
+ // and otherwise lambert.
+ // More complex materials cause a bare-bones FbxSurfaceMaterial definition
+ // and are treated specially, as they're not really supported by FBX.
+ // TODO: support Maya's Stingray PBS material
+ count = mScene->mNumMaterials;
+ if (count) {
+ bool has_phong = has_phong_mat(mScene);
+ n = FBX::Node("ObjectType", "Material");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate");
+ if (has_phong) {
+ pt.AddProperty("FbxSurfacePhong");
+ } else {
+ pt.AddProperty("FbxSurfaceLambert");
+ }
+ p = FBX::Node("Properties70");
+ if (has_phong) {
+ p.AddP70string("ShadingModel", "Phong");
+ } else {
+ p.AddP70string("ShadingModel", "Lambert");
+ }
+ p.AddP70bool("MultiLayer", 0);
+ p.AddP70colorA("EmissiveColor", 0.0, 0.0, 0.0);
+ p.AddP70numberA("EmissiveFactor", 1.0);
+ p.AddP70colorA("AmbientColor", 0.2, 0.2, 0.2);
+ p.AddP70numberA("AmbientFactor", 1.0);
+ p.AddP70colorA("DiffuseColor", 0.8, 0.8, 0.8);
+ p.AddP70numberA("DiffuseFactor", 1.0);
+ p.AddP70vector("Bump", 0.0, 0.0, 0.0);
+ p.AddP70vector("NormalMap", 0.0, 0.0, 0.0);
+ p.AddP70double("BumpFactor", 1.0);
+ p.AddP70colorA("TransparentColor", 0.0, 0.0, 0.0);
+ p.AddP70numberA("TransparencyFactor", 0.0);
+ p.AddP70color("DisplacementColor", 0.0, 0.0, 0.0);
+ p.AddP70double("DisplacementFactor", 1.0);
+ p.AddP70color("VectorDisplacementColor", 0.0, 0.0, 0.0);
+ p.AddP70double("VectorDisplacementFactor", 1.0);
+ if (has_phong) {
+ p.AddP70colorA("SpecularColor", 0.2, 0.2, 0.2);
+ p.AddP70numberA("SpecularFactor", 1.0);
+ p.AddP70numberA("ShininessExponent", 20.0);
+ p.AddP70colorA("ReflectionColor", 0.0, 0.0, 0.0);
+ p.AddP70numberA("ReflectionFactor", 1.0);
+ }
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // Video / FbxVideo
+ // one for each image file.
+ count = int32_t(count_images(mScene));
+ if (count) {
+ n = FBX::Node("ObjectType", "Video");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FbxVideo");
+ p = FBX::Node("Properties70");
+ p.AddP70bool("ImageSequence", 0);
+ p.AddP70int("ImageSequenceOffset", 0);
+ p.AddP70double("FrameRate", 0.0);
+ p.AddP70int("LastFrame", 0);
+ p.AddP70int("Width", 0);
+ p.AddP70int("Height", 0);
+ p.AddP70("Path", "KString", "XRefUrl", "", "");
+ p.AddP70int("StartFrame", 0);
+ p.AddP70int("StopFrame", 0);
+ p.AddP70double("PlaySpeed", 0.0);
+ p.AddP70time("Offset", 0);
+ p.AddP70enum("InterlaceMode", 0);
+ p.AddP70bool("FreeRunning", 0);
+ p.AddP70bool("Loop", 0);
+ p.AddP70enum("AccessMode", 0);
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // Texture / FbxFileTexture
+ // <~~ aiTexture
+ count = int32_t(count_textures(mScene));
+ if (count) {
+ n = FBX::Node("ObjectType", "Texture");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FbxFileTexture");
+ p = FBX::Node("Properties70");
+ p.AddP70enum("TextureTypeUse", 0);
+ p.AddP70numberA("Texture alpha", 1.0);
+ p.AddP70enum("CurrentMappingType", 0);
+ p.AddP70enum("WrapModeU", 0);
+ p.AddP70enum("WrapModeV", 0);
+ p.AddP70bool("UVSwap", 0);
+ p.AddP70bool("PremultiplyAlpha", 1);
+ p.AddP70vectorA("Translation", 0.0, 0.0, 0.0);
+ p.AddP70vectorA("Rotation", 0.0, 0.0, 0.0);
+ p.AddP70vectorA("Scaling", 1.0, 1.0, 1.0);
+ p.AddP70vector("TextureRotationPivot", 0.0, 0.0, 0.0);
+ p.AddP70vector("TextureScalingPivot", 0.0, 0.0, 0.0);
+ p.AddP70enum("CurrentTextureBlendMode", 1);
+ p.AddP70string("UVSet", "default");
+ p.AddP70bool("UseMaterial", 0);
+ p.AddP70bool("UseMipMap", 0);
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // AnimationCurveNode / FbxAnimCurveNode
+ count = mScene->mNumAnimations * 3;
+ if (count) {
+ n = FBX::Node("ObjectType", "AnimationCurveNode");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode");
+ p = FBX::Node("Properties70");
+ p.AddP70("d", "Compound", "", "");
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // AnimationCurve / FbxAnimCurve
+ count = mScene->mNumAnimations * 9;
+ if (count) {
+ n = FBX::Node("ObjectType", "AnimationCurve");
+ n.AddChild("Count", count);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // Pose
+ count = 0;
+ for (size_t i = 0; i < mScene->mNumMeshes; ++i) {
+ aiMesh* mesh = mScene->mMeshes[i];
+ if (mesh->HasBones()) { ++count; }
+ }
+ if (count) {
+ n = FBX::Node("ObjectType", "Pose");
+ n.AddChild("Count", count);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // Deformer
+ count = int32_t(count_deformers(mScene));
+ if (count) {
+ n = FBX::Node("ObjectType", "Deformer");
+ n.AddChild("Count", count);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // (template)
+ count = 0;
+ if (count) {
+ n = FBX::Node("ObjectType", "");
+ n.AddChild("Count", count);
+ pt = FBX::Node("PropertyTemplate", "");
+ p = FBX::Node("Properties70");
+ pt.AddChild(p);
+ n.AddChild(pt);
+ object_nodes.push_back(n);
+ total_count += count;
+ }
+
+ // now write it all
+ FBX::Node defs("Definitions");
+ defs.AddChild("Version", int32_t(100));
+ defs.AddChild("Count", int32_t(total_count));
+ for (auto &n : object_nodes) { defs.AddChild(n); }
+ defs.Dump(outfile, binary, 0);
+}
+
+
+// -------------------------------------------------------------------
+// some internal helper functions used for writing the objects section
+// (which holds the actual data)
+// -------------------------------------------------------------------
+
+aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node)
+{
+ for (size_t i = 0; i < node->mNumMeshes; ++i) {
+ if (node->mMeshes[i] == meshIndex) {
+ return node;
+ }
+ }
+ for (size_t i = 0; i < node->mNumChildren; ++i) {
+ aiNode* ret = get_node_for_mesh(meshIndex, node->mChildren[i]);
+ if (ret) { return ret; }
+ }
+ return nullptr;
+}
+
+aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene)
+{
+ std::vector<const aiNode*> node_chain;
+ while (node != scene->mRootNode) {
+ node_chain.push_back(node);
+ node = node->mParent;
+ }
+ aiMatrix4x4 transform;
+ for (auto n = node_chain.rbegin(); n != node_chain.rend(); ++n) {
+ transform *= (*n)->mTransformation;
+ }
+ return transform;
+}
+
+int64_t to_ktime(double ticks, const aiAnimation* anim) {
+ if (anim->mTicksPerSecond <= 0) {
+ return static_cast<int64_t>(ticks) * FBX::SECOND;
+ }
+ return (static_cast<int64_t>(ticks) / static_cast<int64_t>(anim->mTicksPerSecond)) * FBX::SECOND;
+}
+
+int64_t to_ktime(double time) {
+ return (static_cast<int64_t>(time * FBX::SECOND));
+}
+
+void FBXExporter::WriteObjects ()
+{
+ if (!binary) {
+ WriteAsciiSectionHeader("Object properties");
+ }
+ // numbers should match those given in definitions! make sure to check
+ StreamWriterLE outstream(outfile);
+ FBX::Node object_node("Objects");
+ int indent = 0;
+ object_node.Begin(outstream, binary, indent);
+ object_node.EndProperties(outstream, binary, indent);
+ object_node.BeginChildren(outstream, binary, indent);
+
+ // geometry (aiMesh)
+ mesh_uids.clear();
+ indent = 1;
+ for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+ // it's all about this mesh
+ aiMesh* m = mScene->mMeshes[mi];
+
+ // start the node record
+ FBX::Node n("Geometry");
+ int64_t uid = generate_uid();
+ mesh_uids.push_back(uid);
+ n.AddProperty(uid);
+ n.AddProperty(FBX::SEPARATOR + "Geometry");
+ n.AddProperty("Mesh");
+ n.Begin(outstream, binary, indent);
+ n.DumpProperties(outstream, binary, indent);
+ n.EndProperties(outstream, binary, indent);
+ n.BeginChildren(outstream, binary, indent);
+ indent = 2;
+
+ // output vertex data - each vertex should be unique (probably)
+ std::vector<double> flattened_vertices;
+ // index of original vertex in vertex data vector
+ std::vector<int32_t> vertex_indices;
+ // map of vertex value to its index in the data vector
+ std::map<aiVector3D,size_t> index_by_vertex_value;
+ int32_t index = 0;
+ for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
+ aiVector3D vtx = m->mVertices[vi];
+ auto elem = index_by_vertex_value.find(vtx);
+ if (elem == index_by_vertex_value.end()) {
+ vertex_indices.push_back(index);
+ index_by_vertex_value[vtx] = index;
+ flattened_vertices.push_back(vtx[0]);
+ flattened_vertices.push_back(vtx[1]);
+ flattened_vertices.push_back(vtx[2]);
+ ++index;
+ } else {
+ vertex_indices.push_back(int32_t(elem->second));
+ }
+ }
+ FBX::Node::WritePropertyNode(
+ "Vertices", flattened_vertices, outstream, binary, indent
+ );
+
+ // output polygon data as a flattened array of vertex indices.
+ // the last vertex index of each polygon is negated and - 1
+ std::vector<int32_t> polygon_data;
+ for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
+ const aiFace &f = m->mFaces[fi];
+ for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) {
+ polygon_data.push_back(vertex_indices[f.mIndices[pvi]]);
+ }
+ polygon_data.push_back(
+ -1 - vertex_indices[f.mIndices[f.mNumIndices-1]]
+ );
+ }
+ FBX::Node::WritePropertyNode(
+ "PolygonVertexIndex", polygon_data, outstream, binary, indent
+ );
+
+ // here could be edges but they're insane.
+ // it's optional anyway, so let's ignore it.
+
+ FBX::Node::WritePropertyNode(
+ "GeometryVersion", int32_t(124), outstream, binary, indent
+ );
+
+ // normals, if any
+ if (m->HasNormals()) {
+ FBX::Node normals("LayerElementNormal", int32_t(0));
+ normals.Begin(outstream, binary, indent);
+ normals.DumpProperties(outstream, binary, indent);
+ normals.EndProperties(outstream, binary, indent);
+ normals.BeginChildren(outstream, binary, indent);
+ indent = 3;
+ FBX::Node::WritePropertyNode(
+ "Version", int32_t(101), outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "Name", "", outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "MappingInformationType", "ByPolygonVertex",
+ outstream, binary, indent
+ );
+ // TODO: vertex-normals or indexed normals when appropriate
+ FBX::Node::WritePropertyNode(
+ "ReferenceInformationType", "Direct",
+ outstream, binary, indent
+ );
+ std::vector<double> normal_data;
+ normal_data.reserve(3 * polygon_data.size());
+ for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
+ const aiFace &f = m->mFaces[fi];
+ for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
+ const aiVector3D &n = m->mNormals[f.mIndices[pvi]];
+ normal_data.push_back(n.x);
+ normal_data.push_back(n.y);
+ normal_data.push_back(n.z);
+ }
+ }
+ FBX::Node::WritePropertyNode(
+ "Normals", normal_data, outstream, binary, indent
+ );
+ // note: version 102 has a NormalsW also... not sure what it is,
+ // so we can stick with version 101 for now.
+ indent = 2;
+ normals.End(outstream, binary, indent, true);
+ }
+
+ // uvs, if any
+ for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) {
+ if (m->mNumUVComponents[uvi] > 2) {
+ // FBX only supports 2-channel UV maps...
+ // or at least i'm not sure how to indicate a different number
+ std::stringstream err;
+ err << "Only 2-channel UV maps supported by FBX,";
+ err << " but mesh " << mi;
+ if (m->mName.length) {
+ err << " (" << m->mName.C_Str() << ")";
+ }
+ err << " UV map " << uvi;
+ err << " has " << m->mNumUVComponents[uvi];
+ err << " components! Data will be preserved,";
+ err << " but may be incorrectly interpreted on load.";
+ ASSIMP_LOG_WARN(err.str());
+ }
+ FBX::Node uv("LayerElementUV", int32_t(uvi));
+ uv.Begin(outstream, binary, indent);
+ uv.DumpProperties(outstream, binary, indent);
+ uv.EndProperties(outstream, binary, indent);
+ uv.BeginChildren(outstream, binary, indent);
+ indent = 3;
+ FBX::Node::WritePropertyNode(
+ "Version", int32_t(101), outstream, binary, indent
+ );
+ // it doesn't seem like assimp keeps the uv map name,
+ // so just leave it blank.
+ FBX::Node::WritePropertyNode(
+ "Name", "", outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "MappingInformationType", "ByPolygonVertex",
+ outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "ReferenceInformationType", "IndexToDirect",
+ outstream, binary, indent
+ );
+
+ std::vector<double> uv_data;
+ std::vector<int32_t> uv_indices;
+ std::map<aiVector3D,int32_t> index_by_uv;
+ int32_t index = 0;
+ for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
+ const aiFace &f = m->mFaces[fi];
+ for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
+ const aiVector3D &uv =
+ m->mTextureCoords[uvi][f.mIndices[pvi]];
+ auto elem = index_by_uv.find(uv);
+ if (elem == index_by_uv.end()) {
+ index_by_uv[uv] = index;
+ uv_indices.push_back(index);
+ for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) {
+ uv_data.push_back(uv[x]);
+ }
+ ++index;
+ } else {
+ uv_indices.push_back(elem->second);
+ }
+ }
+ }
+ FBX::Node::WritePropertyNode(
+ "UV", uv_data, outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "UVIndex", uv_indices, outstream, binary, indent
+ );
+ indent = 2;
+ uv.End(outstream, binary, indent, true);
+ }
+
+ // i'm not really sure why this material section exists,
+ // as the material is linked via "Connections".
+ // it seems to always have the same "0" value.
+ FBX::Node mat("LayerElementMaterial", int32_t(0));
+ mat.AddChild("Version", int32_t(101));
+ mat.AddChild("Name", "");
+ mat.AddChild("MappingInformationType", "AllSame");
+ mat.AddChild("ReferenceInformationType", "IndexToDirect");
+ std::vector<int32_t> mat_indices = {0};
+ mat.AddChild("Materials", mat_indices);
+ mat.Dump(outstream, binary, indent);
+
+ // finally we have the layer specifications,
+ // which select the normals / UV set / etc to use.
+ // TODO: handle multiple uv sets correctly?
+ FBX::Node layer("Layer", int32_t(0));
+ layer.AddChild("Version", int32_t(100));
+ FBX::Node le("LayerElement");
+ le.AddChild("Type", "LayerElementNormal");
+ le.AddChild("TypedIndex", int32_t(0));
+ layer.AddChild(le);
+ le = FBX::Node("LayerElement");
+ le.AddChild("Type", "LayerElementMaterial");
+ le.AddChild("TypedIndex", int32_t(0));
+ layer.AddChild(le);
+ le = FBX::Node("LayerElement");
+ le.AddChild("Type", "LayerElementUV");
+ le.AddChild("TypedIndex", int32_t(0));
+ layer.AddChild(le);
+ layer.Dump(outstream, binary, indent);
+
+ // finish the node record
+ indent = 1;
+ n.End(outstream, binary, indent, true);
+ }
+
+ // aiMaterial
+ material_uids.clear();
+ for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
+ // it's all about this material
+ aiMaterial* m = mScene->mMaterials[i];
+
+ // these are used to receive material data
+ float f; aiColor3D c;
+
+ // start the node record
+ FBX::Node n("Material");
+
+ int64_t uid = generate_uid();
+ material_uids.push_back(uid);
+ n.AddProperty(uid);
+
+ aiString name;
+ m->Get(AI_MATKEY_NAME, name);
+ n.AddProperty(name.C_Str() + FBX::SEPARATOR + "Material");
+
+ n.AddProperty("");
+
+ n.AddChild("Version", int32_t(102));
+ f = 0;
+ m->Get(AI_MATKEY_SHININESS, f);
+ bool phong = (f > 0);
+ if (phong) {
+ n.AddChild("ShadingModel", "phong");
+ } else {
+ n.AddChild("ShadingModel", "lambert");
+ }
+ n.AddChild("MultiLayer", int32_t(0));
+
+ FBX::Node p("Properties70");
+
+ // materials exported using the FBX SDK have two sets of fields.
+ // there are the properties specified in the PropertyTemplate,
+ // which are those supported by the modernFBX SDK,
+ // and an extra set of properties with simpler names.
+ // The extra properties are a legacy material system from pre-2009.
+ //
+ // In the modern system, each property has "color" and "factor".
+ // Generally the interpretation of these seems to be
+ // that the colour is multiplied by the factor before use,
+ // but this is not always clear-cut.
+ //
+ // Usually assimp only stores the colour,
+ // so we can just leave the factors at the default "1.0".
+
+ // first we can export the "standard" properties
+ if (m->Get(AI_MATKEY_COLOR_AMBIENT, c) == aiReturn_SUCCESS) {
+ p.AddP70colorA("AmbientColor", c.r, c.g, c.b);
+ //p.AddP70numberA("AmbientFactor", 1.0);
+ }
+ if (m->Get(AI_MATKEY_COLOR_DIFFUSE, c) == aiReturn_SUCCESS) {
+ p.AddP70colorA("DiffuseColor", c.r, c.g, c.b);
+ //p.AddP70numberA("DiffuseFactor", 1.0);
+ }
+ if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) {
+ // "TransparentColor" / "TransparencyFactor"...
+ // thanks FBX, for your insightful interpretation of consistency
+ p.AddP70colorA("TransparentColor", c.r, c.g, c.b);
+ // TransparencyFactor defaults to 0.0, so set it to 1.0.
+ // note: Maya always sets this to 1.0,
+ // so we can't use it sensibly as "Opacity".
+ // In stead we rely on the legacy "Opacity" value, below.
+ // Blender also relies on "Opacity" not "TransparencyFactor",
+ // probably for a similar reason.
+ p.AddP70numberA("TransparencyFactor", 1.0);
+ }
+ if (m->Get(AI_MATKEY_COLOR_REFLECTIVE, c) == aiReturn_SUCCESS) {
+ p.AddP70colorA("ReflectionColor", c.r, c.g, c.b);
+ }
+ if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) {
+ p.AddP70numberA("ReflectionFactor", f);
+ }
+ if (phong) {
+ if (m->Get(AI_MATKEY_COLOR_SPECULAR, c) == aiReturn_SUCCESS) {
+ p.AddP70colorA("SpecularColor", c.r, c.g, c.b);
+ }
+ if (m->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) {
+ p.AddP70numberA("ShininessFactor", f);
+ }
+ if (m->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS) {
+ p.AddP70numberA("ShininessExponent", f);
+ }
+ if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) {
+ p.AddP70numberA("ReflectionFactor", f);
+ }
+ }
+
+ // Now the legacy system.
+ // For safety let's include it.
+ // thrse values don't exist in the property template,
+ // and usually are completely ignored when loading.
+ // One notable exception is the "Opacity" property,
+ // which Blender uses as (1.0 - alpha).
+ c.r = 0.0f; c.g = 0.0f; c.b = 0.0f;
+ m->Get(AI_MATKEY_COLOR_EMISSIVE, c);
+ p.AddP70vector("Emissive", c.r, c.g, c.b);
+ c.r = 0.2f; c.g = 0.2f; c.b = 0.2f;
+ m->Get(AI_MATKEY_COLOR_AMBIENT, c);
+ p.AddP70vector("Ambient", c.r, c.g, c.b);
+ c.r = 0.8f; c.g = 0.8f; c.b = 0.8f;
+ m->Get(AI_MATKEY_COLOR_DIFFUSE, c);
+ p.AddP70vector("Diffuse", c.r, c.g, c.b);
+ // The FBX SDK determines "Opacity" from transparency colour (RGB)
+ // and factor (F) as: O = (1.0 - F * ((R + G + B) / 3)).
+ // However we actually have an opacity value,
+ // so we should take it from AI_MATKEY_OPACITY if possible.
+ // It might make more sense to use TransparencyFactor,
+ // but Blender actually loads "Opacity" correctly, so let's use it.
+ f = 1.0f;
+ if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) {
+ f = 1.0f - ((c.r + c.g + c.b) / 3.0f);
+ }
+ m->Get(AI_MATKEY_OPACITY, f);
+ p.AddP70double("Opacity", f);
+ if (phong) {
+ // specular color is multiplied by shininess_strength
+ c.r = 0.2f; c.g = 0.2f; c.b = 0.2f;
+ m->Get(AI_MATKEY_COLOR_SPECULAR, c);
+ f = 1.0f;
+ m->Get(AI_MATKEY_SHININESS_STRENGTH, f);
+ p.AddP70vector("Specular", f*c.r, f*c.g, f*c.b);
+ f = 20.0f;
+ m->Get(AI_MATKEY_SHININESS, f);
+ p.AddP70double("Shininess", f);
+ // Legacy "Reflectivity" is F*F*((R+G+B)/3),
+ // where F is the proportion of light reflected (AKA reflectivity),
+ // and RGB is the reflective colour of the material.
+ // No idea why, but we might as well set it the same way.
+ f = 0.0f;
+ m->Get(AI_MATKEY_REFLECTIVITY, f);
+ c.r = 1.0f, c.g = 1.0f, c.b = 1.0f;
+ m->Get(AI_MATKEY_COLOR_REFLECTIVE, c);
+ p.AddP70double("Reflectivity", f*f*((c.r+c.g+c.b)/3.0));
+ }
+
+ n.AddChild(p);
+
+ n.Dump(outstream, binary, indent);
+ }
+
+ // we need to look up all the images we're using,
+ // so we can generate uids, and eliminate duplicates.
+ std::map<std::string, int64_t> uid_by_image;
+ for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
+ aiString texpath;
+ aiMaterial* mat = mScene->mMaterials[i];
+ for (
+ size_t tt = aiTextureType_DIFFUSE;
+ tt < aiTextureType_UNKNOWN;
+ ++tt
+ ){
+ const aiTextureType textype = static_cast<aiTextureType>(tt);
+ const size_t texcount = mat->GetTextureCount(textype);
+ for (size_t j = 0; j < texcount; ++j) {
+ mat->GetTexture(textype, (unsigned int)j, &texpath);
+ const std::string texstring = texpath.C_Str();
+ auto elem = uid_by_image.find(texstring);
+ if (elem == uid_by_image.end()) {
+ uid_by_image[texstring] = generate_uid();
+ }
+ }
+ }
+ }
+
+ // FbxVideo - stores images used by textures.
+ for (const auto &it : uid_by_image) {
+ if (it.first.compare(0, 1, "*") == 0) {
+ // TODO: embedded textures
+ continue;
+ }
+ FBX::Node n("Video");
+ const int64_t& uid = it.second;
+ const std::string name = ""; // TODO: ... name???
+ n.AddProperties(uid, name + FBX::SEPARATOR + "Video", "Clip");
+ n.AddChild("Type", "Clip");
+ FBX::Node p("Properties70");
+ // TODO: get full path... relative path... etc... ugh...
+ // for now just use the same path for everything,
+ // and hopefully one of them will work out.
+ const std::string& path = it.first;
+ p.AddP70("Path", "KString", "XRefUrl", "", path);
+ n.AddChild(p);
+ n.AddChild("UseMipMap", int32_t(0));
+ n.AddChild("Filename", path);
+ n.AddChild("RelativeFilename", path);
+ n.Dump(outstream, binary, indent);
+ }
+
+ // Textures
+ // referenced by material_index/texture_type pairs.
+ std::map<std::pair<size_t,size_t>,int64_t> texture_uids;
+ const std::map<aiTextureType,std::string> prop_name_by_tt = {
+ {aiTextureType_DIFFUSE, "DiffuseColor"},
+ {aiTextureType_SPECULAR, "SpecularColor"},
+ {aiTextureType_AMBIENT, "AmbientColor"},
+ {aiTextureType_EMISSIVE, "EmissiveColor"},
+ {aiTextureType_HEIGHT, "Bump"},
+ {aiTextureType_NORMALS, "NormalMap"},
+ {aiTextureType_SHININESS, "ShininessExponent"},
+ {aiTextureType_OPACITY, "TransparentColor"},
+ {aiTextureType_DISPLACEMENT, "DisplacementColor"},
+ //{aiTextureType_LIGHTMAP, "???"},
+ {aiTextureType_REFLECTION, "ReflectionColor"}
+ //{aiTextureType_UNKNOWN, ""}
+ };
+ for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
+ // textures are attached to materials
+ aiMaterial* mat = mScene->mMaterials[i];
+ int64_t material_uid = material_uids[i];
+
+ for (
+ size_t j = aiTextureType_DIFFUSE;
+ j < aiTextureType_UNKNOWN;
+ ++j
+ ) {
+ const aiTextureType tt = static_cast<aiTextureType>(j);
+ size_t n = mat->GetTextureCount(tt);
+
+ if (n < 1) { // no texture of this type
+ continue;
+ }
+
+ if (n > 1) {
+ // TODO: multilayer textures
+ std::stringstream err;
+ err << "Multilayer textures not supported (for now),";
+ err << " skipping texture type " << j;
+ err << " of material " << i;
+ ASSIMP_LOG_WARN(err.str());
+ }
+
+ // get image path for this (single-image) texture
+ aiString tpath;
+ if (mat->GetTexture(tt, 0, &tpath) != aiReturn_SUCCESS) {
+ std::stringstream err;
+ err << "Failed to get texture 0 for texture of type " << tt;
+ err << " on material " << i;
+ err << ", however GetTextureCount returned 1.";
+ throw DeadlyExportError(err.str());
+ }
+ const std::string texture_path(tpath.C_Str());
+
+ // get connected image uid
+ auto elem = uid_by_image.find(texture_path);
+ if (elem == uid_by_image.end()) {
+ // this should never happen
+ std::stringstream err;
+ err << "Failed to find video element for texture with path";
+ err << " \"" << texture_path << "\"";
+ err << ", type " << j << ", material " << i;
+ throw DeadlyExportError(err.str());
+ }
+ const int64_t image_uid = elem->second;
+
+ // get the name of the material property to connect to
+ auto elem2 = prop_name_by_tt.find(tt);
+ if (elem2 == prop_name_by_tt.end()) {
+ // don't know how to handle this type of texture,
+ // so skip it.
+ std::stringstream err;
+ err << "Not sure how to handle texture of type " << j;
+ err << " on material " << i;
+ err << ", skipping...";
+ ASSIMP_LOG_WARN(err.str());
+ continue;
+ }
+ const std::string& prop_name = elem2->second;
+
+ // generate a uid for this texture
+ const int64_t texture_uid = generate_uid();
+
+ // link the texture to the material
+ connections.emplace_back(
+ "C", "OP", texture_uid, material_uid, prop_name
+ );
+
+ // link the image data to the texture
+ connections.emplace_back("C", "OO", image_uid, texture_uid);
+
+ // now write the actual texture node
+ FBX::Node tnode("Texture");
+ // TODO: some way to determine texture name?
+ const std::string texture_name = "" + FBX::SEPARATOR + "Texture";
+ tnode.AddProperties(texture_uid, texture_name, "");
+ // there really doesn't seem to be a better type than this:
+ tnode.AddChild("Type", "TextureVideoClip");
+ tnode.AddChild("Version", int32_t(202));
+ tnode.AddChild("TextureName", texture_name);
+ FBX::Node p("Properties70");
+ p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify
+ //p.AddP70string("UVSet", ""); // TODO: how should this work?
+ p.AddP70bool("UseMaterial", 1);
+ tnode.AddChild(p);
+ // can't easily detrmine which texture path will be correct,
+ // so just store what we have in every field.
+ // these being incorrect is a common problem with FBX anyway.
+ tnode.AddChild("FileName", texture_path);
+ tnode.AddChild("RelativeFilename", texture_path);
+ tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0));
+ tnode.AddChild("ModelUVScaling", double(1.0), double(1.0));
+ tnode.AddChild("Texture_Alpha_Source", "None");
+ tnode.AddChild(
+ "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0)
+ );
+ tnode.Dump(outstream, binary, indent);
+ }
+ }
+
+ // bones.
+ //
+ // output structure:
+ // subset of node hierarchy that are "skeleton",
+ // i.e. do not have meshes but only bones.
+ // but.. i'm not sure how anyone could guarantee that...
+ //
+ // input...
+ // well, for each mesh it has "bones",
+ // and the bone names correspond to nodes.
+ // of course we also need the parent nodes,
+ // as they give some of the transform........
+ //
+ // well. we can assume a sane input, i suppose.
+ //
+ // so input is the bone node hierarchy,
+ // with an extra thing for the transformation of the MESH in BONE space.
+ //
+ // output is a set of bone nodes,
+ // a "bindpose" which indicates the default local transform of all bones,
+ // and a set of "deformers".
+ // each deformer is parented to a mesh geometry,
+ // and has one or more "subdeformer"s as children.
+ // each subdeformer has one bone node as a child,
+ // and represents the influence of that bone on the grandparent mesh.
+ // the subdeformer has a list of indices, and weights,
+ // with indices specifying vertex indices,
+ // and weights specifying the corresponding influence of this bone.
+ // it also has Transform and TransformLink elements,
+ // specifying the transform of the MESH in BONE space,
+ // and the transformation of the BONE in WORLD space,
+ // likely in the bindpose.
+ //
+ // the input bone structure is different but similar,
+ // storing the number of weights for this bone,
+ // and an array of (vertex index, weight) pairs.
+ //
+ // one sticky point is that the number of vertices may not match,
+ // because assimp splits vertices by normal, uv, etc.
+
+ // first we should mark the skeleton for each mesh.
+ // the skeleton must include not only the aiBones,
+ // but also all their parent nodes.
+ // anything that affects the position of any bone node must be included.
+ std::vector<std::set<const aiNode*>> skeleton_by_mesh(mScene->mNumMeshes);
+ // at the same time we can build a list of all the skeleton nodes,
+ // which will be used later to mark them as type "limbNode".
+ std::unordered_set<const aiNode*> limbnodes;
+ // and a map of nodes by bone name, as finding them is annoying.
+ std::map<std::string,aiNode*> node_by_bone;
+ for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+ const aiMesh* m = mScene->mMeshes[mi];
+ std::set<const aiNode*> skeleton;
+ for (size_t bi =0; bi < m->mNumBones; ++bi) {
+ const aiBone* b = m->mBones[bi];
+ const std::string name(b->mName.C_Str());
+ auto elem = node_by_bone.find(name);
+ aiNode* n;
+ if (elem != node_by_bone.end()) {
+ n = elem->second;
+ } else {
+ n = mScene->mRootNode->FindNode(b->mName);
+ if (!n) {
+ // this should never happen
+ std::stringstream err;
+ err << "Failed to find node for bone: \"" << name << "\"";
+ throw DeadlyExportError(err.str());
+ }
+ node_by_bone[name] = n;
+ limbnodes.insert(n);
+ }
+ skeleton.insert(n);
+ // mark all parent nodes as skeleton as well,
+ // up until we find the root node,
+ // or else the node containing the mesh,
+ // or else the parent of a node containig the mesh.
+ for (
+ const aiNode* parent = n->mParent;
+ parent && parent != mScene->mRootNode;
+ parent = parent->mParent
+ ) {
+ // if we've already done this node we can skip it all
+ if (skeleton.count(parent)) {
+ break;
+ }
+ // ignore fbx transform nodes as these will be collapsed later
+ // TODO: cache this by aiNode*
+ const std::string node_name(parent->mName.C_Str());
+ if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) {
+ continue;
+ }
+ // otherwise check if this is the root of the skeleton
+ bool end = false;
+ // is the mesh part of this node?
+ for (size_t i = 0; i < parent->mNumMeshes; ++i) {
+ if (parent->mMeshes[i] == mi) {
+ end = true;
+ break;
+ }
+ }
+ // is the mesh in one of the children of this node?
+ for (size_t j = 0; j < parent->mNumChildren; ++j) {
+ aiNode* child = parent->mChildren[j];
+ for (size_t i = 0; i < child->mNumMeshes; ++i) {
+ if (child->mMeshes[i] == mi) {
+ end = true;
+ break;
+ }
+ }
+ if (end) { break; }
+ }
+ limbnodes.insert(parent);
+ skeleton.insert(parent);
+ // if it was the skeleton root we can finish here
+ if (end) { break; }
+ }
+ }
+ skeleton_by_mesh[mi] = skeleton;
+ }
+
+ // we'll need the uids for the bone nodes, so generate them now
+ for (size_t i = 0; i < mScene->mNumMeshes; ++i) {
+ auto &s = skeleton_by_mesh[i];
+ for (const aiNode* n : s) {
+ auto elem = node_uids.find(n);
+ if (elem == node_uids.end()) {
+ node_uids[n] = generate_uid();
+ }
+ }
+ }
+
+ // now, for each aiMesh, we need to export a deformer,
+ // and for each aiBone a subdeformer,
+ // which should have all the skinning info.
+ // these will need to be connected properly to the mesh,
+ // and we can do that all now.
+ for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+ const aiMesh* m = mScene->mMeshes[mi];
+ if (!m->HasBones()) {
+ continue;
+ }
+ // make a deformer for this mesh
+ int64_t deformer_uid = generate_uid();
+ FBX::Node dnode("Deformer");
+ dnode.AddProperties(deformer_uid, FBX::SEPARATOR + "Deformer", "Skin");
+ dnode.AddChild("Version", int32_t(101));
+ // "acuracy"... this is not a typo....
+ dnode.AddChild("Link_DeformAcuracy", double(50));
+ dnode.AddChild("SkinningType", "Linear"); // TODO: other modes?
+ dnode.Dump(outstream, binary, indent);
+
+ // connect it
+ connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
+
+ // we will be indexing by vertex...
+ // but there might be a different number of "vertices"
+ // between assimp and our output FBX.
+ // this code is cut-and-pasted from the geometry section above...
+ // ideally this should not be so.
+ // ---
+ // index of original vertex in vertex data vector
+ std::vector<int32_t> vertex_indices;
+ // map of vertex value to its index in the data vector
+ std::map<aiVector3D,size_t> index_by_vertex_value;
+ int32_t index = 0;
+ for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
+ aiVector3D vtx = m->mVertices[vi];
+ auto elem = index_by_vertex_value.find(vtx);
+ if (elem == index_by_vertex_value.end()) {
+ vertex_indices.push_back(index);
+ index_by_vertex_value[vtx] = index;
+ ++index;
+ } else {
+ vertex_indices.push_back(int32_t(elem->second));
+ }
+ }
+
+ // TODO, FIXME: this won't work if anything is not in the bind pose.
+ // for now if such a situation is detected, we throw an exception.
+ std::set<const aiBone*> not_in_bind_pose;
+ std::set<const aiNode*> no_offset_matrix;
+
+ // first get this mesh's position in world space,
+ // as we'll need it for each subdeformer.
+ //
+ // ...of course taking the position of the MESH doesn't make sense,
+ // as it can be instanced to many nodes.
+ // All we can do is assume no instancing,
+ // and take the first node we find that contains the mesh.
+ aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode);
+ aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
+
+ // now make a subdeformer for each bone in the skeleton
+ const std::set<const aiNode*> &skeleton = skeleton_by_mesh[mi];
+ for (const aiNode* bone_node : skeleton) {
+ // if there's a bone for this node, find it
+ const aiBone* b = nullptr;
+ for (size_t bi = 0; bi < m->mNumBones; ++bi) {
+ // TODO: this probably should index by something else
+ const std::string name(m->mBones[bi]->mName.C_Str());
+ if (node_by_bone[name] == bone_node) {
+ b = m->mBones[bi];
+ break;
+ }
+ }
+ if (!b) {
+ no_offset_matrix.insert(bone_node);
+ }
+
+ // start the subdeformer node
+ const int64_t subdeformer_uid = generate_uid();
+ FBX::Node sdnode("Deformer");
+ sdnode.AddProperties(
+ subdeformer_uid, FBX::SEPARATOR + "SubDeformer", "Cluster"
+ );
+ sdnode.AddChild("Version", int32_t(100));
+ sdnode.AddChild("UserData", "", "");
+
+ // add indices and weights, if any
+ if (b) {
+ std::vector<int32_t> subdef_indices;
+ std::vector<double> subdef_weights;
+ int32_t last_index = -1;
+ for (size_t wi = 0; wi < b->mNumWeights; ++wi) {
+ int32_t vi = vertex_indices[b->mWeights[wi].mVertexId];
+ if (vi == last_index) {
+ // only for vertices we exported to fbx
+ // TODO, FIXME: this assumes identically-located vertices
+ // will always deform in the same way.
+ // as assimp doesn't store a separate list of "positions",
+ // there's not much that can be done about this
+ // other than assuming that identical position means
+ // identical vertex.
+ continue;
+ }
+ subdef_indices.push_back(vi);
+ subdef_weights.push_back(b->mWeights[wi].mWeight);
+ last_index = vi;
+ }
+ // yes, "indexes"
+ sdnode.AddChild("Indexes", subdef_indices);
+ sdnode.AddChild("Weights", subdef_weights);
+ }
+
+ // transform is the transform of the mesh, but in bone space.
+ // if the skeleton is in the bind pose,
+ // we can take the inverse of the world-space bone transform
+ // and multiply by the world-space transform of the mesh.
+ aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene);
+ aiMatrix4x4 inverse_bone_xform = bone_xform;
+ inverse_bone_xform.Inverse();
+ aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
+
+ // this should be the same as the bone's mOffsetMatrix.
+ // if it's not the same, the skeleton isn't in the bind pose.
+ const float epsilon = 1e-4f; // some error is to be expected
+ bool bone_xform_okay = true;
+ if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
+ not_in_bind_pose.insert(b);
+ bone_xform_okay = false;
+ }
+
+ // if we have a bone we should use the mOffsetMatrix,
+ // otherwise try to just use the calculated transform.
+ if (b) {
+ sdnode.AddChild("Transform", b->mOffsetMatrix);
+ } else {
+ sdnode.AddChild("Transform", tr);
+ }
+ // note: it doesn't matter if we mix these,
+ // because if they disagree we'll throw an exception later.
+ // it could be that the skeleton is not in the bone pose
+ // but all bones are still defined,
+ // in which case this would use the mOffsetMatrix for everything
+ // and a correct skeleton would still be output.
+
+ // transformlink should be the position of the bone in world space.
+ // if the bone is in the bind pose (or nonexistent),
+ // we can just use the matrix we already calculated
+ if (bone_xform_okay) {
+ sdnode.AddChild("TransformLink", bone_xform);
+ // otherwise we can only work it out using the mesh position.
+ } else {
+ aiMatrix4x4 trl = b->mOffsetMatrix;
+ trl.Inverse();
+ trl *= mesh_xform;
+ sdnode.AddChild("TransformLink", trl);
+ }
+ // note: this means we ALWAYS rely on the mesh node transform
+ // being unchanged from the time the skeleton was bound.
+ // there's not really any way around this at the moment.
+
+ // done
+ sdnode.Dump(outstream, binary, indent);
+
+ // lastly, connect to the parent deformer
+ connections.emplace_back(
+ "C", "OO", subdeformer_uid, deformer_uid
+ );
+
+ // we also need to connect the limb node to the subdeformer.
+ connections.emplace_back(
+ "C", "OO", node_uids[bone_node], subdeformer_uid
+ );
+ }
+
+ // if we cannot create a valid FBX file, simply die.
+ // this will both prevent unnecessary bug reports,
+ // and tell the user what they can do to fix the situation
+ // (i.e. export their model in the bind pose).
+ if (no_offset_matrix.size() && not_in_bind_pose.size()) {
+ std::stringstream err;
+ err << "Not enough information to construct bind pose";
+ err << " for mesh " << mi << "!";
+ err << " Transform matrix for bone \"";
+ err << (*not_in_bind_pose.begin())->mName.C_Str() << "\"";
+ if (not_in_bind_pose.size() > 1) {
+ err << " (and " << not_in_bind_pose.size() - 1 << " more)";
+ }
+ err << " does not match mOffsetMatrix,";
+ err << " and node \"";
+ err << (*no_offset_matrix.begin())->mName.C_Str() << "\"";
+ if (no_offset_matrix.size() > 1) {
+ err << " (and " << no_offset_matrix.size() - 1 << " more)";
+ }
+ err << " has no offset matrix to rely on.";
+ err << " Please ensure bones are in the bind pose to export.";
+ throw DeadlyExportError(err.str());
+ }
+
+ }
+
+ // BindPose
+ //
+ // This is a legacy system, which should be unnecessary.
+ //
+ // Somehow including it slows file loading by the official FBX SDK,
+ // and as it can reconstruct it from the deformers anyway,
+ // this is not currently included.
+ //
+ // The code is kept here in case it's useful in the future,
+ // but it's pretty much a hack anyway,
+ // as assimp doesn't store bindpose information for full skeletons.
+ //
+ /*for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+ aiMesh* mesh = mScene->mMeshes[mi];
+ if (! mesh->HasBones()) { continue; }
+ int64_t bindpose_uid = generate_uid();
+ FBX::Node bpnode("Pose");
+ bpnode.AddProperty(bindpose_uid);
+ // note: this uid is never linked or connected to anything.
+ bpnode.AddProperty(FBX::SEPARATOR + "Pose"); // blank name
+ bpnode.AddProperty("BindPose");
+
+ bpnode.AddChild("Type", "BindPose");
+ bpnode.AddChild("Version", int32_t(100));
+
+ aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode);
+
+ // next get the whole skeleton for this mesh.
+ // we need it all to define the bindpose section.
+ // the FBX SDK will complain if it's missing,
+ // and also if parents of used bones don't have a subdeformer.
+ // order shouldn't matter.
+ std::set<aiNode*> skeleton;
+ for (size_t bi = 0; bi < mesh->mNumBones; ++bi) {
+ // bone node should have already been indexed
+ const aiBone* b = mesh->mBones[bi];
+ const std::string bone_name(b->mName.C_Str());
+ aiNode* parent = node_by_bone[bone_name];
+ // insert all nodes down to the root or mesh node
+ while (
+ parent
+ && parent != mScene->mRootNode
+ && parent != mesh_node
+ ) {
+ skeleton.insert(parent);
+ parent = parent->mParent;
+ }
+ }
+
+ // number of pose nodes. includes one for the mesh itself.
+ bpnode.AddChild("NbPoseNodes", int32_t(1 + skeleton.size()));
+
+ // the first pose node is always the mesh itself
+ FBX::Node pose("PoseNode");
+ pose.AddChild("Node", mesh_uids[mi]);
+ aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene);
+ pose.AddChild("Matrix", mesh_node_xform);
+ bpnode.AddChild(pose);
+
+ for (aiNode* bonenode : skeleton) {
+ // does this node have a uid yet?
+ int64_t node_uid;
+ auto node_uid_iter = node_uids.find(bonenode);
+ if (node_uid_iter != node_uids.end()) {
+ node_uid = node_uid_iter->second;
+ } else {
+ node_uid = generate_uid();
+ node_uids[bonenode] = node_uid;
+ }
+
+ // make a pose thingy
+ pose = FBX::Node("PoseNode");
+ pose.AddChild("Node", node_uid);
+ aiMatrix4x4 node_xform = get_world_transform(bonenode, mScene);
+ pose.AddChild("Matrix", node_xform);
+ bpnode.AddChild(pose);
+ }
+
+ // now write it
+ bpnode.Dump(outstream, binary, indent);
+ }*/
+
+ // TODO: cameras, lights
+
+ // write nodes (i.e. model hierarchy)
+ // start at root node
+ WriteModelNodes(
+ outstream, mScene->mRootNode, 0, limbnodes
+ );
+
+ // animations
+ //
+ // in FBX there are:
+ // * AnimationStack - corresponds to an aiAnimation
+ // * AnimationLayer - a combinable animation component
+ // * AnimationCurveNode - links the property to be animated
+ // * AnimationCurve - defines animation data for a single property value
+ //
+ // the CurveNode also provides the default value for a property,
+ // such as the X, Y, Z coordinates for animatable translation.
+ //
+ // the Curve only specifies values for one component of the property,
+ // so there will be a separate AnimationCurve for X, Y, and Z.
+ //
+ // Assimp has:
+ // * aiAnimation - basically corresponds to an AnimationStack
+ // * aiNodeAnim - defines all animation for one aiNode
+ // * aiVectorKey/aiQuatKey - define the keyframe data for T/R/S
+ //
+ // assimp has no equivalent for AnimationLayer,
+ // and these are flattened on FBX import.
+ // we can assume there will be one per AnimationStack.
+ //
+ // the aiNodeAnim contains all animation data for a single aiNode,
+ // which will correspond to three AnimationCurveNode's:
+ // one each for translation, rotation and scale.
+ // The data for each of these will be put in 9 AnimationCurve's,
+ // T.X, T.Y, T.Z, R.X, R.Y, R.Z, etc.
+
+ // AnimationStack / aiAnimation
+ std::vector<int64_t> animation_stack_uids(mScene->mNumAnimations);
+ for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) {
+ int64_t animstack_uid = generate_uid();
+ animation_stack_uids[ai] = animstack_uid;
+ const aiAnimation* anim = mScene->mAnimations[ai];
+
+ FBX::Node asnode("AnimationStack");
+ std::string name = anim->mName.C_Str() + FBX::SEPARATOR + "AnimStack";
+ asnode.AddProperties(animstack_uid, name, "");
+ FBX::Node p("Properties70");
+ p.AddP70time("LocalStart", 0); // assimp doesn't store this
+ p.AddP70time("LocalStop", to_ktime(anim->mDuration, anim));
+ p.AddP70time("ReferenceStart", 0);
+ p.AddP70time("ReferenceStop", to_ktime(anim->mDuration, anim));
+ asnode.AddChild(p);
+
+ // this node absurdly always pretends it has children
+ // (in this case it does, but just in case...)
+ asnode.force_has_children = true;
+ asnode.Dump(outstream, binary, indent);
+
+ // note: animation stacks are not connected to anything
+ }
+
+ // AnimationLayer - one per aiAnimation
+ std::vector<int64_t> animation_layer_uids(mScene->mNumAnimations);
+ for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) {
+ int64_t animlayer_uid = generate_uid();
+ animation_layer_uids[ai] = animlayer_uid;
+ FBX::Node alnode("AnimationLayer");
+ alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", "");
+
+ // this node absurdly always pretends it has children
+ alnode.force_has_children = true;
+ alnode.Dump(outstream, binary, indent);
+
+ // connect to the relevant animstack
+ connections.emplace_back(
+ "C", "OO", animlayer_uid, animation_stack_uids[ai]
+ );
+ }
+
+ // AnimCurveNode - three per aiNodeAnim
+ std::vector<std::vector<std::array<int64_t,3>>> curve_node_uids;
+ for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) {
+ const aiAnimation* anim = mScene->mAnimations[ai];
+ const int64_t layer_uid = animation_layer_uids[ai];
+ std::vector<std::array<int64_t,3>> nodeanim_uids;
+ for (size_t nai = 0; nai < anim->mNumChannels; ++nai) {
+ const aiNodeAnim* na = anim->mChannels[nai];
+ // get the corresponding aiNode
+ const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName);
+ // and its transform
+ const aiMatrix4x4 node_xfm = get_world_transform(node, mScene);
+ aiVector3D T, R, S;
+ node_xfm.Decompose(S, R, T);
+
+ // AnimationCurveNode uids
+ std::array<int64_t,3> ids;
+ ids[0] = generate_uid(); // T
+ ids[1] = generate_uid(); // R
+ ids[2] = generate_uid(); // S
+
+ // translation
+ WriteAnimationCurveNode(outstream,
+ ids[0], "T", T, "Lcl Translation",
+ layer_uid, node_uids[node]
+ );
+
+ // rotation
+ WriteAnimationCurveNode(outstream,
+ ids[1], "R", R, "Lcl Rotation",
+ layer_uid, node_uids[node]
+ );
+
+ // scale
+ WriteAnimationCurveNode(outstream,
+ ids[2], "S", S, "Lcl Scale",
+ layer_uid, node_uids[node]
+ );
+
+ // store the uids for later use
+ nodeanim_uids.push_back(ids);
+ }
+ curve_node_uids.push_back(nodeanim_uids);
+ }
+
+ // AnimCurve - defines actual keyframe data.
+ // there's a separate curve for every component of every vector,
+ // for example a transform curvenode will have separate X/Y/Z AnimCurve's
+ for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) {
+ const aiAnimation* anim = mScene->mAnimations[ai];
+ for (size_t nai = 0; nai < anim->mNumChannels; ++nai) {
+ const aiNodeAnim* na = anim->mChannels[nai];
+ // get the corresponding aiNode
+ const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName);
+ // and its transform
+ const aiMatrix4x4 node_xfm = get_world_transform(node, mScene);
+ aiVector3D T, R, S;
+ node_xfm.Decompose(S, R, T);
+ const std::array<int64_t,3>& ids = curve_node_uids[ai][nai];
+
+ std::vector<int64_t> times;
+ std::vector<float> xval, yval, zval;
+
+ // position/translation
+ for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) {
+ const aiVectorKey& k = na->mPositionKeys[ki];
+ times.push_back(to_ktime(k.mTime));
+ xval.push_back(k.mValue.x);
+ yval.push_back(k.mValue.y);
+ zval.push_back(k.mValue.z);
+ }
+ // one curve each for X, Y, Z
+ WriteAnimationCurve(outstream, T.x, times, xval, ids[0], "d|X");
+ WriteAnimationCurve(outstream, T.y, times, yval, ids[0], "d|Y");
+ WriteAnimationCurve(outstream, T.z, times, zval, ids[0], "d|Z");
+
+ // rotation
+ times.clear(); xval.clear(); yval.clear(); zval.clear();
+ for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) {
+ const aiQuatKey& k = na->mRotationKeys[ki];
+ times.push_back(to_ktime(k.mTime));
+ // TODO: aiQuaternion method to convert to Euler...
+ aiMatrix4x4 m(k.mValue.GetMatrix());
+ aiVector3D qs, qr, qt;
+ m.Decompose(qs, qr, qt);
+ qr *= DEG;
+ xval.push_back(qr.x);
+ yval.push_back(qr.y);
+ zval.push_back(qr.z);
+ }
+ WriteAnimationCurve(outstream, R.x, times, xval, ids[1], "d|X");
+ WriteAnimationCurve(outstream, R.y, times, yval, ids[1], "d|Y");
+ WriteAnimationCurve(outstream, R.z, times, zval, ids[1], "d|Z");
+
+ // scaling/scale
+ times.clear(); xval.clear(); yval.clear(); zval.clear();
+ for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) {
+ const aiVectorKey& k = na->mScalingKeys[ki];
+ times.push_back(to_ktime(k.mTime));
+ xval.push_back(k.mValue.x);
+ yval.push_back(k.mValue.y);
+ zval.push_back(k.mValue.z);
+ }
+ WriteAnimationCurve(outstream, S.x, times, xval, ids[2], "d|X");
+ WriteAnimationCurve(outstream, S.y, times, yval, ids[2], "d|Y");
+ WriteAnimationCurve(outstream, S.z, times, zval, ids[2], "d|Z");
+ }
+ }
+
+ indent = 0;
+ object_node.End(outstream, binary, indent, true);
+}
+
+// convenience map of magic node name strings to FBX properties,
+// including the expected type of transform.
+const std::map<std::string,std::pair<std::string,char>> transform_types = {
+ {"Translation", {"Lcl Translation", 't'}},
+ {"RotationOffset", {"RotationOffset", 't'}},
+ {"RotationPivot", {"RotationPivot", 't'}},
+ {"PreRotation", {"PreRotation", 'r'}},
+ {"Rotation", {"Lcl Rotation", 'r'}},
+ {"PostRotation", {"PostRotation", 'r'}},
+ {"RotationPivotInverse", {"RotationPivotInverse", 'i'}},
+ {"ScalingOffset", {"ScalingOffset", 't'}},
+ {"ScalingPivot", {"ScalingPivot", 't'}},
+ {"Scaling", {"Lcl Scaling", 's'}},
+ {"ScalingPivotInverse", {"ScalingPivotInverse", 'i'}},
+ {"GeometricScaling", {"GeometricScaling", 's'}},
+ {"GeometricRotation", {"GeometricRotation", 'r'}},
+ {"GeometricTranslation", {"GeometricTranslation", 't'}},
+ {"GeometricTranslationInverse", {"GeometricTranslationInverse", 'i'}},
+ {"GeometricRotationInverse", {"GeometricRotationInverse", 'i'}},
+ {"GeometricScalingInverse", {"GeometricScalingInverse", 'i'}}
+};
+
+// write a single model node to the stream
+void FBXExporter::WriteModelNode(
+ StreamWriterLE& outstream,
+ bool binary,
+ const aiNode* node,
+ int64_t node_uid,
+ const std::string& type,
+ const std::vector<std::pair<std::string,aiVector3D>>& transform_chain,
+ TransformInheritance inherit_type
+){
+ const aiVector3D zero = {0, 0, 0};
+ const aiVector3D one = {1, 1, 1};
+ FBX::Node m("Model");
+ std::string name = node->mName.C_Str() + FBX::SEPARATOR + "Model";
+ m.AddProperties(node_uid, name, type);
+ m.AddChild("Version", int32_t(232));
+ FBX::Node p("Properties70");
+ p.AddP70bool("RotationActive", 1);
+ p.AddP70int("DefaultAttributeIndex", 0);
+ p.AddP70enum("InheritType", inherit_type);
+ if (transform_chain.empty()) {
+ // decompose 4x4 transform matrix into TRS
+ aiVector3D t, r, s;
+ node->mTransformation.Decompose(s, r, t);
+ if (t != zero) {
+ p.AddP70(
+ "Lcl Translation", "Lcl Translation", "", "A",
+ double(t.x), double(t.y), double(t.z)
+ );
+ }
+ if (r != zero) {
+ p.AddP70(
+ "Lcl Rotation", "Lcl Rotation", "", "A",
+ double(DEG*r.x), double(DEG*r.y), double(DEG*r.z)
+ );
+ }
+ if (s != one) {
+ p.AddP70(
+ "Lcl Scaling", "Lcl Scaling", "", "A",
+ double(s.x), double(s.y), double(s.z)
+ );
+ }
+ } else {
+ // apply the transformation chain.
+ // these transformation elements are created when importing FBX,
+ // which has a complex transformation hierarchy for each node.
+ // as such we can bake the hierarchy back into the node on export.
+ for (auto &item : transform_chain) {
+ auto elem = transform_types.find(item.first);
+ if (elem == transform_types.end()) {
+ // then this is a bug
+ std::stringstream err;
+ err << "unrecognized FBX transformation type: ";
+ err << item.first;
+ throw DeadlyExportError(err.str());
+ }
+ const std::string &name = elem->second.first;
+ const aiVector3D &v = item.second;
+ if (name.compare(0, 4, "Lcl ") == 0) {
+ // special handling for animatable properties
+ p.AddP70(
+ name, name, "", "A",
+ double(v.x), double(v.y), double(v.z)
+ );
+ } else {
+ p.AddP70vector(name, v.x, v.y, v.z);
+ }
+ }
+ }
+ m.AddChild(p);
+
+ // not sure what these are for,
+ // but they seem to be omnipresent
+ m.AddChild("Shading", Property(true));
+ m.AddChild("Culling", Property("CullingOff"));
+
+ m.Dump(outstream, binary, 1);
+}
+
+// wrapper for WriteModelNodes to create and pass a blank transform chain
+void FBXExporter::WriteModelNodes(
+ StreamWriterLE& s,
+ const aiNode* node,
+ int64_t parent_uid,
+ const std::unordered_set<const aiNode*>& limbnodes
+) {
+ std::vector<std::pair<std::string,aiVector3D>> chain;
+ WriteModelNodes(s, node, parent_uid, limbnodes, chain);
+}
+
+void FBXExporter::WriteModelNodes(
+ StreamWriterLE& outstream,
+ const aiNode* node,
+ int64_t parent_uid,
+ const std::unordered_set<const aiNode*>& limbnodes,
+ std::vector<std::pair<std::string,aiVector3D>>& transform_chain
+) {
+ // first collapse any expanded transformation chains created by FBX import.
+ std::string node_name(node->mName.C_Str());
+ if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) {
+ auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1;
+ std::string type_name = node_name.substr(pos);
+ auto elem = transform_types.find(type_name);
+ if (elem == transform_types.end()) {
+ // then this is a bug and should be fixed
+ std::stringstream err;
+ err << "unrecognized FBX transformation node";
+ err << " of type " << type_name << " in node " << node_name;
+ throw DeadlyExportError(err.str());
+ }
+ aiVector3D t, r, s;
+ node->mTransformation.Decompose(s, r, t);
+ switch (elem->second.second) {
+ case 'i': // inverse
+ // we don't need to worry about the inverse matrices
+ break;
+ case 't': // translation
+ transform_chain.emplace_back(elem->first, t);
+ break;
+ case 'r': // rotation
+ r *= float(DEG);
+ transform_chain.emplace_back(elem->first, r);
+ break;
+ case 's': // scale
+ transform_chain.emplace_back(elem->first, s);
+ break;
+ default:
+ // this should never happen
+ std::stringstream err;
+ err << "unrecognized FBX transformation type code: ";
+ err << elem->second.second;
+ throw DeadlyExportError(err.str());
+ }
+ // now continue on to any child nodes
+ for (unsigned i = 0; i < node->mNumChildren; ++i) {
+ WriteModelNodes(
+ outstream,
+ node->mChildren[i],
+ parent_uid,
+ limbnodes,
+ transform_chain
+ );
+ }
+ return;
+ }
+
+ int64_t node_uid = 0;
+ // generate uid and connect to parent, if not the root node,
+ if (node != mScene->mRootNode) {
+ auto elem = node_uids.find(node);
+ if (elem != node_uids.end()) {
+ node_uid = elem->second;
+ } else {
+ node_uid = generate_uid();
+ node_uids[node] = node_uid;
+ }
+ connections.emplace_back("C", "OO", node_uid, parent_uid);
+ }
+
+ // what type of node is this?
+ if (node == mScene->mRootNode) {
+ // handled later
+ } else if (node->mNumMeshes == 1) {
+ // connect to child mesh, which should have been written previously
+ connections.emplace_back(
+ "C", "OO", mesh_uids[node->mMeshes[0]], node_uid
+ );
+ // also connect to the material for the child mesh
+ connections.emplace_back(
+ "C", "OO",
+ material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex],
+ node_uid
+ );
+ // write model node
+ WriteModelNode(
+ outstream, binary, node, node_uid, "Mesh", transform_chain
+ );
+ } else if (limbnodes.count(node)) {
+ WriteModelNode(
+ outstream, binary, node, node_uid, "LimbNode", transform_chain
+ );
+ // we also need to write a nodeattribute to mark it as a skeleton
+ int64_t node_attribute_uid = generate_uid();
+ FBX::Node na("NodeAttribute");
+ na.AddProperties(
+ node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
+ );
+ na.AddChild("TypeFlags", Property("Skeleton"));
+ na.Dump(outstream, binary, 1);
+ // and connect them
+ connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
+ } else {
+ // generate a null node so we can add children to it
+ WriteModelNode(
+ outstream, binary, node, node_uid, "Null", transform_chain
+ );
+ }
+
+ // if more than one child mesh, make nodes for each mesh
+ if (node->mNumMeshes > 1 || node == mScene->mRootNode) {
+ for (size_t i = 0; i < node->mNumMeshes; ++i) {
+ // make a new model node
+ int64_t new_node_uid = generate_uid();
+ // connect to parent node
+ connections.emplace_back("C", "OO", new_node_uid, node_uid);
+ // connect to child mesh, which should have been written previously
+ connections.emplace_back(
+ "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid
+ );
+ // also connect to the material for the child mesh
+ connections.emplace_back(
+ "C", "OO",
+ material_uids[
+ mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex
+ ],
+ new_node_uid
+ );
+ // write model node
+ FBX::Node m("Model");
+ // take name from mesh name, if it exists
+ std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str();
+ name += FBX::SEPARATOR + "Model";
+ m.AddProperties(new_node_uid, name, "Mesh");
+ m.AddChild("Version", int32_t(232));
+ FBX::Node p("Properties70");
+ p.AddP70enum("InheritType", 1);
+ m.AddChild(p);
+ m.Dump(outstream, binary, 1);
+ }
+ }
+
+ // now recurse into children
+ for (size_t i = 0; i < node->mNumChildren; ++i) {
+ WriteModelNodes(
+ outstream, node->mChildren[i], node_uid, limbnodes
+ );
+ }
+}
+
+
+void FBXExporter::WriteAnimationCurveNode(
+ StreamWriterLE& outstream,
+ int64_t uid,
+ std::string name, // "T", "R", or "S"
+ aiVector3D default_value,
+ std::string property_name, // "Lcl Translation" etc
+ int64_t layer_uid,
+ int64_t node_uid
+) {
+ FBX::Node n("AnimationCurveNode");
+ n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", "");
+ FBX::Node p("Properties70");
+ p.AddP70numberA("d|X", default_value.x);
+ p.AddP70numberA("d|Y", default_value.y);
+ p.AddP70numberA("d|Z", default_value.z);
+ n.AddChild(p);
+ n.Dump(outstream, binary, 1);
+ // connect to layer
+ this->connections.emplace_back("C", "OO", uid, layer_uid);
+ // connect to bone
+ this->connections.emplace_back("C", "OP", uid, node_uid, property_name);
+}
+
+
+void FBXExporter::WriteAnimationCurve(
+ StreamWriterLE& outstream,
+ double default_value,
+ const std::vector<int64_t>& times,
+ const std::vector<float>& values,
+ int64_t curvenode_uid,
+ const std::string& property_link // "d|X", "d|Y", etc
+) {
+ FBX::Node n("AnimationCurve");
+ int64_t curve_uid = generate_uid();
+ n.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", "");
+ n.AddChild("Default", default_value);
+ n.AddChild("KeyVer", int32_t(4009));
+ n.AddChild("KeyTime", times);
+ n.AddChild("KeyValueFloat", values);
+ // TODO: keyattr flags and data (STUB for now)
+ n.AddChild("KeyAttrFlags", std::vector<int32_t>{0});
+ n.AddChild("KeyAttrDataFloat", std::vector<float>{0,0,0,0});
+ n.AddChild(
+ "KeyAttrRefCount",
+ std::vector<int32_t>{static_cast<int32_t>(times.size())}
+ );
+ n.Dump(outstream, binary, 1);
+ this->connections.emplace_back(
+ "C", "OP", curve_uid, curvenode_uid, property_link
+ );
+}
+
+
+void FBXExporter::WriteConnections ()
+{
+ // we should have completed the connection graph already,
+ // so basically just dump it here
+ if (!binary) {
+ WriteAsciiSectionHeader("Object connections");
+ }
+ // TODO: comments with names in the ascii version
+ FBX::Node conn("Connections");
+ StreamWriterLE outstream(outfile);
+ conn.Begin(outstream, binary, 0);
+ conn.BeginChildren(outstream, binary, 0);
+ for (auto &n : connections) {
+ n.Dump(outstream, binary, 1);
+ }
+ conn.End(outstream, binary, 0, !connections.empty());
+ connections.clear();
+}
+
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
diff --git a/thirdparty/assimp/code/FBXExporter.h b/thirdparty/assimp/code/FBXExporter.h
new file mode 100644
index 0000000000..71fb55c57f
--- /dev/null
+++ b/thirdparty/assimp/code/FBXExporter.h
@@ -0,0 +1,178 @@
+/*
+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 FBXExporter.h
+* Declares the exporter class to write a scene to an fbx file
+*/
+#ifndef AI_FBXEXPORTER_H_INC
+#define AI_FBXEXPORTER_H_INC
+
+#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#include "FBXExportNode.h" // FBX::Node
+#include "FBXCommon.h" // FBX::TransformInheritance
+
+#include <assimp/types.h>
+//#include <assimp/material.h>
+#include <assimp/StreamWriter.h> // StreamWriterLE
+#include <assimp/Exceptional.h> // DeadlyExportError
+
+#include <vector>
+#include <map>
+#include <unordered_set>
+#include <memory> // shared_ptr
+#include <sstream> // stringstream
+
+struct aiScene;
+struct aiNode;
+//struct aiMaterial;
+
+namespace Assimp
+{
+ class IOSystem;
+ class IOStream;
+ class ExportProperties;
+
+ // ---------------------------------------------------------------------
+ /** Helper class to export a given scene to an FBX file. */
+ // ---------------------------------------------------------------------
+ class FBXExporter
+ {
+ public:
+ /// Constructor for a specific scene to export
+ FBXExporter(const aiScene* pScene, const ExportProperties* pProperties);
+
+ // call one of these methods to export
+ void ExportBinary(const char* pFile, IOSystem* pIOSystem);
+ void ExportAscii(const char* pFile, IOSystem* pIOSystem);
+
+ private:
+ bool binary; // whether current export is in binary or ascii format
+ const aiScene* mScene; // the scene to export
+ const ExportProperties* mProperties; // currently unused
+ std::shared_ptr<IOStream> outfile; // file to write to
+
+ std::vector<FBX::Node> connections; // connection storage
+
+ std::vector<int64_t> mesh_uids;
+ std::vector<int64_t> material_uids;
+ std::map<const aiNode*,int64_t> node_uids;
+
+ // this crude unique-ID system is actually fine
+ int64_t last_uid = 999999;
+ int64_t generate_uid() { return ++last_uid; }
+
+ // binary files have a specific header and footer,
+ // in addition to the actual data
+ void WriteBinaryHeader();
+ void WriteBinaryFooter();
+
+ // ascii files have a comment at the top
+ void WriteAsciiHeader();
+
+ // WriteAllNodes does the actual export.
+ // It just calls all the Write<Section> methods below in order.
+ void WriteAllNodes();
+
+ // Methods to write individual sections.
+ // The order here matches the order inside an FBX file.
+ // Each method corresponds to a top-level FBX section,
+ // except WriteHeader which also includes some binary-only sections
+ // and WriteFooter which is binary data only.
+ void WriteHeaderExtension();
+ // WriteFileId(); // binary-only, included in WriteHeader
+ // WriteCreationTime(); // binary-only, included in WriteHeader
+ // WriteCreator(); // binary-only, included in WriteHeader
+ void WriteGlobalSettings();
+ void WriteDocuments();
+ void WriteReferences();
+ void WriteDefinitions();
+ void WriteObjects();
+ void WriteConnections();
+ // WriteTakes(); // deprecated since at least 2015 (fbx 7.4)
+
+ // helpers
+ void WriteAsciiSectionHeader(const std::string& title);
+ void WriteModelNodes(
+ Assimp::StreamWriterLE& s,
+ const aiNode* node,
+ int64_t parent_uid,
+ const std::unordered_set<const aiNode*>& limbnodes
+ );
+ void WriteModelNodes( // usually don't call this directly
+ StreamWriterLE& s,
+ const aiNode* node,
+ int64_t parent_uid,
+ const std::unordered_set<const aiNode*>& limbnodes,
+ std::vector<std::pair<std::string,aiVector3D>>& transform_chain
+ );
+ void WriteModelNode( // nor this
+ StreamWriterLE& s,
+ bool binary,
+ const aiNode* node,
+ int64_t node_uid,
+ const std::string& type,
+ const std::vector<std::pair<std::string,aiVector3D>>& xfm_chain,
+ FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs
+ );
+ void WriteAnimationCurveNode(
+ StreamWriterLE& outstream,
+ int64_t uid,
+ std::string name, // "T", "R", or "S"
+ aiVector3D default_value,
+ std::string property_name, // "Lcl Translation" etc
+ int64_t animation_layer_uid,
+ int64_t node_uid
+ );
+ void WriteAnimationCurve(
+ StreamWriterLE& outstream,
+ double default_value,
+ const std::vector<int64_t>& times,
+ const std::vector<float>& values,
+ int64_t curvenode_id,
+ const std::string& property_link // "d|X", "d|Y", etc
+ );
+ };
+}
+
+#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
+
+#endif // AI_FBXEXPORTER_H_INC
diff --git a/thirdparty/assimp/code/FBXImportSettings.h b/thirdparty/assimp/code/FBXImportSettings.h
new file mode 100644
index 0000000000..d5e1c20608
--- /dev/null
+++ b/thirdparty/assimp/code/FBXImportSettings.h
@@ -0,0 +1,153 @@
+/*
+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 INCLUDED_AI_FBX_IMPORTSETTINGS_H
+#define INCLUDED_AI_FBX_IMPORTSETTINGS_H
+
+namespace Assimp {
+namespace FBX {
+
+/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */
+struct ImportSettings
+{
+ ImportSettings()
+ : strictMode(true)
+ , readAllLayers(true)
+ , readAllMaterials(false)
+ , readMaterials(true)
+ , readTextures(true)
+ , readCameras(true)
+ , readLights(true)
+ , readAnimations(true)
+ , readWeights(true)
+ , preservePivots(true)
+ , optimizeEmptyAnimationCurves(true)
+ , useLegacyEmbeddedTextureNaming(false)
+ {}
+
+
+ /** 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;
+};
+
+
+} // !FBX
+} // !Assimp
+
+#endif
+
diff --git a/thirdparty/assimp/code/FBXImporter.cpp b/thirdparty/assimp/code/FBXImporter.cpp
new file mode 100644
index 0000000000..2cc8bffc29
--- /dev/null
+++ b/thirdparty/assimp/code/FBXImporter.cpp
@@ -0,0 +1,197 @@
+/*
+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.
+r
+* 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 FBXImporter.cpp
+ * @brief Implementation of the FBX importer.
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXImporter.h"
+
+#include "FBXTokenizer.h"
+#include "FBXParser.h"
+#include "FBXUtil.h"
+#include "FBXDocument.h"
+#include "FBXConverter.h"
+
+#include <assimp/StreamReader.h>
+#include <assimp/MemoryIOWrapper.h>
+#include <assimp/Importer.hpp>
+#include <assimp/importerdesc.h>
+
+namespace Assimp {
+
+template<>
+const char* LogFunctions<FBXImporter>::Prefix() {
+ static auto prefix = "FBX: ";
+ return prefix;
+}
+
+}
+
+using namespace Assimp;
+using namespace Assimp::Formatter;
+using namespace Assimp::FBX;
+
+namespace {
+
+static const aiImporterDesc desc = {
+ "Autodesk FBX Importer",
+ "",
+ "",
+ "",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "fbx"
+};
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by #Importer
+FBXImporter::FBXImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FBXImporter::~FBXImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string& extension = GetExtension(pFile);
+ if (extension == std::string( desc.mFileExtensions ) ) {
+ return true;
+ }
+
+ else if ((!extension.length() || checkSig) && pIOHandler) {
+ // at least ASCII-FBX files usually have a 'FBX' somewhere in their head
+ const char* tokens[] = {"fbx"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// List all extensions handled by this loader
+const aiImporterDesc* FBXImporter::GetInfo () const
+{
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void FBXImporter::SetupProperties(const Importer* pImp)
+{
+ settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
+ settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
+ settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
+ settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
+ settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
+ settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
+ settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
+ settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
+ settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
+ settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
+ settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
+ if (!stream) {
+ ThrowException("Could not open file for reading");
+ }
+
+ // read entire file into memory - no streaming for this, fbx
+ // files can grow large, but the assimp output data structure
+ // then becomes very large, too. Assimp doesn't support
+ // streaming for its output data structures so the net win with
+ // streaming input data would be very low.
+ std::vector<char> contents;
+ contents.resize(stream->FileSize()+1);
+ stream->Read( &*contents.begin(), 1, contents.size()-1 );
+ contents[ contents.size() - 1 ] = 0;
+ const char* const begin = &*contents.begin();
+
+ // broadphase tokenizing pass in which we identify the core
+ // syntax elements of FBX (brackets, commas, key:value mappings)
+ TokenList tokens;
+ try {
+
+ bool is_binary = false;
+ if (!strncmp(begin,"Kaydara FBX Binary",18)) {
+ is_binary = true;
+ TokenizeBinary(tokens,begin,static_cast<unsigned int>(contents.size()));
+ }
+ else {
+ Tokenize(tokens,begin);
+ }
+
+ // use this information to construct a very rudimentary
+ // parse-tree representing the FBX scope structure
+ Parser parser(tokens, is_binary);
+
+ // take the raw parse-tree and convert it to a FBX DOM
+ Document doc(parser,settings);
+
+ // convert the FBX DOM to aiScene
+ ConvertToAssimpScene(pScene,doc);
+
+ std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
+ }
+ catch(std::exception&) {
+ std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
+ throw;
+ }
+}
+
+#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER
diff --git a/thirdparty/assimp/code/FBXImporter.h b/thirdparty/assimp/code/FBXImporter.h
new file mode 100644
index 0000000000..c365b2cddf
--- /dev/null
+++ b/thirdparty/assimp/code/FBXImporter.h
@@ -0,0 +1,100 @@
+/*
+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 FBXImporter.h
+ * @brief Declaration of the FBX main importer class
+ */
+#ifndef INCLUDED_AI_FBX_IMPORTER_H
+#define INCLUDED_AI_FBX_IMPORTER_H
+
+#include <assimp/BaseImporter.h>
+#include <assimp/LogAux.h>
+
+#include "FBXImportSettings.h"
+
+namespace Assimp {
+
+// TinyFormatter.h
+namespace Formatter {
+ template <typename T,typename TR, typename A> class basic_formatter;
+ typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
+}
+
+// -------------------------------------------------------------------------------------------
+/** Load the Autodesk FBX file format.
+
+ See http://en.wikipedia.org/wiki/FBX
+*/
+// -------------------------------------------------------------------------------------------
+class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter>
+{
+public:
+ FBXImporter();
+ virtual ~FBXImporter();
+
+ // --------------------
+ bool CanRead( const std::string& pFile,
+ IOSystem* pIOHandler,
+ bool checkSig
+ ) const;
+
+protected:
+
+ // --------------------
+ const aiImporterDesc* GetInfo () const;
+
+ // --------------------
+ void SetupProperties(const Importer* pImp);
+
+ // --------------------
+ void InternReadFile( const std::string& pFile,
+ aiScene* pScene,
+ IOSystem* pIOHandler
+ );
+
+private:
+ FBX::ImportSettings settings;
+}; // !class FBXImporter
+
+} // end of namespace Assimp
+#endif // !INCLUDED_AI_FBX_IMPORTER_H
+
diff --git a/thirdparty/assimp/code/FBXMaterial.cpp b/thirdparty/assimp/code/FBXMaterial.cpp
new file mode 100644
index 0000000000..f16f134404
--- /dev/null
+++ b/thirdparty/assimp/code/FBXMaterial.cpp
@@ -0,0 +1,381 @@
+/*
+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 FBXMaterial.cpp
+ * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXImportSettings.h"
+#include "FBXDocumentUtil.h"
+#include "FBXProperties.h"
+#include <assimp/ByteSwapper.h>
+
+#include <algorithm> // std::transform
+#include "FBXUtil.h"
+
+namespace Assimp {
+namespace FBX {
+
+ using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Material::Material(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Object(id,element,name)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ const Element* const ShadingModel = sc["ShadingModel"];
+ const Element* const MultiLayer = sc["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;
+
+ // lower-case shading because Blender (for example) writes "Phong"
+ std::transform(shading.begin(), shading.end(), shading.begin(), ::tolower);
+ 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;
+ }
+
+ 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);
+ if(!tex) {
+ const LayeredTexture* const layeredTexture = dynamic_cast<const 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*)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()
+{
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Object(id,element,name)
+, uvScaling(1.0f,1.0f)
+, media(0)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ const Element* const Type = sc["Type"];
+ const Element* const FileName = sc["FileName"];
+ const Element* const RelativeFilename = sc["RelativeFilename"];
+ const Element* const ModelUVTranslation = sc["ModelUVTranslation"];
+ const Element* const ModelUVScaling = sc["ModelUVScaling"];
+ const Element* const Texture_Alpha_Source = sc["Texture_Alpha_Source"];
+ const Element* const Cropping = sc["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 = aiVector2D(ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,0)),
+ ParseTokenAsFloat(GetRequiredToken(*ModelUVTranslation,1))
+ );
+ }
+
+ if(ModelUVScaling) {
+ uvScaling = aiVector2D(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 aiVector3D& scaling = PropertyGet<aiVector3D>(*props, "Scaling", ok);
+ if (ok) {
+ uvScaling.x = scaling.x;
+ uvScaling.y = scaling.y;
+ }
+
+ const aiVector3D& trans = PropertyGet<aiVector3D>(*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()
+{
+
+}
+
+LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name)
+: Object(id,element,name)
+,blendMode(BlendMode_Modulate)
+,alpha(1)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ const Element* const BlendModes = sc["BlendModes"];
+ const Element* const Alphas = sc["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 Element& element, const Document& doc, const std::string& name)
+: Object(id,element,name)
+, contentLength(0)
+, content(0)
+{
+ const Scope& sc = GetRequiredScope(element);
+
+ const Element* const Type = sc["Type"];
+ const Element* const FileName = sc.FindElementCaseInsensitive("FileName"); //some files retain the information as "Filename", others "FileName", who knows
+ const Element* const RelativeFilename = sc["RelativeFilename"];
+ const Element* const Content = sc["Content"];
+
+ if(Type) {
+ type = ParseTokenAsString(GetRequiredToken(*Type,0));
+ }
+
+ if(FileName) {
+ fileName = ParseTokenAsString(GetRequiredToken(*FileName,0));
+ }
+
+ if(RelativeFilename) {
+ relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0));
+ }
+
+ if(Content) {
+ //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 {
+ const char* encodedData = data + 1;
+ size_t encodedDataLen = static_cast<size_t>(token.end() - token.begin());
+ // search for last quotation mark
+ while (encodedDataLen > 1 && encodedData[encodedDataLen] != '"')
+ encodedDataLen--;
+ if (encodedDataLen % 4 != 0) {
+ DOMError("embedded content is invalid, needs to be in base64", &element);
+ }
+ else {
+ contentLength = Util::DecodeBase64(encodedData, encodedDataLen, content);
+ }
+ }
+ }
+ 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 (const runtime_error& runtimeError)
+ {
+ //we don't need the content data for contents that has already been loaded
+ ASSIMP_LOG_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;
+ }
+}
+
+} //!FBX
+} //!Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXMeshGeometry.cpp b/thirdparty/assimp/code/FBXMeshGeometry.cpp
new file mode 100644
index 0000000000..d75476b826
--- /dev/null
+++ b/thirdparty/assimp/code/FBXMeshGeometry.cpp
@@ -0,0 +1,711 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include <functional>
+
+#include "FBXMeshGeometry.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXImportSettings.h"
+#include "FBXDocumentUtil.h"
+
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Geometry::Geometry(uint64_t id, const Element& 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* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
+ if(sk) {
+ skin = sk;
+ }
+ const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
+ if (bsp) {
+ blendShapes.push_back(bsp);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Geometry::~Geometry()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<const BlendShape*>& Geometry::GetBlendShapes() const {
+ return blendShapes;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Skin* Geometry::DeformerSkin() const {
+ return skin;
+}
+
+// ------------------------------------------------------------------------------------------------
+MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+: Geometry(id, element,name, doc)
+{
+ const Scope* sc = element.Compound();
+ if (!sc) {
+ DOMError("failed to read Geometry object (class: Mesh), no data scope found");
+ }
+
+ // must have Mesh elements:
+ const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element);
+ const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element);
+
+ // optional Mesh elements:
+ const ElementCollection& Layer = sc->GetCollection("Layer");
+
+ std::vector<aiVector3D> tempVerts;
+ ParseVectorDataArray(tempVerts,Vertices);
+
+ if(tempVerts.empty()) {
+ FBXImporter::LogWarn("encountered mesh with no vertices");
+ return;
+ }
+
+ std::vector<int> tempFaces;
+ ParseVectorDataArray(tempFaces,PolygonVertexIndex);
+
+ if(tempFaces.empty()) {
+ FBXImporter::LogWarn("encountered mesh with no faces");
+ return;
+ }
+
+ m_vertices.reserve(tempFaces.size());
+ m_faces.reserve(tempFaces.size() / 3);
+
+ m_mapping_offsets.resize(tempVerts.size());
+ m_mapping_counts.resize(tempVerts.size(),0);
+ m_mappings.resize(tempFaces.size());
+
+ const size_t vertex_count = tempVerts.size();
+
+ // generate output vertices, computing an adjacency table to
+ // preserve the mapping from fbx indices to *this* indexing.
+ unsigned int count = 0;
+ for(int index : tempFaces) {
+ const int absi = index < 0 ? (-index - 1) : index;
+ if(static_cast<size_t>(absi) >= vertex_count) {
+ DOMError("polygon vertex index out of range",&PolygonVertexIndex);
+ }
+
+ m_vertices.push_back(tempVerts[absi]);
+ ++count;
+
+ ++m_mapping_counts[absi];
+
+ if (index < 0) {
+ m_faces.push_back(count);
+ count = 0;
+ }
+ }
+
+ unsigned int cursor = 0;
+ for (size_t i = 0, e = tempVerts.size(); i < e; ++i) {
+ m_mapping_offsets[i] = cursor;
+ cursor += m_mapping_counts[i];
+
+ m_mapping_counts[i] = 0;
+ }
+
+ cursor = 0;
+ for(int index : tempFaces) {
+ const int absi = index < 0 ? (-index - 1) : index;
+ m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++;
+ }
+
+ // if settings.readAllLayers is true:
+ // * read all layers, try to load as many vertex channels as possible
+ // if settings.readAllLayers is false:
+ // * read only the layer with index 0, but warn about any further layers
+ for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
+ const TokenList& tokens = (*it).second->Tokens();
+
+ const char* err;
+ const int index = ParseTokenAsInt(*tokens[0], err);
+ if(err) {
+ DOMError(err,&element);
+ }
+
+ if(doc.Settings().readAllLayers || index == 0) {
+ const Scope& layer = GetRequiredScope(*(*it).second);
+ ReadLayer(layer);
+ }
+ else {
+ FBXImporter::LogWarn("ignoring additional geometry layers");
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+MeshGeometry::~MeshGeometry() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector3D>& MeshGeometry::GetVertices() const {
+ return m_vertices;
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector3D>& MeshGeometry::GetNormals() const {
+ return m_normals;
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector3D>& MeshGeometry::GetTangents() const {
+ return m_tangents;
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector3D>& MeshGeometry::GetBinormals() const {
+ return m_binormals;
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<unsigned int>& MeshGeometry::GetFaceIndexCounts() const {
+ return m_faces;
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector2D>& MeshGeometry::GetTextureCoords( unsigned int index ) const {
+ static const std::vector<aiVector2D> empty;
+ return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ];
+}
+
+std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const {
+ return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ];
+}
+
+const std::vector<aiColor4D>& MeshGeometry::GetVertexColors( unsigned int index ) const {
+ static const std::vector<aiColor4D> empty;
+ return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ];
+}
+
+const MatIndexArray& MeshGeometry::GetMaterialIndices() const {
+ return m_materials;
+}
+// ------------------------------------------------------------------------------------------------
+const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const {
+ if ( in_index >= m_mapping_counts.size() ) {
+ return NULL;
+ }
+
+ ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() );
+ count = m_mapping_counts[ in_index ];
+
+ ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() );
+
+ return &m_mappings[ m_mapping_offsets[ in_index ] ];
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const {
+ ai_assert( in_index < m_vertices.size() );
+
+ // in the current conversion pattern this will only be needed if
+ // weights are present, so no need to always pre-compute this table
+ if ( m_facesVertexStartIndices.empty() ) {
+ m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 );
+
+ std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 );
+ m_facesVertexStartIndices.pop_back();
+ }
+
+ ai_assert( m_facesVertexStartIndices.size() == m_faces.size() );
+ const std::vector<unsigned int>::iterator it = std::upper_bound(
+ m_facesVertexStartIndices.begin(),
+ m_facesVertexStartIndices.end(),
+ in_index
+ );
+
+ return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+void MeshGeometry::ReadLayer(const Scope& layer)
+{
+ const ElementCollection& LayerElement = layer.GetCollection("LayerElement");
+ for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
+ const Scope& elayer = GetRequiredScope(*(*eit).second);
+
+ ReadLayerElement(elayer);
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void MeshGeometry::ReadLayerElement(const Scope& layerElement)
+{
+ const Element& Type = GetRequiredElement(layerElement,"Type");
+ const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex");
+
+ const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0));
+ const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0));
+
+ const Scope& top = GetRequiredScope(element);
+ const ElementCollection candidates = top.GetCollection(type);
+
+ for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) {
+ const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0));
+ if(index == typedIndex) {
+ ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second));
+ return;
+ }
+ }
+
+ FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ")
+ << type << ", index: " << typedIndex);
+}
+
+// ------------------------------------------------------------------------------------------------
+void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source)
+{
+ const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken(
+ GetRequiredElement(source,"MappingInformationType"),0)
+ );
+
+ const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
+ GetRequiredElement(source,"ReferenceInformationType"),0)
+ );
+
+ if (type == "LayerElementUV") {
+ if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+ FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ")
+ << index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" );
+ return;
+ }
+
+ const Element* Name = source["Name"];
+ m_uvNames[index] = "";
+ if(Name) {
+ m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
+ }
+
+ ReadVertexDataUV(m_uvs[index],source,
+ MappingInformationType,
+ ReferenceInformationType
+ );
+ }
+ else if (type == "LayerElementMaterial") {
+ if (m_materials.size() > 0) {
+ FBXImporter::LogError("ignoring additional material layer");
+ return;
+ }
+
+ std::vector<int> temp_materials;
+
+ ReadVertexDataMaterials(temp_materials,source,
+ MappingInformationType,
+ ReferenceInformationType
+ );
+
+ // sometimes, there will be only negative entries. Drop the material
+ // layer in such a case (I guess it means a default material should
+ // be used). This is what the converter would do anyway, and it
+ // avoids losing the material if there are more material layers
+ // coming of which at least one contains actual data (did observe
+ // that with one test file).
+ const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),[](int n) { return n < 0; });
+ if(count_neg == temp_materials.size()) {
+ FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)");
+ return;
+ }
+
+ std::swap(temp_materials, m_materials);
+ }
+ else if (type == "LayerElementNormal") {
+ if (m_normals.size() > 0) {
+ FBXImporter::LogError("ignoring additional normal layer");
+ return;
+ }
+
+ ReadVertexDataNormals(m_normals,source,
+ MappingInformationType,
+ ReferenceInformationType
+ );
+ }
+ else if (type == "LayerElementTangent") {
+ if (m_tangents.size() > 0) {
+ FBXImporter::LogError("ignoring additional tangent layer");
+ return;
+ }
+
+ ReadVertexDataTangents(m_tangents,source,
+ MappingInformationType,
+ ReferenceInformationType
+ );
+ }
+ else if (type == "LayerElementBinormal") {
+ if (m_binormals.size() > 0) {
+ FBXImporter::LogError("ignoring additional binormal layer");
+ return;
+ }
+
+ ReadVertexDataBinormals(m_binormals,source,
+ MappingInformationType,
+ ReferenceInformationType
+ );
+ }
+ else if (type == "LayerElementColor") {
+ if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) {
+ FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ")
+ << index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" );
+ return;
+ }
+
+ ReadVertexDataColors(m_colors[index],source,
+ MappingInformationType,
+ ReferenceInformationType
+ );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Lengthy utility function to read and resolve a FBX vertex data array - that is, the
+// output is in polygon vertex order. This logic is used for reading normals, UVs, colors,
+// tangents ..
+template <typename T>
+void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType,
+ const char* dataElementName,
+ const char* indexDataElementName,
+ size_t vertex_count,
+ const std::vector<unsigned int>& mapping_counts,
+ const std::vector<unsigned int>& mapping_offsets,
+ const std::vector<unsigned int>& mappings)
+{
+ bool isDirect = ReferenceInformationType == "Direct";
+ bool isIndexToDirect = ReferenceInformationType == "IndexToDirect";
+
+ // fall-back to direct data if there is no index data element
+ if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) {
+ isDirect = true;
+ isIndexToDirect = false;
+ }
+
+ // handle permutations of Mapping and Reference type - it would be nice to
+ // deal with this more elegantly and with less redundancy, but right
+ // now it seems unavoidable.
+ if (MappingInformationType == "ByVertice" && isDirect) {
+ if (!HasElement(source, dataElementName)) {
+ return;
+ }
+ std::vector<T> tempData;
+ ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
+
+ data_out.resize(vertex_count);
+ for (size_t i = 0, e = tempData.size(); i < e; ++i) {
+
+ const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
+ for (unsigned int j = istart; j < iend; ++j) {
+ data_out[mappings[j]] = tempData[i];
+ }
+ }
+ }
+ else if (MappingInformationType == "ByVertice" && isIndexToDirect) {
+ std::vector<T> tempData;
+ ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
+
+ data_out.resize(vertex_count);
+
+ std::vector<int> uvIndices;
+ ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
+ for (size_t i = 0, e = uvIndices.size(); i < e; ++i) {
+
+ const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
+ for (unsigned int j = istart; j < iend; ++j) {
+ if (static_cast<size_t>(uvIndices[i]) >= tempData.size()) {
+ DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
+ }
+ data_out[mappings[j]] = tempData[uvIndices[i]];
+ }
+ }
+ }
+ else if (MappingInformationType == "ByPolygonVertex" && isDirect) {
+ std::vector<T> tempData;
+ ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
+
+ if (tempData.size() != vertex_count) {
+ FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
+ << tempData.size() << ", expected " << vertex_count
+ );
+ return;
+ }
+
+ data_out.swap(tempData);
+ }
+ else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) {
+ std::vector<T> tempData;
+ ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
+
+ data_out.resize(vertex_count);
+
+ std::vector<int> uvIndices;
+ ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
+
+ if (uvIndices.size() != vertex_count) {
+ FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping");
+ return;
+ }
+
+ const T empty;
+ unsigned int next = 0;
+ for(int i : uvIndices) {
+ if ( -1 == i ) {
+ data_out[ next++ ] = empty;
+ continue;
+ }
+ if (static_cast<size_t>(i) >= tempData.size()) {
+ DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
+ }
+
+ data_out[next++] = tempData[i];
+ }
+ }
+ else {
+ FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ")
+ << MappingInformationType << "," << ReferenceInformationType);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D>& normals_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType)
+{
+ ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType,
+ "Normals",
+ "NormalsIndex",
+ m_vertices.size(),
+ m_mapping_counts,
+ m_mapping_offsets,
+ m_mappings);
+}
+
+// ------------------------------------------------------------------------------------------------
+void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D>& uv_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType)
+{
+ ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType,
+ "UV",
+ "UVIndex",
+ m_vertices.size(),
+ m_mapping_counts,
+ m_mapping_offsets,
+ m_mappings);
+}
+
+// ------------------------------------------------------------------------------------------------
+void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType)
+{
+ ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType,
+ "Colors",
+ "ColorIndex",
+ m_vertices.size(),
+ m_mapping_counts,
+ m_mapping_offsets,
+ m_mappings);
+}
+
+// ------------------------------------------------------------------------------------------------
+static const std::string TangentIndexToken = "TangentIndex";
+static const std::string TangentsIndexToken = "TangentsIndex";
+
+void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType)
+{
+ const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
+ const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken.c_str() : TangentIndexToken.c_str();
+ ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
+ str,
+ strIdx,
+ m_vertices.size(),
+ m_mapping_counts,
+ m_mapping_offsets,
+ m_mappings);
+}
+
+// ------------------------------------------------------------------------------------------------
+static const std::string BinormalIndexToken = "BinormalIndex";
+static const std::string BinormalsIndexToken = "BinormalsIndex";
+
+void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType)
+{
+ const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal";
+ const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str();
+ ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType,
+ str,
+ strIdx,
+ m_vertices.size(),
+ m_mapping_counts,
+ m_mapping_offsets,
+ m_mappings);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType)
+{
+ const size_t face_count = m_faces.size();
+ ai_assert(face_count);
+
+ // materials are handled separately. First of all, they are assigned per-face
+ // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
+ // has a slightly different meaning for materials.
+ ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials"));
+
+ if (MappingInformationType == "AllSame") {
+ // easy - same material for all faces
+ if (materials_out.empty()) {
+ FBXImporter::LogError(Formatter::format("expected material index, ignoring"));
+ return;
+ }
+ else if (materials_out.size() > 1) {
+ FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one"));
+ materials_out.clear();
+ }
+
+ m_materials.assign(m_vertices.size(),materials_out[0]);
+ }
+ else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
+ m_materials.resize(face_count);
+
+ if(materials_out.size() != face_count) {
+ FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
+ << materials_out.size() << ", expected " << face_count
+ );
+ return;
+ }
+ }
+ else {
+ FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ")
+ << MappingInformationType << "," << ReferenceInformationType);
+ }
+}
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+ : Geometry(id, element, name, doc)
+{
+ const Scope* sc = element.Compound();
+ if (!sc) {
+ DOMError("failed to read Geometry object (class: Shape), no data scope found");
+ }
+ const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
+ const Element& Normals = GetRequiredElement(*sc, "Normals", &element);
+ const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element);
+ ParseVectorDataArray(m_indices, Indexes);
+ ParseVectorDataArray(m_vertices, Vertices);
+ ParseVectorDataArray(m_normals, Normals);
+}
+
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::~ShapeGeometry() {
+ // empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector3D>& ShapeGeometry::GetVertices() const {
+ return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector3D>& ShapeGeometry::GetNormals() const {
+ return m_normals;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<unsigned int>& ShapeGeometry::GetIndices() const {
+ return m_indices;
+}
+// ------------------------------------------------------------------------------------------------
+LineGeometry::LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+ : Geometry(id, element, name, doc)
+{
+ const Scope* sc = element.Compound();
+ if (!sc) {
+ DOMError("failed to read Geometry object (class: Line), no data scope found");
+ }
+ const Element& Points = GetRequiredElement(*sc, "Points", &element);
+ const Element& PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element);
+ ParseVectorDataArray(m_vertices, Points);
+ ParseVectorDataArray(m_indices, PointsIndex);
+}
+
+// ------------------------------------------------------------------------------------------------
+LineGeometry::~LineGeometry() {
+ // empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<aiVector3D>& LineGeometry::GetVertices() const {
+ return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<int>& LineGeometry::GetIndices() const {
+ return m_indices;
+}
+} // !FBX
+} // !Assimp
+#endif
+
diff --git a/thirdparty/assimp/code/FBXMeshGeometry.h b/thirdparty/assimp/code/FBXMeshGeometry.h
new file mode 100644
index 0000000000..d6d4512177
--- /dev/null
+++ b/thirdparty/assimp/code/FBXMeshGeometry.h
@@ -0,0 +1,235 @@
+/*
+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 FBXImporter.h
+* @brief Declaration of the FBX main importer class
+*/
+#ifndef INCLUDED_AI_FBX_MESHGEOMETRY_H
+#define INCLUDED_AI_FBX_MESHGEOMETRY_H
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+
+namespace Assimp {
+namespace FBX {
+
+/**
+ * DOM base class for all kinds of FBX geometry
+ */
+class Geometry : public Object
+{
+public:
+ Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
+ virtual ~Geometry();
+
+ /** Get the Skin attached to this geometry or NULL */
+ const Skin* DeformerSkin() const;
+
+ /** Get the BlendShape attached to this geometry or NULL */
+ const std::vector<const BlendShape*>& GetBlendShapes() const;
+
+private:
+ const Skin* skin;
+ std::vector<const BlendShape*> blendShapes;
+
+};
+
+typedef std::vector<int> MatIndexArray;
+
+
+/**
+ * DOM class for FBX geometry of type "Mesh"
+ */
+class MeshGeometry : public Geometry
+{
+public:
+ /** The class constructor */
+ MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc );
+
+ /** The class destructor */
+ virtual ~MeshGeometry();
+
+ /** Get a list of all vertex points, non-unique*/
+ const std::vector<aiVector3D>& GetVertices() const;
+
+ /** Get a list of all vertex normals or an empty array if
+ * no normals are specified. */
+ const std::vector<aiVector3D>& GetNormals() const;
+
+ /** Get a list of all vertex tangents or an empty array
+ * if no tangents are specified */
+ const std::vector<aiVector3D>& GetTangents() const;
+
+ /** Get a list of all vertex bi-normals or an empty array
+ * if no bi-normals are specified */
+ const std::vector<aiVector3D>& GetBinormals() const;
+
+ /** Return list of faces - each entry denotes a face and specifies
+ * how many vertices it has. Vertices are taken from the
+ * vertex data arrays in sequential order. */
+ const std::vector<unsigned int>& GetFaceIndexCounts() const;
+
+ /** Get a UV coordinate slot, returns an empty array if
+ * the requested slot does not exist. */
+ const std::vector<aiVector2D>& GetTextureCoords( unsigned int index ) const;
+
+ /** Get a UV coordinate slot, returns an empty array if
+ * the requested slot does not exist. */
+ std::string GetTextureCoordChannelName( unsigned int index ) const;
+
+ /** Get a vertex color coordinate slot, returns an empty array if
+ * the requested slot does not exist. */
+ const std::vector<aiColor4D>& GetVertexColors( unsigned int index ) const;
+
+ /** Get per-face-vertex material assignments */
+ const MatIndexArray& GetMaterialIndices() const;
+
+ /** Convert from a fbx file vertex index (for example from a #Cluster weight) or NULL
+ * if the vertex index is not valid. */
+ const unsigned int* ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const;
+
+ /** Determine the face to which a particular output vertex index belongs.
+ * This mapping is always unique. */
+ unsigned int FaceForVertexIndex( unsigned int in_index ) const;
+private:
+ void ReadLayer( const Scope& layer );
+ void ReadLayerElement( const Scope& layerElement );
+ void ReadVertexData( const std::string& type, int index, const Scope& source );
+
+ void ReadVertexDataUV( std::vector<aiVector2D>& uv_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType );
+
+ void ReadVertexDataNormals( std::vector<aiVector3D>& normals_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType );
+
+ void ReadVertexDataColors( std::vector<aiColor4D>& colors_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType );
+
+ void ReadVertexDataTangents( std::vector<aiVector3D>& tangents_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType );
+
+ void ReadVertexDataBinormals( std::vector<aiVector3D>& binormals_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType );
+
+ void ReadVertexDataMaterials( MatIndexArray& materials_out, const Scope& source,
+ const std::string& MappingInformationType,
+ const std::string& ReferenceInformationType );
+
+private:
+ // cached data arrays
+ MatIndexArray m_materials;
+ std::vector<aiVector3D> m_vertices;
+ std::vector<unsigned int> m_faces;
+ mutable std::vector<unsigned int> m_facesVertexStartIndices;
+ std::vector<aiVector3D> m_tangents;
+ std::vector<aiVector3D> m_binormals;
+ std::vector<aiVector3D> m_normals;
+
+ std::string m_uvNames[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
+ std::vector<aiVector2D> m_uvs[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
+ std::vector<aiColor4D> m_colors[ AI_MAX_NUMBER_OF_COLOR_SETS ];
+
+ std::vector<unsigned int> m_mapping_counts;
+ std::vector<unsigned int> m_mapping_offsets;
+ std::vector<unsigned int> m_mappings;
+};
+
+/**
+* DOM class for FBX geometry of type "Shape"
+*/
+class ShapeGeometry : public Geometry
+{
+public:
+ /** The class constructor */
+ ShapeGeometry(uint64_t id, const Element& 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<aiVector3D>& GetVertices() const;
+
+ /** Get a list of all vertex normals or an empty array if
+ * no normals are specified. */
+ const std::vector<aiVector3D>& GetNormals() const;
+
+ /** Return list of vertex indices. */
+ const std::vector<unsigned int>& GetIndices() const;
+
+private:
+ std::vector<aiVector3D> m_vertices;
+ std::vector<aiVector3D> 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 Element& 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<aiVector3D>& GetVertices() const;
+
+ /** Return list of vertex indices. */
+ const std::vector<int>& GetIndices() const;
+
+private:
+ std::vector<aiVector3D> m_vertices;
+ std::vector<int> m_indices;
+};
+
+}
+}
+
+#endif // INCLUDED_AI_FBX_MESHGEOMETRY_H
+
diff --git a/thirdparty/assimp/code/FBXModel.cpp b/thirdparty/assimp/code/FBXModel.cpp
new file mode 100644
index 0000000000..589af36ac7
--- /dev/null
+++ b/thirdparty/assimp/code/FBXModel.cpp
@@ -0,0 +1,153 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXMeshGeometry.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXDocumentUtil.h"
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Model::Model(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+ : Object(id,element,name)
+ , shading("Y")
+{
+ const Scope& sc = GetRequiredScope(element);
+ const Element* const Shading = sc["Shading"];
+ const Element* const Culling = sc["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()
+{
+
+}
+
+// ------------------------------------------------------------------------------------------------
+void Model::ResolveLinks(const Element& 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;
+}
+
+
+} //!FBX
+} //!Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXNodeAttribute.cpp b/thirdparty/assimp/code/FBXNodeAttribute.cpp
new file mode 100644
index 0000000000..b72e5637ee
--- /dev/null
+++ b/thirdparty/assimp/code/FBXNodeAttribute.cpp
@@ -0,0 +1,170 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXDocumentUtil.h"
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Object(id,element,name)
+, props()
+{
+ const Scope& 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 Element& element, const Document& doc, const std::string& name)
+ : NodeAttribute(id,element,doc,name)
+{
+ const Scope& sc = GetRequiredScope(element);
+ const Element* const CameraId = sc["CameraId"];
+ const Element* const CameraName = sc["CameraName"];
+ const Element* const CameraIndexName = sc["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 Element& element, const Document& doc, const std::string& name)
+: NodeAttribute(id,element,doc,name)
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Camera::~Camera()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: NodeAttribute(id,element,doc,name)
+{
+ // empty
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Light::~Light()
+{
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Null::Null(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: NodeAttribute(id,element,doc,name)
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Null::~Null()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: NodeAttribute(id,element,doc,name)
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::~LimbNode()
+{
+
+}
+
+}
+}
+
+#endif
diff --git a/thirdparty/assimp/code/FBXParser.cpp b/thirdparty/assimp/code/FBXParser.cpp
new file mode 100644
index 0000000000..b255c47347
--- /dev/null
+++ b/thirdparty/assimp/code/FBXParser.cpp
@@ -0,0 +1,1313 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+#else
+# include "../contrib/zlib/zlib.h"
+#endif
+
+#include "FBXTokenizer.h"
+#include "FBXParser.h"
+#include "FBXUtil.h"
+
+#include <assimp/ParsingUtils.h>
+#include <assimp/fast_atof.h>
+#include <assimp/ByteSwapper.h>
+
+#include <iostream>
+
+using namespace Assimp;
+using namespace Assimp::FBX;
+
+namespace {
+
+ // ------------------------------------------------------------------------------------------------
+ // signal parse error, this is always unrecoverable. Throws DeadlyImportError.
+ AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
+ AI_WONT_RETURN void ParseError(const std::string& message, const Token& token)
+ {
+ throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token));
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ AI_WONT_RETURN void ParseError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX;
+ AI_WONT_RETURN void ParseError(const std::string& message, const Element* element)
+ {
+ if(element) {
+ ParseError(message,element->KeyToken());
+ }
+ throw DeadlyImportError("FBX-Parser " + message);
+ }
+
+
+ // ------------------------------------------------------------------------------------------------
+ void ParseError(const std::string& message, TokenPtr token)
+ {
+ if(token) {
+ ParseError(message, *token);
+ }
+ ParseError(message);
+ }
+
+ // 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 Assimp {
+namespace FBX {
+
+// ------------------------------------------------------------------------------------------------
+Element::Element(const Token& key_token, Parser& parser)
+: key_token(key_token)
+{
+ TokenPtr n = NULL;
+ do {
+ n = parser.AdvanceToNextToken();
+ if(!n) {
+ ParseError("unexpected end of file, expected closing bracket",parser.LastToken());
+ }
+
+ if (n->Type() == TokenType_DATA) {
+ tokens.push_back(n);
+ TokenPtr prev = n;
+ n = parser.AdvanceToNextToken();
+ if(!n) {
+ ParseError("unexpected end of file, expected bracket, comma or key",parser.LastToken());
+ }
+
+ 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) {
+ ParseError("unexpected token; expected bracket, comma or key",n);
+ }
+ }
+
+ if (n->Type() == TokenType_OPEN_BRACKET) {
+ compound.reset(new Scope(parser));
+
+ // current token should be a TOK_CLOSE_BRACKET
+ n = parser.CurrentToken();
+ ai_assert(n);
+
+ if (n->Type() != TokenType_CLOSE_BRACKET) {
+ ParseError("expected closing bracket",n);
+ }
+
+ parser.AdvanceToNextToken();
+ return;
+ }
+ }
+ while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET);
+}
+
+// ------------------------------------------------------------------------------------------------
+Element::~Element()
+{
+ // no need to delete tokens, they are owned by the parser
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::Scope(Parser& parser,bool topLevel)
+{
+ if(!topLevel) {
+ TokenPtr t = parser.CurrentToken();
+ if (t->Type() != TokenType_OPEN_BRACKET) {
+ ParseError("expected open bracket",t);
+ }
+ }
+
+ TokenPtr n = parser.AdvanceToNextToken();
+ if(n == NULL) {
+ ParseError("unexpected end of file");
+ }
+
+ // note: empty scopes are allowed
+ while(n->Type() != TokenType_CLOSE_BRACKET) {
+ if (n->Type() != TokenType_KEY) {
+ ParseError("unexpected token, expected TOK_KEY",n);
+ }
+
+ const std::string& str = n->StringContents();
+ 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 == NULL) {
+ if (topLevel) {
+ return;
+ }
+ ParseError("unexpected end of file",parser.LastToken());
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::~Scope()
+{
+ for(ElementMap::value_type& v : elements) {
+ delete v.second;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::Parser (const TokenList& tokens, bool is_binary)
+: tokens(tokens)
+, last()
+, current()
+, cursor(tokens.begin())
+, is_binary(is_binary)
+{
+ root.reset(new Scope(*this,true));
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::~Parser()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::AdvanceToNextToken()
+{
+ last = current;
+ if (cursor == tokens.end()) {
+ current = NULL;
+ } else {
+ current = *cursor++;
+ }
+ return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::CurrentToken() const
+{
+ return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::LastToken() const
+{
+ return last;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
+{
+ err_out = NULL;
+
+ 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;
+ }
+
+ BE_NCONST uint64_t id = SafeParse<uint64_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);
+
+ const char* out = nullptr;
+ const uint64_t id = strtoul10_64(t.begin(),&out,&length);
+ if (out > t.end()) {
+ err_out = "failed to parse ID (text)";
+ return 0L;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t ParseTokenAsDim(const Token& t, const char*& err_out)
+{
+ // same as ID parsing, except there is a trailing asterisk
+ err_out = NULL;
+
+ 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;
+ }
+
+ BE_NCONST 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;
+ const size_t id = static_cast<size_t>(strtoul10_64(t.begin() + 1,&out,&length));
+ if (out > t.end()) {
+ err_out = "failed to parse ID";
+ return 0;
+ }
+
+ return id;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+float ParseTokenAsFloat(const Token& t, const char*& err_out)
+{
+ err_out = NULL;
+
+ 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 fast_atof(temp);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+int ParseTokenAsInt(const Token& t, const char*& err_out)
+{
+ err_out = NULL;
+
+ if (t.Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0;
+ }
+
+ 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;
+ }
+
+ BE_NCONST int32_t ival = SafeParse<int32_t>(data+1, t.end());
+ AI_SWAP4(ival);
+ return static_cast<int>(ival);
+ }
+
+ ai_assert(static_cast<size_t>(t.end() - t.begin()) > 0);
+
+ const char* out;
+ const int intval = strtol10(t.begin(),&out);
+ if (out != t.end()) {
+ err_out = "failed to parse ID";
+ return 0;
+ }
+
+ return intval;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+int64_t ParseTokenAsInt64(const Token& t, const char*& err_out)
+{
+ err_out = NULL;
+
+ 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;
+ }
+
+ BE_NCONST 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);
+
+ const char* out = nullptr;
+ const int64_t id = strtol10_64(t.begin(), &out, &length);
+ if (out > t.end()) {
+ err_out = "failed to parse Int64 (text)";
+ return 0L;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string ParseTokenAsString(const Token& t, const char*& err_out)
+{
+ err_out = NULL;
+
+ 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 S(tring), unexpected data type (binary)";
+ return "";
+ }
+
+ // read string length
+ BE_NCONST 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 Element& el)
+{
+ if (static_cast<size_t>(end-data) < 5) {
+ ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el);
+ }
+
+ // data type
+ type = *data;
+
+ // read number of elements
+ BE_NCONST 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 Element& /*el*/)
+{
+ BE_NCONST uint32_t encmode = SafeParse<uint32_t>(data, end);
+ AI_SWAP4(encmode);
+ data += 4;
+
+ // next comes the compressed length
+ BE_NCONST 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;
+
+ default:
+ ai_assert(false);
+ };
+
+ 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)) {
+ ParseError("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) {
+ ParseError("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);
+}
+
+} // !anon
+
+
+// ------------------------------------------------------------------------------------------------
+// read an array of float3 tuples
+void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
+{
+ out.resize( 0 );
+
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ ParseError("unexpected empty element",&el);
+ }
+
+ 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) {
+ ParseError("number of floats is not a multiple of three (3) (binary)",&el);
+ }
+
+ if(!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ ParseError("expected float or double array (binary)",&el);
+ }
+
+ 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(aiVector3D(static_cast<float>(d[0]),
+ static_cast<float>(d[1]),
+ static_cast<float>(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(aiVector3D(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 Scope& scope = GetRequiredScope(el);
+ const Element& a = GetRequiredElement(scope,"a",&el);
+
+ if (a.Tokens().size() % 3 != 0) {
+ ParseError("number of floats is not a multiple of three (3)",&el);
+ }
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
+ aiVector3D 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<aiColor4D>& out, const Element& el)
+{
+ out.resize( 0 );
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ ParseError("unexpected empty element",&el);
+ }
+
+ 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) {
+ ParseError("number of floats is not a multiple of four (4) (binary)",&el);
+ }
+
+ if(!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ ParseError("expected float or double array (binary)",&el);
+ }
+
+ 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(aiColor4D(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(aiColor4D(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 Scope& scope = GetRequiredScope(el);
+ const Element& a = GetRequiredElement(scope,"a",&el);
+
+ if (a.Tokens().size() % 4 != 0) {
+ ParseError("number of floats is not a multiple of four (4)",&el);
+ }
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
+ aiColor4D 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<aiVector2D>& out, const Element& el)
+{
+ out.resize( 0 );
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ ParseError("unexpected empty element",&el);
+ }
+
+ 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) {
+ ParseError("number of floats is not a multiple of two (2) (binary)",&el);
+ }
+
+ if(!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ ParseError("expected float or double array (binary)",&el);
+ }
+
+ 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(aiVector2D(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(aiVector2D(f[0],f[1]));
+ }
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(*tok[0]);
+
+ // see notes in ParseVectorDataArray() above
+ out.reserve(dim);
+
+ const Scope& scope = GetRequiredScope(el);
+ const Element& a = GetRequiredElement(scope,"a",&el);
+
+ if (a.Tokens().size() % 2 != 0) {
+ ParseError("number of floats is not a multiple of two (2)",&el);
+ }
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
+ aiVector2D 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 Element& el)
+{
+ out.resize( 0 );
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ ParseError("unexpected empty element",&el);
+ }
+
+ 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') {
+ ParseError("expected int array (binary)",&el);
+ }
+
+ 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) {
+ BE_NCONST 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 Scope& scope = GetRequiredScope(el);
+ const Element& 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 Element& el)
+{
+ out.resize( 0 );
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ ParseError("unexpected empty element",&el);
+ }
+
+ 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') {
+ ParseError("expected float or double array (binary)",&el);
+ }
+
+ 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 Scope& scope = GetRequiredScope(el);
+ const Element& 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 Element& el)
+{
+ out.resize( 0 );
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ ParseError("unexpected empty element",&el);
+ }
+
+ 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') {
+ ParseError("expected (u)int array (binary)",&el);
+ }
+
+ 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) {
+ BE_NCONST int32_t val = *ip;
+ if(val < 0) {
+ ParseError("encountered negative integer index (binary)");
+ }
+
+ AI_SWAP4(val);
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(*tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const Scope& scope = GetRequiredScope(el);
+ const Element& 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) {
+ ParseError("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 Element& el)
+{
+ out.resize( 0 );
+ const TokenList& tok = el.Tokens();
+ if(tok.empty()) {
+ ParseError("unexpected empty element",&el);
+ }
+
+ 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') {
+ ParseError("expected long array (binary)",&el);
+ }
+
+ 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) {
+ BE_NCONST 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 Scope& scope = GetRequiredScope(el);
+ const Element& 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 Element& el)
+{
+ out.resize( 0 );
+ const TokenList& tok = el.Tokens();
+ if (tok.empty()) {
+ ParseError("unexpected empty element", &el);
+ }
+
+ 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') {
+ ParseError("expected long array (binary)", &el);
+ }
+
+ 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) {
+ BE_NCONST 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 Scope& scope = GetRequiredScope(el);
+ const Element& a = GetRequiredElement(scope, "a", &el);
+
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end;) {
+ const int64_t ival = ParseTokenAsInt64(**it++);
+
+ out.push_back(ival);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMatrix4x4 ReadMatrix(const Element& element)
+{
+ std::vector<float> values;
+ ParseVectorDataArray(values,element);
+
+ if(values.size() != 16) {
+ ParseError("expected 16 matrix elements");
+ }
+
+ aiMatrix4x4 result;
+
+
+ result.a1 = values[0];
+ result.a2 = values[1];
+ result.a3 = values[2];
+ result.a4 = values[3];
+
+ result.b1 = values[4];
+ result.b2 = values[5];
+ result.b3 = values[6];
+ result.b4 = values[7];
+
+ result.c1 = values[8];
+ result.c2 = values[9];
+ result.c3 = values[10];
+ result.c4 = values[11];
+
+ result.d1 = values[12];
+ result.d2 = values[13];
+ result.d3 = values[14];
+ result.d4 = values[15];
+
+ result.Transpose();
+ return result;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsString() with ParseError handling
+std::string ParseTokenAsString(const Token& t)
+{
+ const char* err;
+ const std::string& i = ParseTokenAsString(t,err);
+ if(err) {
+ ParseError(err,t);
+ }
+ return i;
+}
+
+bool HasElement( const Scope& sc, const std::string& index ) {
+ const Element* el = sc[ index ];
+ if ( nullptr == el ) {
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract a required element from a scope, abort if the element cannot be found
+const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element /*= NULL*/)
+{
+ const Element* el = sc[index];
+ if(!el) {
+ ParseError("did not find required element \"" + index + "\"",element);
+ }
+ return *el;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// extract required compound scope
+const Scope& GetRequiredScope(const Element& el)
+{
+ const Scope* const s = el.Compound();
+ if(!s) {
+ ParseError("expected compound scope",&el);
+ }
+
+ return *s;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// get token at a particular index
+const Token& GetRequiredToken(const Element& el, unsigned int index)
+{
+ const TokenList& t = el.Tokens();
+ if(index >= t.size()) {
+ ParseError(Formatter::format( "missing token at index " ) << index,&el);
+ }
+
+ return *t[index];
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsID() with ParseError handling
+uint64_t ParseTokenAsID(const Token& t)
+{
+ const char* err;
+ const uint64_t i = ParseTokenAsID(t,err);
+ if(err) {
+ ParseError(err,t);
+ }
+ return i;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsDim() with ParseError handling
+size_t ParseTokenAsDim(const Token& t)
+{
+ const char* err;
+ const size_t i = ParseTokenAsDim(t,err);
+ if(err) {
+ ParseError(err,t);
+ }
+ return i;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsFloat() with ParseError handling
+float ParseTokenAsFloat(const Token& t)
+{
+ const char* err;
+ const float i = ParseTokenAsFloat(t,err);
+ if(err) {
+ ParseError(err,t);
+ }
+ return i;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt() with ParseError handling
+int ParseTokenAsInt(const Token& t)
+{
+ const char* err;
+ const int i = ParseTokenAsInt(t,err);
+ if(err) {
+ ParseError(err,t);
+ }
+ return i;
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt64() with ParseError handling
+int64_t ParseTokenAsInt64(const Token& t)
+{
+ const char* err;
+ const int64_t i = ParseTokenAsInt64(t, err);
+ if (err) {
+ ParseError(err, t);
+ }
+ return i;
+}
+
+} // !FBX
+} // !Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXParser.h b/thirdparty/assimp/code/FBXParser.h
new file mode 100644
index 0000000000..7b0cf72039
--- /dev/null
+++ b/thirdparty/assimp/code/FBXParser.h
@@ -0,0 +1,235 @@
+/*
+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 INCLUDED_AI_FBX_PARSER_H
+#define INCLUDED_AI_FBX_PARSER_H
+
+#include <stdint.h>
+#include <map>
+#include <memory>
+#include <assimp/LogAux.h>
+#include <assimp/fast_atof.h>
+
+#include "FBXCompileConfig.h"
+#include "FBXTokenizer.h"
+
+namespace Assimp {
+namespace FBX {
+
+class Scope;
+class Parser;
+class Element;
+
+// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
+typedef std::vector< Scope* > ScopeList;
+typedef std::fbx_unordered_multimap< std::string, Element* > 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(const Token& key_token, Parser& parser);
+ ~Element();
+
+ const Scope* Compound() const {
+ return compound.get();
+ }
+
+ const Token& KeyToken() const {
+ return key_token;
+ }
+
+ const TokenList& Tokens() const {
+ return tokens;
+ }
+
+private:
+ const Token& key_token;
+ TokenList tokens;
+ std::unique_ptr<Scope> compound;
+};
+
+/** 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();
+
+ const Element* operator[] (const std::string& index) const {
+ ElementMap::const_iterator it = elements.find(index);
+ return it == elements.end() ? NULL : (*it).second;
+ }
+
+ const Element* FindElementCaseInsensitive(const std::string& elementName) const {
+ const char* elementNameCStr = elementName.c_str();
+ for (auto element = elements.begin(); element != elements.end(); ++element)
+ {
+ if (!ASSIMP_strincmp(element->first.c_str(), elementNameCStr, MAXLEN)) {
+ return element->second;
+ }
+ }
+ return NULL;
+ }
+
+ 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 Scope& GetRootScope() const {
+ return *root.get();
+ }
+
+ bool IsBinary() const {
+ return is_binary;
+ }
+
+private:
+ friend class Scope;
+ friend class Element;
+
+ TokenPtr AdvanceToNextToken();
+ TokenPtr LastToken() const;
+ TokenPtr CurrentToken() const;
+
+private:
+ const TokenList& tokens;
+
+ TokenPtr last, current;
+ TokenList::const_iterator cursor;
+ std::unique_ptr<Scope> root;
+
+ const bool is_binary;
+};
+
+
+/* token parsing - this happens when building the DOM out of the parse-tree*/
+uint64_t ParseTokenAsID(const Token& t, const char*& err_out);
+size_t ParseTokenAsDim(const Token& t, const char*& err_out);
+
+float ParseTokenAsFloat(const Token& t, const char*& err_out);
+int ParseTokenAsInt(const Token& t, const char*& err_out);
+int64_t ParseTokenAsInt64(const Token& t, const char*& err_out);
+std::string ParseTokenAsString(const Token& t, const char*& err_out);
+
+/* wrapper around ParseTokenAsXXX() with DOMError handling */
+uint64_t ParseTokenAsID(const Token& t);
+size_t ParseTokenAsDim(const Token& t);
+float ParseTokenAsFloat(const Token& t);
+int ParseTokenAsInt(const Token& t);
+int64_t ParseTokenAsInt64(const Token& t);
+std::string ParseTokenAsString(const Token& t);
+
+/* read data arrays */
+void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el);
+void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el);
+void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el);
+void ParseVectorDataArray(std::vector<int>& out, const Element& el);
+void ParseVectorDataArray(std::vector<float>& out, const Element& el);
+void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el);
+void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& e);
+void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el);
+
+bool HasElement( const Scope& sc, const std::string& index );
+
+// extract a required element from a scope, abort if the element cannot be found
+const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element = NULL);
+
+// extract required compound scope
+const Scope& GetRequiredScope(const Element& el);
+// get token at a particular index
+const Token& GetRequiredToken(const Element& el, unsigned int index);
+
+// read a 4x4 matrix from an array of 16 floats
+aiMatrix4x4 ReadMatrix(const Element& element);
+
+} // ! FBX
+} // ! Assimp
+
+#endif // ! INCLUDED_AI_FBX_PARSER_H
diff --git a/thirdparty/assimp/code/FBXProperties.cpp b/thirdparty/assimp/code/FBXProperties.cpp
new file mode 100644
index 0000000000..8d7036b6a9
--- /dev/null
+++ b/thirdparty/assimp/code/FBXProperties.cpp
@@ -0,0 +1,235 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXTokenizer.h"
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXProperties.h"
+
+namespace Assimp {
+namespace FBX {
+
+ 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.
+Property* ReadTypedProperty(const Element& 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<aiVector3D>(aiVector3D(
+ 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 NULL;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// 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]);
+}
+
+} //! anon
+
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::PropertyTable()
+: templateProps()
+, element()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::PropertyTable(const Element& element, std::shared_ptr<const PropertyTable> templateProps)
+: templateProps(templateProps)
+, element(&element)
+{
+ const Scope& scope = GetRequiredScope(element);
+ 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;
+ }
+
+ lazyProps[name] = v.second;
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::~PropertyTable()
+{
+ for(PropertyMap::value_type& v : props) {
+ delete v.second;
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const Property* 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 NULL;
+ }
+ }
+
+ 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.
+ std::shared_ptr<Property> prop = std::shared_ptr<Property>(ReadTypedProperty(*element.second));
+
+ // Element could not be read. Skip it.
+ if (!prop) continue;
+
+ // Add to result
+ result[element.first] = prop;
+ }
+
+ return result;
+}
+
+} //! FBX
+} //! Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXProperties.h b/thirdparty/assimp/code/FBXProperties.h
new file mode 100644
index 0000000000..58755542fc
--- /dev/null
+++ b/thirdparty/assimp/code/FBXProperties.h
@@ -0,0 +1,185 @@
+/*
+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 INCLUDED_AI_FBX_PROPERTIES_H
+#define INCLUDED_AI_FBX_PROPERTIES_H
+
+#include "FBXCompileConfig.h"
+#include <memory>
+#include <string>
+
+namespace Assimp {
+namespace FBX {
+
+// 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;
+};
+
+
+typedef std::fbx_unordered_map<std::string,std::shared_ptr<Property> > DirectPropertyMap;
+typedef std::fbx_unordered_map<std::string,const Property*> PropertyMap;
+typedef std::fbx_unordered_map<std::string,const Element*> 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 Element& element, std::shared_ptr<const PropertyTable> templateProps);
+ ~PropertyTable();
+
+ const Property* Get(const std::string& name) const;
+
+ // PropertyTable's need not be coupled with FBX elements so this can be NULL
+ const Element* GetElement() const {
+ return element;
+ }
+
+ const PropertyTable* TemplateProps() const {
+ return templateProps.get();
+ }
+
+ DirectPropertyMap GetUnparsedProperties() const;
+
+private:
+ LazyPropertyMap lazyProps;
+ mutable PropertyMap props;
+ const std::shared_ptr<const PropertyTable> templateProps;
+ const Element* const element;
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline
+T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) {
+ const Property* const 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 ) {
+ const Property* 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();
+}
+
+} //! FBX
+} //! Assimp
+
+#endif // INCLUDED_AI_FBX_PROPERTIES_H
diff --git a/thirdparty/assimp/code/FBXTokenizer.cpp b/thirdparty/assimp/code/FBXTokenizer.cpp
new file mode 100644
index 0000000000..252cce3557
--- /dev/null
+++ b/thirdparty/assimp/code/FBXTokenizer.cpp
@@ -0,0 +1,248 @@
+/*
+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
+ */
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+// tab width for logging columns
+#define ASSIMP_FBX_TAB_WIDTH 4
+
+#include <assimp/ParsingUtils.h>
+
+#include "FBXTokenizer.h"
+#include "FBXUtil.h"
+#include <assimp/Exceptional.h>
+
+namespace Assimp {
+namespace FBX {
+
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column)
+ :
+#ifdef DEBUG
+ contents(sbegin, static_cast<size_t>(send-sbegin)),
+#endif
+ sbegin(sbegin)
+ , send(send)
+ , type(type)
+ , line(line)
+ , column(column)
+{
+ ai_assert(sbegin);
+ ai_assert(send);
+
+ // tokens must be of non-zero length
+ ai_assert(static_cast<size_t>(send-sbegin) > 0);
+}
+
+// ------------------------------------------------------------------------------------------------
+Token::~Token()
+{
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX;
+AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column)
+{
+ throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,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 = NULL;
+}
+
+}
+
+// ------------------------------------------------------------------------------------------------
+void Tokenize(TokenList& output_tokens, const char* input)
+{
+ ai_assert(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 = NULL, *token_end = NULL;
+ 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;
+ }
+ }
+}
+
+} // !FBX
+} // !Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXTokenizer.h b/thirdparty/assimp/code/FBXTokenizer.h
new file mode 100644
index 0000000000..2af29743f4
--- /dev/null
+++ b/thirdparty/assimp/code/FBXTokenizer.h
@@ -0,0 +1,187 @@
+/*
+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 INCLUDED_AI_FBX_TOKENIZER_H
+#define INCLUDED_AI_FBX_TOKENIZER_H
+
+#include "FBXCompileConfig.h"
+#include <assimp/ai_assert.h>
+#include <vector>
+#include <string>
+
+namespace Assimp {
+namespace FBX {
+
+/** 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* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column);
+
+ /** construct a binary token */
+ Token(const char* sbegin, const char* send, TokenType type, unsigned int 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;
+ }
+
+ unsigned int Offset() const {
+ ai_assert(IsBinary());
+ return offset;
+ }
+
+ unsigned int Line() const {
+ ai_assert(!IsBinary());
+ return line;
+ }
+
+ unsigned int Column() const {
+ ai_assert(!IsBinary());
+ return column;
+ }
+
+private:
+
+#ifdef DEBUG
+ // full string copy for the sole purpose that it nicely appears
+ // in msvc's debugger window.
+ const std::string contents;
+#endif
+
+
+ const char* const sbegin;
+ const char* const send;
+ const TokenType type;
+
+ union {
+ const unsigned int line;
+ unsigned int offset;
+ };
+ const unsigned int column;
+};
+
+// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
+typedef const 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.
+ * @throw DeadlyImportError 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.
+ * @throw DeadlyImportError if something goes wrong */
+void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length);
+
+
+} // ! FBX
+} // ! Assimp
+
+#endif // ! INCLUDED_AI_FBX_PARSER_H
diff --git a/thirdparty/assimp/code/FBXUtil.cpp b/thirdparty/assimp/code/FBXUtil.cpp
new file mode 100644
index 0000000000..fb483161b2
--- /dev/null
+++ b/thirdparty/assimp/code/FBXUtil.cpp
@@ -0,0 +1,164 @@
+/*
+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 <assimp/TinyFormatter.h>
+#include <string>
+#include <cstring>
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+namespace Assimp {
+namespace FBX {
+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 "";
+}
+
+
+// ------------------------------------------------------------------------------------------------
+std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset)
+{
+ return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) );
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column)
+{
+ return static_cast<std::string>( (Formatter::format() << prefix << " (line " << line << " << col " << column << ") " << text) );
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok)
+{
+ if(tok->IsBinary()) {
+ return static_cast<std::string>( (Formatter::format() << prefix <<
+ " (" << TokenTypeString(tok->Type()) <<
+ ", offset 0x" << std::hex << tok->Offset() << ") " <<
+ text) );
+ }
+
+ return static_cast<std::string>( (Formatter::format() << prefix <<
+ " (" << TokenTypeString(tok->Type()) <<
+ ", line " << tok->Line() <<
+ ", col " << tok->Column() << ") " <<
+ text) );
+}
+
+static const uint8_t base64DecodeTable[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0,
+ 0, 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, 0, 0, 0, 0, 0,
+ 0, 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, 0, 0, 0, 0, 0
+};
+
+uint8_t DecodeBase64(char ch)
+{
+ return base64DecodeTable[size_t(ch)];
+}
+
+size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
+{
+ if (inLength < 4) {
+ out = 0;
+ return 0;
+ }
+
+ const size_t outLength = (inLength * 3) / 4;
+ out = new uint8_t[outLength];
+ memset(out, 0, outLength);
+
+ size_t i = 0;
+ size_t j = 0;
+ for (i = 0; i < inLength - 4; i += 4)
+ {
+ uint8_t b0 = Util::DecodeBase64(in[i]);
+ uint8_t b1 = Util::DecodeBase64(in[i + 1]);
+ uint8_t b2 = Util::DecodeBase64(in[i + 2]);
+ uint8_t b3 = Util::DecodeBase64(in[i + 3]);
+
+ out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
+ out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
+ out[j++] = (uint8_t)((b2 << 6) | b3);
+ }
+ return outLength;
+}
+
+} // !Util
+} // !FBX
+} // !Assimp
+
+#endif
diff --git a/thirdparty/assimp/code/FBXUtil.h b/thirdparty/assimp/code/FBXUtil.h
new file mode 100644
index 0000000000..6890e015ba
--- /dev/null
+++ b/thirdparty/assimp/code/FBXUtil.h
@@ -0,0 +1,120 @@
+/*
+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.h
+ * @brief FBX utility functions for internal use
+ */
+#ifndef INCLUDED_AI_FBX_UTIL_H
+#define INCLUDED_AI_FBX_UTIL_H
+
+#include "FBXCompileConfig.h"
+#include "FBXTokenizer.h"
+#include <stdint.h>
+
+namespace Assimp {
+namespace FBX {
+
+
+namespace Util {
+
+
+/** 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;
+ }
+};
+
+/** 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, unsigned int 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);
+
+/** Decode a single Base64-encoded character.
+*
+* @param ch Character to decode (from base64 to binary).
+* @return decoded byte value*/
+uint8_t DecodeBase64(char ch);
+
+/** Decode a Base64-encoded string
+*
+* @param in Characters to decode.
+* @param inLength Number of characters to decode.
+* @param out Reference to pointer where we will store the decoded data.
+* @return size of the decoded data (number of bytes)*/
+size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
+
+}
+}
+}
+
+#endif // ! INCLUDED_AI_FBX_UTIL_H
diff --git a/thirdparty/assimp/code/FIReader.cpp b/thirdparty/assimp/code/FIReader.cpp
new file mode 100644
index 0000000000..2116316ca3
--- /dev/null
+++ b/thirdparty/assimp/code/FIReader.cpp
@@ -0,0 +1,1834 @@
+/*
+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 FIReader.cpp
+/// \brief Reader for Fast Infoset encoded binary XML files.
+/// \date 2017
+/// \author Patrick Daehne
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "FIReader.hpp"
+#include <assimp/StringUtils.h>
+
+// Workaround for issue #1361
+// https://github.com/assimp/assimp/issues/1361
+#ifdef __ANDROID__
+# define _GLIBCXX_USE_C99 1
+#endif
+
+#include <assimp/Exceptional.h>
+#include <assimp/IOStream.hpp>
+#include <assimp/types.h>
+#include <assimp/MemoryIOWrapper.h>
+#include <assimp/irrXMLWrapper.h>
+#include "../contrib/utf8cpp/source/utf8.h"
+#include <assimp/fast_atof.h>
+#include <stack>
+#include <map>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+
+namespace Assimp {
+
+static const std::string parseErrorMessage = "Fast Infoset parse error";
+
+static const char *xmlDeclarations[] = {
+ "<?xml encoding='finf'?>",
+ "<?xml encoding='finf' standalone='yes'?>",
+ "<?xml encoding='finf' standalone='no'?>",
+ "<?xml version='1.0' encoding='finf'?>",
+ "<?xml version='1.0' encoding='finf' standalone='yes'?>",
+ "<?xml version='1.0' encoding='finf' standalone='no'?>",
+ "<?xml version='1.1' encoding='finf'?>",
+ "<?xml version='1.1' encoding='finf' standalone='yes'?>",
+ "<?xml version='1.1' encoding='finf' standalone='no'?>"
+};
+
+static size_t parseMagic(const uint8_t *data, const uint8_t *dataEnd) {
+ if (dataEnd - data < 4) {
+ return 0;
+ }
+ uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ switch (magic) {
+ case 0xe0000001:
+ return 4;
+ case 0x3c3f786d: // "<?xm"
+ {
+ size_t xmlDeclarationsLength = sizeof(xmlDeclarations) / sizeof(xmlDeclarations[0]);
+ for (size_t i = 0; i < xmlDeclarationsLength; ++i) {
+ auto xmlDeclaration = xmlDeclarations[i];
+ ptrdiff_t xmlDeclarationLength = strlen(xmlDeclaration);
+ if ((dataEnd - data >= xmlDeclarationLength) && (memcmp(xmlDeclaration, data, xmlDeclarationLength) == 0)) {
+ data += xmlDeclarationLength;
+ if (dataEnd - data < 4) {
+ return 0;
+ }
+ magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ return magic == 0xe0000001 ? xmlDeclarationLength + 4 : 0;
+ }
+ }
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static std::string parseUTF8String(const uint8_t *data, size_t len) {
+ return std::string((char*)data, len);
+}
+
+static std::string parseUTF16String(const uint8_t *data, size_t len) {
+ if (len & 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ size_t numShorts = len / 2;
+ std::vector<short> utf16;
+ utf16.reserve(numShorts);
+ for (size_t i = 0; i < numShorts; ++i) {
+ short v = (data[0] << 8) | data[1];
+ utf16.push_back(v);
+ data += 2;
+ }
+ std::string result;
+ utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(result));
+ return result;
+}
+
+struct FIStringValueImpl: public FIStringValue {
+ inline FIStringValueImpl(std::string &&value_) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ { return value; }
+};
+
+std::shared_ptr<FIStringValue> FIStringValue::create(std::string &&value) {
+ return std::make_shared<FIStringValueImpl>(std::move(value));
+}
+
+struct FIHexValueImpl: public FIHexValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIHexValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ os << std::hex << std::uppercase << std::setfill('0');
+ std::for_each(value.begin(), value.end(), [&](uint8_t c) { os << std::setw(2) << static_cast<int>(c); });
+ strValue = os.str();
+ }
+ return strValue;
+ };
+};
+
+std::shared_ptr<FIHexValue> FIHexValue::create(std::vector<uint8_t> &&value) {
+ return std::make_shared<FIHexValueImpl>(std::move(value));
+}
+
+struct FIBase64ValueImpl: public FIBase64Value {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIBase64ValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ uint8_t c1 = 0, c2;
+ int imod3 = 0;
+ std::vector<uint8_t>::size_type valueSize = value.size();
+ for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
+ c2 = value[i];
+ switch (imod3) {
+ case 0:
+ os << basis_64[c2 >> 2];
+ imod3 = 1;
+ break;
+ case 1:
+ os << basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
+ imod3 = 2;
+ break;
+ case 2:
+ os << basis_64[((c1 & 0x0f) << 2) | ((c2 & 0xc0) >> 6)] << basis_64[c2 & 0x3f];
+ imod3 = 0;
+ break;
+ }
+ c1 = c2;
+ }
+ switch (imod3) {
+ case 1:
+ os << basis_64[(c1 & 0x03) << 4] << "==";
+ break;
+ case 2:
+ os << basis_64[(c1 & 0x0f) << 2] << '=';
+ break;
+ }
+ strValue = os.str();
+ }
+ return strValue;
+ };
+ static const char basis_64[];
+};
+
+const char FIBase64ValueImpl::basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+std::shared_ptr<FIBase64Value> FIBase64Value::create(std::vector<uint8_t> &&value) {
+ return std::make_shared<FIBase64ValueImpl>(std::move(value));
+}
+
+struct FIShortValueImpl: public FIShortValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIShortValueImpl(std::vector<int16_t> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ int n = 0;
+ std::for_each(value.begin(), value.end(), [&](int16_t s) { if (++n > 1) os << ' '; os << s; });
+ strValue = os.str();
+ }
+ return strValue;
+ }
+};
+
+std::shared_ptr<FIShortValue> FIShortValue::create(std::vector<int16_t> &&value) {
+ return std::make_shared<FIShortValueImpl>(std::move(value));
+}
+
+struct FIIntValueImpl: public FIIntValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIIntValueImpl(std::vector<int32_t> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ int n = 0;
+ std::for_each(value.begin(), value.end(), [&](int32_t i) { if (++n > 1) os << ' '; os << i; });
+ strValue = os.str();
+ }
+ return strValue;
+ };
+};
+
+std::shared_ptr<FIIntValue> FIIntValue::create(std::vector<int32_t> &&value) {
+ return std::make_shared<FIIntValueImpl>(std::move(value));
+}
+
+struct FILongValueImpl: public FILongValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FILongValueImpl(std::vector<int64_t> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ int n = 0;
+ std::for_each(value.begin(), value.end(), [&](int64_t l) { if (++n > 1) os << ' '; os << l; });
+ strValue = os.str();
+ }
+ return strValue;
+ };
+};
+
+std::shared_ptr<FILongValue> FILongValue::create(std::vector<int64_t> &&value) {
+ return std::make_shared<FILongValueImpl>(std::move(value));
+}
+
+struct FIBoolValueImpl: public FIBoolValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIBoolValueImpl(std::vector<bool> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ os << std::boolalpha;
+ int n = 0;
+ std::for_each(value.begin(), value.end(), [&](bool b) { if (++n > 1) os << ' '; os << b; });
+ strValue = os.str();
+ }
+ return strValue;
+ };
+};
+
+std::shared_ptr<FIBoolValue> FIBoolValue::create(std::vector<bool> &&value) {
+ return std::make_shared<FIBoolValueImpl>(std::move(value));
+}
+
+struct FIFloatValueImpl: public FIFloatValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIFloatValueImpl(std::vector<float> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ int n = 0;
+ std::for_each(value.begin(), value.end(), [&](float f) { if (++n > 1) os << ' '; os << f; });
+ strValue = os.str();
+ }
+ return strValue;
+ }
+};
+
+std::shared_ptr<FIFloatValue> FIFloatValue::create(std::vector<float> &&value) {
+ return std::make_shared<FIFloatValueImpl>(std::move(value));
+}
+
+struct FIDoubleValueImpl: public FIDoubleValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIDoubleValueImpl(std::vector<double> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ int n = 0;
+ std::for_each(value.begin(), value.end(), [&](double d) { if (++n > 1) os << ' '; os << d; });
+ strValue = os.str();
+ }
+ return strValue;
+ }
+};
+
+std::shared_ptr<FIDoubleValue> FIDoubleValue::create(std::vector<double> &&value) {
+ return std::make_shared<FIDoubleValueImpl>(std::move(value));
+}
+
+struct FIUUIDValueImpl: public FIUUIDValue {
+ mutable std::string strValue;
+ mutable bool strValueValid;
+ inline FIUUIDValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ {
+ if (!strValueValid) {
+ strValueValid = true;
+ std::ostringstream os;
+ os << std::hex << std::uppercase << std::setfill('0');
+ std::vector<uint8_t>::size_type valueSize = value.size();
+ for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
+ switch (i & 15) {
+ case 0:
+ if (i > 0) {
+ os << ' ';
+ }
+ os << std::setw(2) << static_cast<int>(value[i]);
+ break;
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ os << '-';
+ // intentionally fall through!
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ case 7:
+ case 9:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ os << std::setw(2) << static_cast<int>(value[i]);
+ break;
+ }
+ }
+ strValue = os.str();
+ }
+ return strValue;
+ };
+};
+
+std::shared_ptr<FIUUIDValue> FIUUIDValue::create(std::vector<uint8_t> &&value) {
+ return std::make_shared<FIUUIDValueImpl>(std::move(value));
+}
+
+struct FICDATAValueImpl: public FICDATAValue {
+ inline FICDATAValueImpl(std::string &&value_) { value = std::move(value_); }
+ virtual const std::string &toString() const /*override*/ { return value; }
+};
+
+std::shared_ptr<FICDATAValue> FICDATAValue::create(std::string &&value) {
+ return std::make_shared<FICDATAValueImpl>(std::move(value));
+}
+
+struct FIHexDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ return FIHexValue::create(std::vector<uint8_t>(data, data + len));
+ }
+};
+
+struct FIBase64Decoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ return FIBase64Value::create(std::vector<uint8_t>(data, data + len));
+ }
+};
+
+struct FIShortDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ if (len & 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::vector<int16_t> value;
+ size_t numShorts = len / 2;
+ value.reserve(numShorts);
+ for (size_t i = 0; i < numShorts; ++i) {
+ int16_t v = (data[0] << 8) | data[1];
+ value.push_back(v);
+ data += 2;
+ }
+ return FIShortValue::create(std::move(value));
+ }
+};
+
+struct FIIntDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ if (len & 3) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::vector<int32_t> value;
+ size_t numInts = len / 4;
+ value.reserve(numInts);
+ for (size_t i = 0; i < numInts; ++i) {
+ int32_t v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ value.push_back(v);
+ data += 4;
+ }
+ return FIIntValue::create(std::move(value));
+ }
+};
+
+struct FILongDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ if (len & 7) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::vector<int64_t> value;
+ size_t numLongs = len / 8;
+ value.reserve(numLongs);
+ for (size_t i = 0; i < numLongs; ++i) {
+ int64_t b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7];
+ int64_t v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
+ value.push_back(v);
+ data += 8;
+ }
+ return FILongValue::create(std::move(value));
+ }
+};
+
+struct FIBoolDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ if (len < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::vector<bool> value;
+ uint8_t b = *data++;
+ size_t unusedBits = b >> 4;
+ size_t numBools = (len * 8) - 4 - unusedBits;
+ value.reserve(numBools);
+ uint8_t mask = 1 << 3;
+ for (size_t i = 0; i < numBools; ++i) {
+ if (!mask) {
+ mask = 1 << 7;
+ b = *data++;
+ }
+ value.push_back((b & mask) != 0);
+ }
+ return FIBoolValue::create(std::move(value));
+ }
+};
+
+struct FIFloatDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ if (len & 3) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::vector<float> value;
+ size_t numFloats = len / 4;
+ value.reserve(numFloats);
+ for (size_t i = 0; i < numFloats; ++i) {
+ int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ float f;
+ memcpy(&f, &v, 4);
+ value.push_back(f);
+ data += 4;
+ }
+ return FIFloatValue::create(std::move(value));
+ }
+};
+
+struct FIDoubleDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ if (len & 7) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::vector<double> value;
+ size_t numDoubles = len / 8;
+ value.reserve(numDoubles);
+ for (size_t i = 0; i < numDoubles; ++i) {
+ long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7];
+ long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
+ double f;
+ memcpy(&f, &v, 8);
+ value.push_back(f);
+ data += 8;
+ }
+ return FIDoubleValue::create(std::move(value));
+ }
+};
+
+struct FIUUIDDecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ if (len & 15) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ return FIUUIDValue::create(std::vector<uint8_t>(data, data + len));
+ }
+};
+
+struct FICDATADecoder: public FIDecoder {
+ virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
+ return FICDATAValue::create(parseUTF8String(data, len));
+ }
+};
+
+class CFIReaderImpl: public FIReader {
+public:
+
+ CFIReaderImpl(std::unique_ptr<uint8_t[]> data_, size_t size):
+ data(std::move(data_)), dataP(data.get()), dataEnd(data.get() + size), currentNodeType(irr::io::EXN_NONE),
+ emptyElement(false), headerPending(true), terminatorPending(false)
+ {}
+
+ virtual ~CFIReaderImpl() {}
+
+ virtual bool read() /*override*/ {
+ if (headerPending) {
+ headerPending = false;
+ parseHeader();
+ }
+ if (terminatorPending) {
+ terminatorPending = false;
+ if (elementStack.empty()) {
+ return false;
+ }
+ else {
+ nodeName = elementStack.top();
+ elementStack.pop();
+ currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
+ return true;
+ }
+ }
+ if (dataP >= dataEnd) {
+ return false;
+ }
+ uint8_t b = *dataP;
+ if (b < 0x80) { // Element (C.2.11.2, C.3.7.2)
+ // C.3
+ parseElement();
+ return true;
+ }
+ else if (b < 0xc0) { // Characters (C.3.7.5)
+ // C.7
+ auto chars = parseNonIdentifyingStringOrIndex3(vocabulary.charactersTable);
+ nodeName = chars->toString();
+ currentNodeType = irr::io::EXN_TEXT;
+ return true;
+ }
+ else if (b < 0xe0) {
+ if ((b & 0xfc) == 0xc4) { // DTD (C.2.11.5)
+ // C.9
+ ++dataP;
+ if (b & 0x02) {
+ /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ }
+ if (b & 0x01) {
+ /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ }
+ elementStack.push(EmptyString);
+ currentNodeType = irr::io::EXN_UNKNOWN;
+ return true;
+ }
+ else if ((b & 0xfc) == 0xc8) { // Unexpanded entity reference (C.3.7.4)
+ // C.6
+ ++dataP;
+ /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
+ if (b & 0x02) {
+ /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ }
+ if (b & 0x01) {
+ /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ }
+ currentNodeType = irr::io::EXN_UNKNOWN;
+ return true;
+ }
+ }
+ else if (b < 0xf0) {
+ if (b == 0xe1) { // Processing instruction (C.2.11.3, C.3.7.3)
+ // C.5
+ ++dataP;
+ /*const std::string &target =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ /*std::shared_ptr<const FIValue> data =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
+ currentNodeType = irr::io::EXN_UNKNOWN;
+ return true;
+ }
+ else if (b == 0xe2) { // Comment (C.2.11.4, C.3.7.6)
+ // C.8
+ ++dataP;
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::shared_ptr<const FIValue> comment = parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
+ nodeName = comment->toString();
+ currentNodeType = irr::io::EXN_COMMENT;
+ return true;
+ }
+ }
+ else { // Terminator (C.2.12, C.3.8)
+ ++dataP;
+ if (b == 0xff) {
+ terminatorPending = true;
+ }
+ if (elementStack.empty()) {
+ return false;
+ }
+ else {
+ nodeName = elementStack.top();
+ elementStack.pop();
+ currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
+ return true;
+ }
+ }
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
+ return currentNodeType;
+ }
+
+ virtual int getAttributeCount() const /*override*/ {
+ return static_cast<int>(attributes.size());
+ }
+
+ virtual const char* getAttributeName(int idx) const /*override*/ {
+ if (idx < 0 || idx >= (int)attributes.size()) {
+ return nullptr;
+ }
+ return attributes[idx].name.c_str();
+ }
+
+ virtual const char* getAttributeValue(int idx) const /*override*/ {
+ if (idx < 0 || idx >= (int)attributes.size()) {
+ return nullptr;
+ }
+ return attributes[idx].value->toString().c_str();
+ }
+
+ virtual const char* getAttributeValue(const char* name) const /*override*/ {
+ const Attribute* attr = getAttributeByName(name);
+ if (!attr) {
+ return nullptr;
+ }
+ return attr->value->toString().c_str();
+ }
+
+ virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
+ const Attribute* attr = getAttributeByName(name);
+ if (!attr) {
+ return EmptyString.c_str();
+ }
+ return attr->value->toString().c_str();
+ }
+
+ virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
+ const Attribute* attr = getAttributeByName(name);
+ if (!attr) {
+ return 0;
+ }
+ std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attr->value);
+ if (intValue) {
+ return intValue->value.size() == 1 ? intValue->value.front() : 0;
+ }
+ return atoi(attr->value->toString().c_str());
+ }
+
+ virtual int getAttributeValueAsInt(int idx) const /*override*/ {
+ if (idx < 0 || idx >= (int)attributes.size()) {
+ return 0;
+ }
+ std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attributes[idx].value);
+ if (intValue) {
+ return intValue->value.size() == 1 ? intValue->value.front() : 0;
+ }
+ return atoi(attributes[idx].value->toString().c_str());
+ }
+
+ virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
+ const Attribute* attr = getAttributeByName(name);
+ if (!attr) {
+ return 0;
+ }
+ std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attr->value);
+ if (floatValue) {
+ return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
+ }
+
+ return fast_atof(attr->value->toString().c_str());
+ }
+
+ virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
+ if (idx < 0 || idx >= (int)attributes.size()) {
+ return 0;
+ }
+ std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attributes[idx].value);
+ if (floatValue) {
+ return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
+ }
+ return fast_atof(attributes[idx].value->toString().c_str());
+ }
+
+ virtual const char* getNodeName() const /*override*/ {
+ return nodeName.c_str();
+ }
+
+ virtual const char* getNodeData() const /*override*/ {
+ return nodeName.c_str();
+ }
+
+ virtual bool isEmptyElement() const /*override*/ {
+ return emptyElement;
+ }
+
+ virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
+ return irr::io::ETF_UTF8;
+ }
+
+ virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
+ return irr::io::ETF_UTF8;
+ }
+
+ virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const /*override*/ {
+ if (idx < 0 || idx >= (int)attributes.size()) {
+ return nullptr;
+ }
+ return attributes[idx].value;
+ }
+
+ virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* name) const /*override*/ {
+ const Attribute* attr = getAttributeByName(name);
+ if (!attr) {
+ return nullptr;
+ }
+ return attr->value;
+ }
+
+ virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) /*override*/ {
+ decoderMap[algorithmUri] = std::move(decoder);
+ }
+
+ virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ {
+ vocabularyMap[vocabularyUri] = vocabulary;
+ }
+
+private:
+
+ struct QName {
+ std::string prefix;
+ std::string uri;
+ std::string name;
+ inline QName() {}
+ inline QName(const FIQName &qname): prefix(qname.prefix ? qname.prefix : ""), uri(qname.uri ? qname.uri : ""), name(qname.name) {}
+ };
+
+ struct Attribute {
+ QName qname;
+ std::string name;
+ std::shared_ptr<const FIValue> value;
+ };
+
+ struct Vocabulary {
+ std::vector<std::string> restrictedAlphabetTable;
+ std::vector<std::string> encodingAlgorithmTable;
+ std::vector<std::string> prefixTable;
+ std::vector<std::string> namespaceNameTable;
+ std::vector<std::string> localNameTable;
+ std::vector<std::string> otherNCNameTable;
+ std::vector<std::string> otherURITable;
+ std::vector<std::shared_ptr<const FIValue>> attributeValueTable;
+ std::vector<std::shared_ptr<const FIValue>> charactersTable;
+ std::vector<std::shared_ptr<const FIValue>> otherStringTable;
+ std::vector<QName> elementNameTable;
+ std::vector<QName> attributeNameTable;
+ Vocabulary() {
+ prefixTable.push_back("xml");
+ namespaceNameTable.push_back("http://www.w3.org/XML/1998/namespace");
+ }
+ };
+
+ const Attribute* getAttributeByName(const char* name) const {
+ if (!name) {
+ return 0;
+ }
+ std::string n = name;
+ for (int i=0; i<(int)attributes.size(); ++i) {
+ if (attributes[i].name == n) {
+ return &attributes[i];
+ }
+ }
+ return 0;
+ }
+
+ size_t parseInt2() { // C.25
+ uint8_t b = *dataP++;
+ if (!(b & 0x40)) { // x0...... (C.25.2)
+ return b & 0x3f;
+ }
+ else if ((b & 0x60) == 0x40) { // x10..... ........ (C.25.3)
+ if (dataEnd - dataP > 0) {
+ return (((b & 0x1f) << 8) | *dataP++) + 0x40;
+ }
+ }
+ else if ((b & 0x70) == 0x60) { // x110.... ........ ........ (C.25.4)
+ if (dataEnd - dataP > 1) {
+ size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x2040;
+ dataP += 2;
+ return result;
+ }
+ }
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ size_t parseInt3() { // C.27
+ uint8_t b = *dataP++;
+ if (!(b & 0x20)) { // xx0..... (C.27.2)
+ return b & 0x1f;
+ }
+ else if ((b & 0x38) == 0x20) { // xx100... ........ (C.27.3)
+ if (dataEnd - dataP > 0) {
+ return (((b & 0x07) << 8) | *dataP++) + 0x20;
+ }
+ }
+ else if ((b & 0x38) == 0x28) { // xx101... ........ ........ (C.27.4)
+ if (dataEnd - dataP > 1) {
+ size_t result = (((b & 0x07) << 16) | (dataP[0] << 8) | dataP[1]) + 0x820;
+ dataP += 2;
+ return result;
+ }
+ }
+ else if ((b & 0x3f) == 0x30) { // xx110000 0000.... ........ ........ (C.27.5)
+ if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
+ size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x80820;
+ dataP += 3;
+ return result;
+ }
+ }
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ size_t parseInt4() { // C.28
+ uint8_t b = *dataP++;
+ if (!(b & 0x10)) { // xxx0.... (C.28.2)
+ return b & 0x0f;
+ }
+ else if ((b & 0x1c) == 0x10) { // xxx100.. ........ (C.28.3)
+ if (dataEnd - dataP > 0) {
+ return (((b & 0x03) << 8) | *dataP++) + 0x10;
+ }
+ }
+ else if ((b & 0x1c) == 0x14) { // xxx101.. ........ ........ (C.28.4)
+ if (dataEnd - dataP > 1) {
+ size_t result = (((b & 0x03) << 16) | (dataP[0] << 8) | dataP[1]) + 0x410;
+ dataP += 2;
+ return result;
+ }
+ }
+ else if ((b & 0x1f) == 0x18) { // xxx11000 0000.... ........ ........ (C.28.5)
+ if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
+ size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x40410;
+ dataP += 3;
+ return result;
+ }
+ }
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ size_t parseSequenceLen() { // C.21
+ if (dataEnd - dataP > 0) {
+ uint8_t b = *dataP++;
+ if (b < 0x80) { // 0....... (C.21.2)
+ return b;
+ }
+ else if ((b & 0xf0) == 0x80) { // 1000.... ........ ........ (C.21.3)
+ if (dataEnd - dataP > 1) {
+ size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x80;
+ dataP += 2;
+ return result;
+ }
+ }
+ }
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ std::string parseNonEmptyOctetString2() { // C.22
+ // Parse the length of the string
+ uint8_t b = *dataP++ & 0x7f;
+ size_t len;
+ if (!(b & 0x40)) { // x0...... (C.22.3.1)
+ len = b + 1;
+ }
+ else if (b == 0x40) { // x1000000 ........ (C.22.3.2)
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ len = *dataP++ + 0x41;
+ }
+ else if (b == 0x60) { // x1100000 ........ ........ ........ ........ (C.22.3.3)
+ if (dataEnd - dataP < 4) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ len = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x141;
+ dataP += 4;
+ }
+ else {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ // Parse the string (C.22.4)
+ if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::string s = parseUTF8String(dataP, len);
+ dataP += len;
+
+ return s;
+ }
+
+ size_t parseNonEmptyOctetString5Length() { // C.23
+ // Parse the length of the string
+ size_t b = *dataP++ & 0x0f;
+ if (!(b & 0x08)) { // xxxx0... (C.23.3.1)
+ return b + 1;
+ }
+ else if (b == 0x08) { // xxxx1000 ........ (C.23.3.2)
+ if (dataEnd - dataP > 0) {
+ return *dataP++ + 0x09;
+ }
+ }
+ else if (b == 0x0c) { // xxxx1100 ........ ........ ........ ........ (C.23.3.3)
+ if (dataEnd - dataP > 3) {
+ size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x109;
+ dataP += 4;
+ return result;
+ }
+ }
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ size_t parseNonEmptyOctetString7Length() { // C.24
+ // Parse the length of the string
+ size_t b = *dataP++ & 0x03;
+ if (!(b & 0x02)) { // xxxxxx0. (C.24.3.1)
+ return b + 1;
+ }
+ else if (b == 0x02) { // xxxxxx10 ........ (C.24.3.2)
+ if (dataEnd - dataP > 0) {
+ return *dataP++ + 0x3;
+ }
+ }
+ else if (b == 0x03) { // xxxxxx11 ........ ........ ........ ........ (C.24.3.3)
+ if (dataEnd - dataP > 3) {
+ size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x103;
+ dataP += 4;
+ return result;
+ }
+ }
+ throw DeadlyImportError(parseErrorMessage);
+ }
+
+ std::shared_ptr<const FIValue> parseEncodedData(size_t index, size_t len) {
+ if (index < 32) {
+ FIDecoder *decoder = defaultDecoder[index];
+ if (!decoder) {
+ throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
+ }
+ return decoder->decode(dataP, len);
+ }
+ else {
+ if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) {
+ throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
+ }
+ std::string uri = vocabulary.encodingAlgorithmTable[index - 32];
+ auto it = decoderMap.find(uri);
+ if (it == decoderMap.end()) {
+ throw DeadlyImportError("Unsupported encoding algorithm " + uri);
+ }
+ else {
+ return it->second->decode(dataP, len);
+ }
+ }
+ }
+
+ std::shared_ptr<const FIValue> parseRestrictedAlphabet(size_t index, size_t len) {
+ std::string alphabet;
+ if (index < 16) {
+ switch (index) {
+ case 0: // numeric
+ alphabet = "0123456789-+.e ";
+ break;
+ case 1: // date and time
+ alphabet = "0123456789-:TZ ";
+ break;
+ default:
+ throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
+ }
+ }
+ else {
+ if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) {
+ throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
+ }
+ alphabet = vocabulary.restrictedAlphabetTable[index - 16];
+ }
+ std::vector<uint32_t> alphabetUTF32;
+ utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32));
+ std::string::size_type alphabetLength = alphabetUTF32.size();
+ if (alphabetLength < 2) {
+ throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength));
+ }
+ std::string::size_type bitsPerCharacter = 1;
+ while ((1ull << bitsPerCharacter) <= alphabetLength) {
+ ++bitsPerCharacter;
+ }
+ size_t bitsAvail = 0;
+ uint8_t mask = (1 << bitsPerCharacter) - 1;
+ uint32_t bits = 0;
+ std::string s;
+ for (size_t i = 0; i < len; ++i) {
+ bits = (bits << 8) | dataP[i];
+ bitsAvail += 8;
+ while (bitsAvail >= bitsPerCharacter) {
+ bitsAvail -= bitsPerCharacter;
+ size_t charIndex = (bits >> bitsAvail) & mask;
+ if (charIndex < alphabetLength) {
+ s.push_back(alphabetUTF32[charIndex]);
+ }
+ else if (charIndex != mask) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ }
+ }
+ return FIStringValue::create(std::move(s));
+ }
+
+ std::shared_ptr<const FIValue> parseEncodedCharacterString3() { // C.19
+ std::shared_ptr<const FIValue> result;
+ size_t len;
+ uint8_t b = *dataP;
+ if (b & 0x20) {
+ ++dataP;
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ size_t index = ((b & 0x0f) << 4) | ((*dataP & 0xf0) >> 4); // C.29
+ len = parseNonEmptyOctetString5Length();
+ if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ if (b & 0x10) {
+ // encoding algorithm (C.19.3.4)
+ result = parseEncodedData(index, len);
+ }
+ else {
+ // Restricted alphabet (C.19.3.3)
+ result = parseRestrictedAlphabet(index, len);
+ }
+ }
+ else {
+ len = parseNonEmptyOctetString5Length();
+ if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ if (b & 0x10) {
+ // UTF-16 (C.19.3.2)
+ if (len & 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ result = FIStringValue::create(parseUTF16String(dataP, len));
+ }
+ else {
+ // UTF-8 (C.19.3.1)
+ result = FIStringValue::create(parseUTF8String(dataP, len));
+ }
+ }
+ dataP += len;
+ return result;
+ }
+
+ std::shared_ptr<const FIValue> parseEncodedCharacterString5() { // C.20
+ std::shared_ptr<const FIValue> result;
+ size_t len;
+ uint8_t b = *dataP;
+ if (b & 0x08) {
+ ++dataP;
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ size_t index = ((b & 0x03) << 6) | ((*dataP & 0xfc) >> 2); /* C.29 */
+ len = parseNonEmptyOctetString7Length();
+ if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ if (b & 0x04) {
+ // encoding algorithm (C.20.3.4)
+ result = parseEncodedData(index, len);
+ }
+ else {
+ // Restricted alphabet (C.20.3.3)
+ result = parseRestrictedAlphabet(index, len);
+ }
+ }
+ else {
+ len = parseNonEmptyOctetString7Length();
+ if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ if (b & 0x04) {
+ // UTF-16 (C.20.3.2)
+ if (len & 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ result = FIStringValue::create(parseUTF16String(dataP, len));
+ }
+ else {
+ // UTF-8 (C.20.3.1)
+ result = FIStringValue::create(parseUTF8String(dataP, len));
+ }
+ }
+ dataP += len;
+ return result;
+ }
+
+ const std::string &parseIdentifyingStringOrIndex(std::vector<std::string> &stringTable) { // C.13
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ uint8_t b = *dataP;
+ if (b & 0x80) {
+ // We have an index (C.13.4)
+ size_t index = parseInt2();
+ if (index >= stringTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ return stringTable[index];
+ }
+ else {
+ // We have a string (C.13.3)
+ stringTable.push_back(parseNonEmptyOctetString2());
+ return stringTable.back();
+ }
+ }
+
+ QName parseNameSurrogate() { // C.16
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ uint8_t b = *dataP++;
+ if (b & 0xfc) { // Padding '000000' C.2.5.5
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ QName result;
+ size_t index;
+ if (b & 0x02) { // prefix (C.16.3)
+ if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ index = parseInt2();
+ if (index >= vocabulary.prefixTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ result.prefix = vocabulary.prefixTable[index];
+ }
+ if (b & 0x01) { // namespace-name (C.16.4)
+ if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ index = parseInt2();
+ if (index >= vocabulary.namespaceNameTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ result.uri = vocabulary.namespaceNameTable[index];
+ }
+ // local-name
+ if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ index = parseInt2();
+ if (index >= vocabulary.localNameTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ result.name = vocabulary.localNameTable[index];
+ return result;
+ }
+
+ const QName &parseQualifiedNameOrIndex2(std::vector<QName> &qNameTable) { // C.17
+ uint8_t b = *dataP;
+ if ((b & 0x7c) == 0x78) { // x11110..
+ // We have a literal (C.17.3)
+ ++dataP;
+ QName result;
+ // prefix (C.17.3.1)
+ result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
+ // namespace-name (C.17.3.1)
+ result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
+ // local-name
+ result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
+ qNameTable.push_back(result);
+ return qNameTable.back();
+ }
+ else {
+ // We have an index (C.17.4)
+ size_t index = parseInt2();
+ if (index >= qNameTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ return qNameTable[index];
+ }
+ }
+
+ const QName &parseQualifiedNameOrIndex3(std::vector<QName> &qNameTable) { // C.18
+ uint8_t b = *dataP;
+ if ((b & 0x3c) == 0x3c) { // xx1111..
+ // We have a literal (C.18.3)
+ ++dataP;
+ QName result;
+ // prefix (C.18.3.1)
+ result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
+ // namespace-name (C.18.3.1)
+ result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
+ // local-name
+ result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
+ qNameTable.push_back(result);
+ return qNameTable.back();
+ }
+ else {
+ // We have an index (C.18.4)
+ size_t index = parseInt3();
+ if (index >= qNameTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ return qNameTable[index];
+ }
+ }
+
+ std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex1(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.14
+ uint8_t b = *dataP;
+ if (b == 0xff) { // C.26.2
+ // empty string
+ ++dataP;
+ return EmptyFIString;
+ }
+ else if (b & 0x80) { // C.14.4
+ // We have an index
+ size_t index = parseInt2();
+ if (index >= valueTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ return valueTable[index];
+ }
+ else { // C.14.3
+ // We have a literal
+ std::shared_ptr<const FIValue> result = parseEncodedCharacterString3();
+ if (b & 0x40) { // C.14.3.1
+ valueTable.push_back(result);
+ }
+ return result;
+ }
+ }
+
+ std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex3(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.15
+ uint8_t b = *dataP;
+ if (b & 0x20) { // C.15.4
+ // We have an index
+ size_t index = parseInt4();
+ if (index >= valueTable.size()) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ return valueTable[index];
+ }
+ else { // C.15.3
+ // We have a literal
+ std::shared_ptr<const FIValue> result = parseEncodedCharacterString5();
+ if (b & 0x10) { // C.15.3.1
+ valueTable.push_back(result);
+ }
+ return result;
+ }
+ }
+
+ void parseElement() {
+ // C.3
+
+ attributes.clear();
+
+ uint8_t b = *dataP;
+ bool hasAttributes = (b & 0x40) != 0; // C.3.3
+ if ((b & 0x3f) == 0x38) { // C.3.4.1
+ // Parse namespaces
+ ++dataP;
+ for (;;) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ b = *dataP++;
+ if (b == 0xf0) { // C.3.4.3
+ break;
+ }
+ if ((b & 0xfc) != 0xcc) { // C.3.4.2
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ // C.12
+ Attribute attr;
+ attr.qname.prefix = "xmlns";
+ attr.qname.name = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
+ attr.qname.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
+ attr.name = attr.qname.name.empty() ? "xmlns" : "xmlns:" + attr.qname.name;
+ attr.value = FIStringValue::create(std::string(attr.qname.uri));
+ attributes.push_back(attr);
+ }
+ if ((dataEnd - dataP < 1) || (*dataP & 0xc0)) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ }
+
+ // Parse Element name (C.3.5)
+ const QName &elemName = parseQualifiedNameOrIndex3(vocabulary.elementNameTable);
+ nodeName = elemName.prefix.empty() ? elemName.name : elemName.prefix + ':' + elemName.name;
+
+ if (hasAttributes) {
+ for (;;) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ b = *dataP;
+ if (b < 0x80) { // C.3.6.1
+ // C.4
+ Attribute attr;
+ attr.qname = parseQualifiedNameOrIndex2(vocabulary.attributeNameTable);
+ attr.name = attr.qname.prefix.empty() ? attr.qname.name : attr.qname.prefix + ':' + attr.qname.name;
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ attr.value = parseNonIdentifyingStringOrIndex1(vocabulary.attributeValueTable);
+ attributes.push_back(attr);
+ }
+ else {
+ if ((b & 0xf0) != 0xf0) { // C.3.6.2
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ emptyElement = b == 0xff; // C.3.6.2, C.3.8
+ ++dataP;
+ break;
+ }
+ }
+ }
+ else {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ b = *dataP;
+ switch (b) {
+ case 0xff:
+ terminatorPending = true;
+ // Intentionally fall through
+ case 0xf0:
+ emptyElement = true;
+ ++dataP;
+ break;
+ default:
+ emptyElement = false;
+ }
+ }
+ if (!emptyElement) {
+ elementStack.push(nodeName);
+ }
+
+ currentNodeType = irr::io::EXN_ELEMENT;
+ }
+
+ void parseHeader() {
+ // Parse header (C.1.3)
+ size_t magicSize = parseMagic(dataP, dataEnd);
+ if (!magicSize) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ dataP += magicSize;
+ // C.2.3
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ uint8_t b = *dataP++;
+ if (b & 0x40) {
+ // Parse additional data (C.2.4)
+ size_t len = parseSequenceLen();
+ for (size_t i = 0; i < len; ++i) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ /*std::string id =*/ parseNonEmptyOctetString2();
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ /*std::string data =*/ parseNonEmptyOctetString2();
+ }
+ }
+ if (b & 0x20) {
+ // Parse initial vocabulary (C.2.5)
+ if (dataEnd - dataP < 2) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ uint16_t b1 = (dataP[0] << 8) | dataP[1];
+ dataP += 2;
+ if (b1 & 0x1000) {
+ // External vocabulary (C.2.5.2)
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ std::string uri = parseNonEmptyOctetString2();
+ auto it = vocabularyMap.find(uri);
+ if (it == vocabularyMap.end()) {
+ throw DeadlyImportError("Unknown vocabulary " + uri);
+ }
+ const FIVocabulary *externalVocabulary = it->second;
+ if (externalVocabulary->restrictedAlphabetTable) {
+ std::copy(externalVocabulary->restrictedAlphabetTable, externalVocabulary->restrictedAlphabetTable + externalVocabulary->restrictedAlphabetTableSize, std::back_inserter(vocabulary.restrictedAlphabetTable));
+ }
+ if (externalVocabulary->encodingAlgorithmTable) {
+ std::copy(externalVocabulary->encodingAlgorithmTable, externalVocabulary->encodingAlgorithmTable + externalVocabulary->encodingAlgorithmTableSize, std::back_inserter(vocabulary.encodingAlgorithmTable));
+ }
+ if (externalVocabulary->prefixTable) {
+ std::copy(externalVocabulary->prefixTable, externalVocabulary->prefixTable + externalVocabulary->prefixTableSize, std::back_inserter(vocabulary.prefixTable));
+ }
+ if (externalVocabulary->namespaceNameTable) {
+ std::copy(externalVocabulary->namespaceNameTable, externalVocabulary->namespaceNameTable + externalVocabulary->namespaceNameTableSize, std::back_inserter(vocabulary.namespaceNameTable));
+ }
+ if (externalVocabulary->localNameTable) {
+ std::copy(externalVocabulary->localNameTable, externalVocabulary->localNameTable + externalVocabulary->localNameTableSize, std::back_inserter(vocabulary.localNameTable));
+ }
+ if (externalVocabulary->otherNCNameTable) {
+ std::copy(externalVocabulary->otherNCNameTable, externalVocabulary->otherNCNameTable + externalVocabulary->otherNCNameTableSize, std::back_inserter(vocabulary.otherNCNameTable));
+ }
+ if (externalVocabulary->otherURITable) {
+ std::copy(externalVocabulary->otherURITable, externalVocabulary->otherURITable + externalVocabulary->otherURITableSize, std::back_inserter(vocabulary.otherURITable));
+ }
+ if (externalVocabulary->attributeValueTable) {
+ std::copy(externalVocabulary->attributeValueTable, externalVocabulary->attributeValueTable + externalVocabulary->attributeValueTableSize, std::back_inserter(vocabulary.attributeValueTable));
+ }
+ if (externalVocabulary->charactersTable) {
+ std::copy(externalVocabulary->charactersTable, externalVocabulary->charactersTable + externalVocabulary->charactersTableSize, std::back_inserter(vocabulary.charactersTable));
+ }
+ if (externalVocabulary->otherStringTable) {
+ std::copy(externalVocabulary->otherStringTable, externalVocabulary->otherStringTable + externalVocabulary->otherStringTableSize, std::back_inserter(vocabulary.otherStringTable));
+ }
+ if (externalVocabulary->elementNameTable) {
+ std::copy(externalVocabulary->elementNameTable, externalVocabulary->elementNameTable + externalVocabulary->elementNameTableSize, std::back_inserter(vocabulary.elementNameTable));
+ }
+ if (externalVocabulary->attributeNameTable) {
+ std::copy(externalVocabulary->attributeNameTable, externalVocabulary->attributeNameTable + externalVocabulary->attributeNameTableSize, std::back_inserter(vocabulary.attributeNameTable));
+ }
+ }
+ if (b1 & 0x0800) {
+ // Parse restricted alphabets (C.2.5.3)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.restrictedAlphabetTable.push_back(parseNonEmptyOctetString2());
+ }
+ }
+ if (b1 & 0x0400) {
+ // Parse encoding algorithms (C.2.5.3)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.encodingAlgorithmTable.push_back(parseNonEmptyOctetString2());
+ }
+ }
+ if (b1 & 0x0200) {
+ // Parse prefixes (C.2.5.3)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.prefixTable.push_back(parseNonEmptyOctetString2());
+ }
+ }
+ if (b1 & 0x0100) {
+ // Parse namespace names (C.2.5.3)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.namespaceNameTable.push_back(parseNonEmptyOctetString2());
+ }
+ }
+ if (b1 & 0x0080) {
+ // Parse local names (C.2.5.3)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.localNameTable.push_back(parseNonEmptyOctetString2());
+ }
+ }
+ if (b1 & 0x0040) {
+ // Parse other ncnames (C.2.5.3)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.otherNCNameTable.push_back(parseNonEmptyOctetString2());
+ }
+ }
+ if (b1 & 0x0020) {
+ // Parse other uris (C.2.5.3)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.otherURITable.push_back(parseNonEmptyOctetString2());
+ }
+ }
+ if (b1 & 0x0010) {
+ // Parse attribute values (C.2.5.4)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.attributeValueTable.push_back(parseEncodedCharacterString3());
+ }
+ }
+ if (b1 & 0x0008) {
+ // Parse content character chunks (C.2.5.4)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.charactersTable.push_back(parseEncodedCharacterString3());
+ }
+ }
+ if (b1 & 0x0004) {
+ // Parse other strings (C.2.5.4)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ vocabulary.otherStringTable.push_back(parseEncodedCharacterString3());
+ }
+ }
+ if (b1 & 0x0002) {
+ // Parse element name surrogates (C.2.5.5)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ vocabulary.elementNameTable.push_back(parseNameSurrogate());
+ }
+ }
+ if (b1 & 0x0001) {
+ // Parse attribute name surrogates (C.2.5.5)
+ for (size_t len = parseSequenceLen(); len > 0; --len) {
+ vocabulary.attributeNameTable.push_back(parseNameSurrogate());
+ }
+ }
+ }
+ if (b & 0x10) {
+ // Parse notations (C.2.6)
+ for (;;) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ uint8_t b1 = *dataP++;
+ if (b1 == 0xf0) {
+ break;
+ }
+ if ((b1 & 0xfc) != 0xc0) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ /* C.11 */
+ /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
+ if (b1 & 0x02) {
+ /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ }
+ if (b1 & 0x01) {
+ /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ }
+ }
+ }
+ if (b & 0x08) {
+ // Parse unparsed entities (C.2.7)
+ for (;;) {
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ uint8_t b1 = *dataP++;
+ if (b1 == 0xf0) {
+ break;
+ }
+ if ((b1 & 0xfe) != 0xd0) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ /* C.10 */
+ /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
+ /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ if (b1 & 0x01) {
+ /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
+ }
+ /*const std::string &notationName =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
+ }
+ }
+ if (b & 0x04) {
+ // Parse character encoding scheme (C.2.8)
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ /*std::string characterEncodingScheme =*/ parseNonEmptyOctetString2();
+ }
+ if (b & 0x02) {
+ // Parse standalone flag (C.2.9)
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ uint8_t b1 = *dataP++;
+ if (b1 & 0xfe) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ //bool standalone = b1 & 0x01;
+ }
+ if (b & 0x01) {
+ // Parse version (C.2.10)
+ if (dataEnd - dataP < 1) {
+ throw DeadlyImportError(parseErrorMessage);
+ }
+ /*std::shared_ptr<const FIValue> version =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
+ }
+ }
+
+ std::unique_ptr<uint8_t[]> data;
+ uint8_t *dataP, *dataEnd;
+ irr::io::EXML_NODE currentNodeType;
+ bool emptyElement;
+ bool headerPending;
+ bool terminatorPending;
+ Vocabulary vocabulary;
+ std::vector<Attribute> attributes;
+ std::stack<std::string> elementStack;
+ std::string nodeName;
+ std::map<std::string, std::unique_ptr<FIDecoder>> decoderMap;
+ std::map<std::string, const FIVocabulary*> vocabularyMap;
+
+ static const std::string EmptyString;
+ static std::shared_ptr<const FIValue> EmptyFIString;
+
+ static FIHexDecoder hexDecoder;
+ static FIBase64Decoder base64Decoder;
+ static FIShortDecoder shortDecoder;
+ static FIIntDecoder intDecoder;
+ static FILongDecoder longDecoder;
+ static FIBoolDecoder boolDecoder;
+ static FIFloatDecoder floatDecoder;
+ static FIDoubleDecoder doubleDecoder;
+ static FIUUIDDecoder uuidDecoder;
+ static FICDATADecoder cdataDecoder;
+ static FIDecoder *defaultDecoder[32];
+};
+
+const std::string CFIReaderImpl::EmptyString;
+std::shared_ptr<const FIValue> CFIReaderImpl::EmptyFIString = FIStringValue::create(std::string());
+
+FIHexDecoder CFIReaderImpl::hexDecoder;
+FIBase64Decoder CFIReaderImpl::base64Decoder;
+FIShortDecoder CFIReaderImpl::shortDecoder;
+FIIntDecoder CFIReaderImpl::intDecoder;
+FILongDecoder CFIReaderImpl::longDecoder;
+FIBoolDecoder CFIReaderImpl::boolDecoder;
+FIFloatDecoder CFIReaderImpl::floatDecoder;
+FIDoubleDecoder CFIReaderImpl::doubleDecoder;
+FIUUIDDecoder CFIReaderImpl::uuidDecoder;
+FICDATADecoder CFIReaderImpl::cdataDecoder;
+
+FIDecoder *CFIReaderImpl::defaultDecoder[32] = {
+ &hexDecoder,
+ &base64Decoder,
+ &shortDecoder,
+ &intDecoder,
+ &longDecoder,
+ &boolDecoder,
+ &floatDecoder,
+ &doubleDecoder,
+ &uuidDecoder,
+ &cdataDecoder
+};
+
+class CXMLReaderImpl : public FIReader
+{
+public:
+
+ //! Constructor
+ CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader_)
+ : reader(std::move(reader_))
+ {}
+
+ virtual ~CXMLReaderImpl() {}
+
+ virtual bool read() /*override*/ {
+ return reader->read();
+ }
+
+ virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
+ return reader->getNodeType();
+ }
+
+ virtual int getAttributeCount() const /*override*/ {
+ return reader->getAttributeCount();
+ }
+
+ virtual const char* getAttributeName(int idx) const /*override*/ {
+ return reader->getAttributeName(idx);
+ }
+
+ virtual const char* getAttributeValue(int idx) const /*override*/ {
+ return reader->getAttributeValue(idx);
+ }
+
+ virtual const char* getAttributeValue(const char* name) const /*override*/ {
+ return reader->getAttributeValue(name);
+ }
+
+ virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
+ return reader->getAttributeValueSafe(name);
+ }
+
+ virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
+ return reader->getAttributeValueAsInt(name);
+ }
+
+ virtual int getAttributeValueAsInt(int idx) const /*override*/ {
+ return reader->getAttributeValueAsInt(idx);
+ }
+
+ virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
+ return reader->getAttributeValueAsFloat(name);
+ }
+
+ virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
+ return reader->getAttributeValueAsFloat(idx);
+ }
+
+ virtual const char* getNodeName() const /*override*/ {
+ return reader->getNodeName();
+ }
+
+ virtual const char* getNodeData() const /*override*/ {
+ return reader->getNodeData();
+ }
+
+ virtual bool isEmptyElement() const /*override*/ {
+ return reader->isEmptyElement();
+ }
+
+ virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
+ return reader->getSourceFormat();
+ }
+
+ virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
+ return reader->getParserFormat();
+ }
+
+ virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int /*idx*/) const /*override*/ {
+ return nullptr;
+ }
+
+ virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* /*name*/) const /*override*/ {
+ return nullptr;
+ }
+
+ virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr<FIDecoder> /*decoder*/) /*override*/ {}
+
+
+ virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {}
+
+private:
+
+ std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader;
+};
+
+static std::unique_ptr<uint8_t[]> readFile(IOStream *stream, size_t &size, bool &isFI) {
+ size = stream->FileSize();
+ std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
+ if (stream->Read(data.get(), size, 1) != 1) {
+ size = 0;
+ data.reset();
+ }
+ isFI = parseMagic(data.get(), data.get() + size) > 0;
+ return data;
+}
+
+std::unique_ptr<FIReader> FIReader::create(IOStream *stream)
+{
+ size_t size;
+ bool isFI;
+ auto data = readFile(stream, size, isFI);
+ if (isFI) {
+ return std::unique_ptr<FIReader>(new CFIReaderImpl(std::move(data), size));
+ }
+ else {
+ auto memios = std::unique_ptr<MemoryIOStream>(new MemoryIOStream(data.release(), size, true));
+ auto callback = std::unique_ptr<CIrrXML_IOStreamReader>(new CIrrXML_IOStreamReader(memios.get()));
+ return std::unique_ptr<FIReader>(new CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>>(createIrrXMLReader(callback.get()))));
+ }
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
diff --git a/thirdparty/assimp/code/FileLogStream.h b/thirdparty/assimp/code/FileLogStream.h
new file mode 100644
index 0000000000..740c503192
--- /dev/null
+++ b/thirdparty/assimp/code/FileLogStream.h
@@ -0,0 +1,107 @@
+/*
+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
diff --git a/thirdparty/assimp/code/FileSystemFilter.h b/thirdparty/assimp/code/FileSystemFilter.h
new file mode 100644
index 0000000000..9923cdbdd3
--- /dev/null
+++ b/thirdparty/assimp/code/FileSystemFilter.h
@@ -0,0 +1,345 @@
+/*
+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
diff --git a/thirdparty/assimp/code/FindDegenerates.cpp b/thirdparty/assimp/code/FindDegenerates.cpp
new file mode 100644
index 0000000000..365f5d7447
--- /dev/null
+++ b/thirdparty/assimp/code/FindDegenerates.cpp
@@ -0,0 +1,300 @@
+/*
+---------------------------------------------------------------------------
+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 FindDegenerates.cpp
+ * @brief Implementation of the FindDegenerates post-process step.
+*/
+
+
+
+// internal headers
+#include "ProcessHelper.h"
+#include "FindDegenerates.h"
+#include <assimp/Exceptional.h>
+
+using namespace Assimp;
+
+//remove mesh at position 'index' from the scene
+static void removeMesh(aiScene* pScene, unsigned const index);
+//correct node indices to meshes and remove references to deleted mesh
+static void updateSceneGraph(aiNode* pNode, unsigned const index);
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindDegeneratesProcess::FindDegeneratesProcess()
+: mConfigRemoveDegenerates( false )
+, mConfigCheckAreaOfTriangle( false ){
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FindDegeneratesProcess::~FindDegeneratesProcess() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
+ return 0 != (pFlags & aiProcess_FindDegenerates);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import configuration
+void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
+ // Get the current value of AI_CONFIG_PP_FD_REMOVE
+ mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
+ mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindDegeneratesProcess::Execute( aiScene* pScene) {
+ ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ //Do not process point cloud, ExecuteOnMesh works only with faces data
+ if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) {
+ removeMesh(pScene, i);
+ --i; //the current i is removed, do not skip the next one
+ }
+ }
+ ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
+}
+
+static void removeMesh(aiScene* pScene, unsigned const index) {
+ //we start at index and copy the pointers one position forward
+ //save the mesh pointer to delete it later
+ auto delete_me = pScene->mMeshes[index];
+ for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) {
+ pScene->mMeshes[i] = pScene->mMeshes[i+1];
+ }
+ pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr;
+ --(pScene->mNumMeshes);
+ delete delete_me;
+
+ //removing a mesh also requires updating all references to it in the scene graph
+ updateSceneGraph(pScene->mRootNode, index);
+}
+
+static void updateSceneGraph(aiNode* pNode, unsigned const index) {
+ for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
+ if (pNode->mMeshes[i] > index) {
+ --(pNode->mMeshes[i]);
+ continue;
+ }
+ if (pNode->mMeshes[i] == index) {
+ for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) {
+ pNode->mMeshes[j] = pNode->mMeshes[j+1];
+ }
+ --(pNode->mNumMeshes);
+ --i;
+ continue;
+ }
+ }
+ //recurse to all children
+ for (unsigned i = 0; i < pNode->mNumChildren; ++i) {
+ updateSceneGraph(pNode->mChildren[i], index);
+ }
+}
+
+static ai_real heron( ai_real a, ai_real b, ai_real c ) {
+ ai_real s = (a + b + c) / 2;
+ ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
+ return area;
+}
+
+static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) {
+ const ai_real lx = ( vB.x - vA.x );
+ const ai_real ly = ( vB.y - vA.y );
+ const ai_real lz = ( vB.z - vA.z );
+ ai_real a = lx*lx + ly*ly + lz*lz;
+ ai_real d = pow( a, (ai_real)0.5 );
+
+ return d;
+}
+
+static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
+ ai_real area = 0;
+
+ aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
+ aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
+ aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
+
+ ai_real a( distance3D( vA, vB ) );
+ ai_real b( distance3D( vB, vC ) );
+ ai_real c( distance3D( vC, vA ) );
+ area = heron( a, b, c );
+
+ return area;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported mesh
+bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
+ mesh->mPrimitiveTypes = 0;
+
+ std::vector<bool> remove_me;
+ if (mConfigRemoveDegenerates) {
+ remove_me.resize( mesh->mNumFaces, false );
+ }
+
+ unsigned int deg = 0, limit;
+ for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
+ aiFace& face = mesh->mFaces[a];
+ bool first = true;
+
+ // check whether the face contains degenerated entries
+ for (unsigned int i = 0; i < face.mNumIndices; ++i) {
+ // Polygons with more than 4 points are allowed to have double points, that is
+ // simulating polygons with holes just with concave polygons. However,
+ // double points may not come directly after another.
+ limit = face.mNumIndices;
+ if (face.mNumIndices > 4) {
+ limit = std::min( limit, i+2 );
+ }
+
+ for (unsigned int t = i+1; t < limit; ++t) {
+ if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
+ // we have found a matching vertex position
+ // remove the corresponding index from the array
+ --face.mNumIndices;
+ --limit;
+ for (unsigned int m = t; m < face.mNumIndices; ++m) {
+ face.mIndices[ m ] = face.mIndices[ m+1 ];
+ }
+ --t;
+
+ // NOTE: we set the removed vertex index to an unique value
+ // to make sure the developer gets notified when his
+ // application attempts to access this data.
+ face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
+
+ if(first) {
+ ++deg;
+ first = false;
+ }
+
+ if ( mConfigRemoveDegenerates ) {
+ remove_me[ a ] = true;
+ goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
+ }
+ }
+ }
+
+ if ( mConfigCheckAreaOfTriangle ) {
+ if ( face.mNumIndices == 3 ) {
+ ai_real area = calculateAreaOfTriangle( face, mesh );
+ if ( area < 1e-6 ) {
+ if ( mConfigRemoveDegenerates ) {
+ remove_me[ a ] = true;
+ goto evil_jump_outside;
+ }
+
+ // todo: check for index which is corrupt.
+ }
+ }
+ }
+ }
+
+ // We need to update the primitive flags array of the mesh.
+ switch (face.mNumIndices)
+ {
+ case 1u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ break;
+ };
+evil_jump_outside:
+ continue;
+ }
+
+ // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
+ if (mConfigRemoveDegenerates && deg) {
+ unsigned int n = 0;
+ for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
+ {
+ aiFace& face_src = mesh->mFaces[a];
+ if (!remove_me[a]) {
+ aiFace& face_dest = mesh->mFaces[n++];
+
+ // Do a manual copy, keep the index array
+ face_dest.mNumIndices = face_src.mNumIndices;
+ face_dest.mIndices = face_src.mIndices;
+
+ if (&face_src != &face_dest) {
+ // clear source
+ face_src.mNumIndices = 0;
+ face_src.mIndices = nullptr;
+ }
+ }
+ else {
+ // Otherwise delete it if we don't need this face
+ delete[] face_src.mIndices;
+ face_src.mIndices = nullptr;
+ face_src.mNumIndices = 0;
+ }
+ }
+ // Just leave the rest of the array unreferenced, we don't care for now
+ mesh->mNumFaces = n;
+ if (!mesh->mNumFaces) {
+ //The whole mesh consists of degenerated faces
+ //signal upward, that this mesh should be deleted.
+ ASSIMP_LOG_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives");
+ return true;
+ }
+ }
+
+ if (deg && !DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives");
+ }
+ return false;
+}
diff --git a/thirdparty/assimp/code/FindDegenerates.h b/thirdparty/assimp/code/FindDegenerates.h
new file mode 100644
index 0000000000..880f5f16a2
--- /dev/null
+++ b/thirdparty/assimp/code/FindDegenerates.h
@@ -0,0 +1,129 @@
+/*
+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_FINDDEGENERATESPROCESS_H_INC
+#define AI_FINDDEGENERATESPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+
+class FindDegeneratesProcessTest;
+namespace Assimp {
+
+
+// ---------------------------------------------------------------------------
+/** FindDegeneratesProcess: Searches a mesh for degenerated triangles.
+*/
+class ASSIMP_API FindDegeneratesProcess : public BaseProcess {
+public:
+ FindDegeneratesProcess();
+ ~FindDegeneratesProcess();
+
+ // -------------------------------------------------------------------
+ // Check whether step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ // Execute step on a given mesh
+ ///@returns true if the current mesh should be deleted, false otherwise
+ bool ExecuteOnMesh( aiMesh* mesh);
+
+ // -------------------------------------------------------------------
+ /// @brief Enable the instant removal of degenerated primitives
+ /// @param enabled true for enabled.
+ void EnableInstantRemoval(bool enabled);
+
+ // -------------------------------------------------------------------
+ /// @brief Check whether instant removal is currently enabled
+ /// @return The instant removal state.
+ bool IsInstantRemoval() const;
+
+ // -------------------------------------------------------------------
+ /// @brief Enable the area check for triangles.
+ /// @param enabled true for enabled.
+ void EnableAreaCheck( bool enabled );
+
+ // -------------------------------------------------------------------
+ /// @brief Check whether the area check is enabled.
+ /// @return The area check state.
+ bool isAreaCheckEnabled() const;
+
+private:
+ //! Configuration option: remove degenerates faces immediately
+ bool mConfigRemoveDegenerates;
+ //! Configuration option: check for area
+ bool mConfigCheckAreaOfTriangle;
+};
+
+inline
+void FindDegeneratesProcess::EnableInstantRemoval(bool enabled) {
+ mConfigRemoveDegenerates = enabled;
+}
+
+inline
+bool FindDegeneratesProcess::IsInstantRemoval() const {
+ return mConfigRemoveDegenerates;
+}
+
+inline
+void FindDegeneratesProcess::EnableAreaCheck( bool enabled ) {
+ mConfigCheckAreaOfTriangle = enabled;
+}
+
+inline
+bool FindDegeneratesProcess::isAreaCheckEnabled() const {
+ return mConfigCheckAreaOfTriangle;
+}
+
+} // Namespace Assimp
+
+#endif // !! AI_FINDDEGENERATESPROCESS_H_INC
diff --git a/thirdparty/assimp/code/FindInstancesProcess.cpp b/thirdparty/assimp/code/FindInstancesProcess.cpp
new file mode 100644
index 0000000000..be1138116e
--- /dev/null
+++ b/thirdparty/assimp/code/FindInstancesProcess.cpp
@@ -0,0 +1,277 @@
+/*
+---------------------------------------------------------------------------
+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 FindInstancesProcess.cpp
+ * @brief Implementation of the aiProcess_FindInstances postprocessing step
+*/
+
+
+#include "FindInstancesProcess.h"
+#include <memory>
+#include <stdio.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindInstancesProcess::FindInstancesProcess()
+: configSpeedFlag (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FindInstancesProcess::~FindInstancesProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FindInstancesProcess::IsActive( unsigned int pFlags) const
+{
+ // FindInstances makes absolutely no sense together with PreTransformVertices
+ // fixme: spawn error message somewhere else?
+ return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties for the step
+void FindInstancesProcess::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Compare the bones of two meshes
+bool CompareBones(const aiMesh* orig, const aiMesh* inst)
+{
+ for (unsigned int i = 0; i < orig->mNumBones;++i) {
+ aiBone* aha = orig->mBones[i];
+ aiBone* oha = inst->mBones[i];
+
+ if (aha->mNumWeights != oha->mNumWeights ||
+ aha->mOffsetMatrix != oha->mOffsetMatrix) {
+ return false;
+ }
+
+ // compare weight per weight ---
+ for (unsigned int n = 0; n < aha->mNumWeights;++n) {
+ if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId ||
+ (aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update mesh indices in the node graph
+void UpdateMeshIndices(aiNode* node, unsigned int* lookup)
+{
+ for (unsigned int n = 0; n < node->mNumMeshes;++n)
+ node->mMeshes[n] = lookup[node->mMeshes[n]];
+
+ for (unsigned int n = 0; n < node->mNumChildren;++n)
+ UpdateMeshIndices(node->mChildren[n],lookup);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindInstancesProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("FindInstancesProcess begin");
+ if (pScene->mNumMeshes) {
+
+ // use a pseudo hash for all meshes in the scene to quickly find
+ // the ones which are possibly equal. This step is executed early
+ // in the pipeline, so we could, depending on the file format,
+ // have several thousand small meshes. That's too much for a brute
+ // everyone-against-everyone check involving up to 10 comparisons
+ // each.
+ std::unique_ptr<uint64_t[]> hashes (new uint64_t[pScene->mNumMeshes]);
+ std::unique_ptr<unsigned int[]> remapping (new unsigned int[pScene->mNumMeshes]);
+
+ unsigned int numMeshesOut = 0;
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+
+ aiMesh* inst = pScene->mMeshes[i];
+ hashes[i] = GetMeshHash(inst);
+
+ for (int a = i-1; a >= 0; --a) {
+ if (hashes[i] == hashes[a])
+ {
+ aiMesh* orig = pScene->mMeshes[a];
+ if (!orig)
+ continue;
+
+ // check for hash collision .. we needn't check
+ // the vertex format, it *must* match due to the
+ // (brilliant) construction of the hash
+ if (orig->mNumBones != inst->mNumBones ||
+ orig->mNumFaces != inst->mNumFaces ||
+ orig->mNumVertices != inst->mNumVertices ||
+ orig->mMaterialIndex != inst->mMaterialIndex ||
+ orig->mPrimitiveTypes != inst->mPrimitiveTypes)
+ continue;
+
+ // up to now the meshes are equal. find an appropriate
+ // epsilon to compare position differences against
+ float epsilon = ComputePositionEpsilon(inst);
+ epsilon *= epsilon;
+
+ // now compare vertex positions, normals,
+ // tangents and bitangents using this epsilon.
+ if (orig->HasPositions()) {
+ if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon))
+ continue;
+ }
+ if (orig->HasNormals()) {
+ if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon))
+ continue;
+ }
+ if (orig->HasTangentsAndBitangents()) {
+ if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) ||
+ !CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon))
+ continue;
+ }
+
+ // use a constant epsilon for colors and UV coordinates
+ static const float uvEpsilon = 10e-4f;
+ {
+ unsigned int j, end = orig->GetNumUVChannels();
+ for(j = 0; j < end; ++j) {
+ if (!orig->mTextureCoords[j]) {
+ continue;
+ }
+ if(!CompareArrays(orig->mTextureCoords[j],inst->mTextureCoords[j],orig->mNumVertices,uvEpsilon)) {
+ break;
+ }
+ }
+ if (j != end) {
+ continue;
+ }
+ }
+ {
+ unsigned int j, end = orig->GetNumColorChannels();
+ for(j = 0; j < end; ++j) {
+ if (!orig->mColors[j]) {
+ continue;
+ }
+ if(!CompareArrays(orig->mColors[j],inst->mColors[j],orig->mNumVertices,uvEpsilon)) {
+ break;
+ }
+ }
+ if (j != end) {
+ continue;
+ }
+ }
+
+ // These two checks are actually quite expensive and almost *never* required.
+ // Almost. That's why they're still here. But there's no reason to do them
+ // in speed-targeted imports.
+ if (!configSpeedFlag) {
+
+ // It seems to be strange, but we really need to check whether the
+ // bones are identical too. Although it's extremely unprobable
+ // that they're not if control reaches here, we need to deal
+ // with unprobable cases, too. It could still be that there are
+ // equal shapes which are deformed differently.
+ if (!CompareBones(orig,inst))
+ continue;
+
+ // For completeness ... compare even the index buffers for equality
+ // face order & winding order doesn't care. Input data is in verbose format.
+ std::unique_ptr<unsigned int[]> ftbl_orig(new unsigned int[orig->mNumVertices]);
+ std::unique_ptr<unsigned int[]> ftbl_inst(new unsigned int[orig->mNumVertices]);
+
+ for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) {
+ aiFace& f = orig->mFaces[tt];
+ for (unsigned int nn = 0; nn < f.mNumIndices;++nn)
+ ftbl_orig[f.mIndices[nn]] = tt;
+
+ aiFace& f2 = inst->mFaces[tt];
+ for (unsigned int nn = 0; nn < f2.mNumIndices;++nn)
+ ftbl_inst[f2.mIndices[nn]] = tt;
+ }
+ if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int)))
+ continue;
+ }
+
+ // We're still here. Or in other words: 'inst' is an instance of 'orig'.
+ // Place a marker in our list that we can easily update mesh indices.
+ remapping[i] = remapping[a];
+
+ // Delete the instanced mesh, we don't need it anymore
+ delete inst;
+ pScene->mMeshes[i] = NULL;
+ break;
+ }
+ }
+
+ // If we didn't find a match for the current mesh: keep it
+ if (pScene->mMeshes[i]) {
+ remapping[i] = numMeshesOut++;
+ }
+ }
+ ai_assert(0 != numMeshesOut);
+ if (numMeshesOut != pScene->mNumMeshes) {
+
+ // Collapse the meshes array by removing all NULL entries
+ for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) {
+ if (pScene->mMeshes[i])
+ pScene->mMeshes[real++] = pScene->mMeshes[i];
+ }
+
+ // And update the node graph with our nice lookup table
+ UpdateMeshIndices(pScene->mRootNode,remapping.get());
+
+ // write to log
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_INFO_F( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" );
+ }
+ pScene->mNumMeshes = numMeshesOut;
+ } else {
+ ASSIMP_LOG_DEBUG("FindInstancesProcess finished. No instanced meshes found");
+ }
+ }
+}
diff --git a/thirdparty/assimp/code/FindInstancesProcess.h b/thirdparty/assimp/code/FindInstancesProcess.h
new file mode 100644
index 0000000000..ab4a371c71
--- /dev/null
+++ b/thirdparty/assimp/code/FindInstancesProcess.h
@@ -0,0 +1,137 @@
+/*
+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 FindInstancesProcess.h
+ * @brief Declares the aiProcess_FindInstances post-process step
+ */
+#ifndef AI_FINDINSTANCES_H_INC
+#define AI_FINDINSTANCES_H_INC
+
+#include "BaseProcess.h"
+#include "ProcessHelper.h"
+
+class FindInstancesProcessTest;
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+/** @brief Get a pseudo(!)-hash representing a mesh.
+ *
+ * The hash is built from number of vertices, faces, primitive types,
+ * .... but *not* from the real mesh data. The funcction is not a perfect hash.
+ * @param in Input mesh
+ * @return Hash.
+ */
+inline
+uint64_t GetMeshHash(aiMesh* in) {
+ ai_assert(nullptr != in);
+
+ // ... get an unique value representing the vertex format of the mesh
+ const unsigned int fhash = GetMeshVFormatUnique(in);
+
+ // and bake it with number of vertices/faces/bones/matidx/ptypes
+ return ((uint64_t)fhash << 32u) | ((
+ (in->mNumBones << 16u) ^ (in->mNumVertices) ^
+ (in->mNumFaces<<4u) ^ (in->mMaterialIndex<<15) ^
+ (in->mPrimitiveTypes<<28)) & 0xffffffff );
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Perform a component-wise comparison of two arrays
+ *
+ * @param first First array
+ * @param second Second array
+ * @param size Size of both arrays
+ * @param e Epsilon
+ * @return true if the arrays are identical
+ */
+inline
+bool CompareArrays(const aiVector3D* first, const aiVector3D* second,
+ unsigned int size, float e) {
+ for (const aiVector3D* end = first+size; first != end; ++first,++second) {
+ if ( (*first - *second).SquareLength() >= e)
+ return false;
+ }
+ return true;
+}
+
+// and the same for colors ...
+inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second,
+ unsigned int size, float e)
+{
+ for (const aiColor4D* end = first+size; first != end; ++first,++second) {
+ if ( GetColorDifference(*first,*second) >= e)
+ return false;
+ }
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+/** @brief A post-processing steps to search for instanced meshes
+*/
+class FindInstancesProcess : public BaseProcess
+{
+public:
+
+ FindInstancesProcess();
+ ~FindInstancesProcess();
+
+public:
+ // -------------------------------------------------------------------
+ // Check whether step is active in given flags combination
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup properties prior to executing the process
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ bool configSpeedFlag;
+
+}; // ! end class FindInstancesProcess
+} // ! end namespace Assimp
+
+#endif // !! AI_FINDINSTANCES_H_INC
diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.cpp b/thirdparty/assimp/code/FindInvalidDataProcess.cpp
new file mode 100644
index 0000000000..433f042448
--- /dev/null
+++ b/thirdparty/assimp/code/FindInvalidDataProcess.cpp
@@ -0,0 +1,424 @@
+/*
+---------------------------------------------------------------------------
+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 an importer's output
+ for data that is obviously invalid */
+
+
+
+#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
+
+// internal headers
+#include "FindInvalidDataProcess.h"
+#include "ProcessHelper.h"
+
+#include <assimp/Macros.h>
+#include <assimp/Exceptional.h>
+#include <assimp/qnan.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindInvalidDataProcess::FindInvalidDataProcess()
+: configEpsilon(0.0)
+, mIgnoreTexCoods( false ){
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FindInvalidDataProcess::~FindInvalidDataProcess() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const {
+ return 0 != (pFlags & aiProcess_FindInvalidData);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import configuration
+void FindInvalidDataProcess::SetupProperties(const Importer* pImp) {
+ // Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY
+ configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f));
+ mIgnoreTexCoods = pImp->GetPropertyBool(AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS, false);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update mesh references in the node graph
+void UpdateMeshReferences(aiNode* node, const std::vector<unsigned int>& meshMapping) {
+ if (node->mNumMeshes) {
+ unsigned int out = 0;
+ for (unsigned int a = 0; a < node->mNumMeshes;++a) {
+
+ unsigned int ref = node->mMeshes[a];
+ if (UINT_MAX != (ref = meshMapping[ref])) {
+ node->mMeshes[out++] = ref;
+ }
+ }
+ // just let the members that are unused, that's much cheaper
+ // than a full array realloc'n'copy party ...
+ if(!(node->mNumMeshes = out)) {
+
+ delete[] node->mMeshes;
+ node->mMeshes = NULL;
+ }
+ }
+ // recursively update all children
+ for (unsigned int i = 0; i < node->mNumChildren;++i) {
+ UpdateMeshReferences(node->mChildren[i],meshMapping);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindInvalidDataProcess::Execute( aiScene* pScene) {
+ ASSIMP_LOG_DEBUG("FindInvalidDataProcess begin");
+
+ bool out = false;
+ std::vector<unsigned int> meshMapping(pScene->mNumMeshes);
+ unsigned int real = 0;
+
+ // Process meshes
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+
+ int result;
+ if ((result = ProcessMesh( pScene->mMeshes[a]))) {
+ out = true;
+
+ if (2 == result) {
+ // remove this mesh
+ delete pScene->mMeshes[a];
+ AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]);
+
+ meshMapping[a] = UINT_MAX;
+ continue;
+ }
+ }
+ pScene->mMeshes[real] = pScene->mMeshes[a];
+ meshMapping[a] = real++;
+ }
+
+ // Process animations
+ for (unsigned int a = 0; a < pScene->mNumAnimations;++a) {
+ ProcessAnimation( pScene->mAnimations[a]);
+ }
+
+
+ if (out) {
+ if ( real != pScene->mNumMeshes) {
+ if (!real) {
+ throw DeadlyImportError("No meshes remaining");
+ }
+
+ // we need to remove some meshes.
+ // therefore we'll also need to remove all references
+ // to them from the scenegraph
+ UpdateMeshReferences(pScene->mRootNode,meshMapping);
+ pScene->mNumMeshes = real;
+ }
+
+ ASSIMP_LOG_INFO("FindInvalidDataProcess finished. Found issues ...");
+ } else {
+ ASSIMP_LOG_DEBUG("FindInvalidDataProcess finished. Everything seems to be OK.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline
+const char* ValidateArrayContents(const T* /*arr*/, unsigned int /*size*/,
+ const std::vector<bool>& /*dirtyMask*/, bool /*mayBeIdentical = false*/, bool /*mayBeZero = true*/)
+{
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <>
+inline
+const char* ValidateArrayContents<aiVector3D>(const aiVector3D* arr, unsigned int size,
+ const std::vector<bool>& dirtyMask, bool mayBeIdentical , bool mayBeZero ) {
+ bool b = false;
+ unsigned int cnt = 0;
+ for (unsigned int i = 0; i < size;++i) {
+
+ if (dirtyMask.size() && dirtyMask[i]) {
+ continue;
+ }
+ ++cnt;
+
+ const aiVector3D& v = arr[i];
+ if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) {
+ return "INF/NAN was found in a vector component";
+ }
+ if (!mayBeZero && !v.x && !v.y && !v.z ) {
+ return "Found zero-length vector";
+ }
+ if (i && v != arr[i-1])b = true;
+ }
+ if (cnt > 1 && !b && !mayBeIdentical) {
+ return "All vectors are identical";
+ }
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline
+bool ProcessArray(T*& in, unsigned int num,const char* name,
+ const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) {
+ const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero);
+ if (err) {
+ ASSIMP_LOG_ERROR_F( "FindInvalidDataProcess fails on mesh ", name, ": ", err);
+ delete[] in;
+ in = NULL;
+ return true;
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, ai_real epsilon);
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE bool EpsilonCompare(ai_real n, ai_real s, ai_real epsilon) {
+ return std::fabs(n-s)>epsilon;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <>
+bool EpsilonCompare<aiVectorKey>(const aiVectorKey& n, const aiVectorKey& s, ai_real epsilon) {
+ return
+ EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
+ EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
+ EpsilonCompare(n.mValue.z,s.mValue.z,epsilon);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <>
+bool EpsilonCompare<aiQuatKey>(const aiQuatKey& n, const aiQuatKey& s, ai_real epsilon) {
+ return
+ EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
+ EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
+ EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) &&
+ EpsilonCompare(n.mValue.w,s.mValue.w,epsilon);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline
+bool AllIdentical(T* in, unsigned int num, ai_real epsilon) {
+ if (num <= 1) {
+ return true;
+ }
+
+ if (fabs(epsilon) > 0.f) {
+ for (unsigned int i = 0; i < num-1;++i) {
+ if (!EpsilonCompare(in[i],in[i+1],epsilon)) {
+ return false;
+ }
+ }
+ } else {
+ for (unsigned int i = 0; i < num-1;++i) {
+ if (in[i] != in[i+1]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Search an animation for invalid content
+void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim) {
+ // Process all animation channels
+ for ( unsigned int a = 0; a < anim->mNumChannels; ++a ) {
+ ProcessAnimationChannel( anim->mChannels[a]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
+ ai_assert( nullptr != anim );
+ if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) {
+ ai_assert_entry();
+ return;
+ }
+
+ // Check whether all values in a tracks are identical - in this case
+ // we can remove al keys except one.
+ // POSITIONS
+ int i = 0;
+ if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon)) {
+ aiVectorKey v = anim->mPositionKeys[0];
+
+ // Reallocate ... we need just ONE element, it makes no sense to reuse the array
+ delete[] anim->mPositionKeys;
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys = 1];
+ anim->mPositionKeys[0] = v;
+ i = 1;
+ }
+
+ // ROTATIONS
+ if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon)) {
+ aiQuatKey v = anim->mRotationKeys[0];
+
+ // Reallocate ... we need just ONE element, it makes no sense to reuse the array
+ delete[] anim->mRotationKeys;
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys = 1];
+ anim->mRotationKeys[0] = v;
+ i = 1;
+ }
+
+ // SCALINGS
+ if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon)) {
+ aiVectorKey v = anim->mScalingKeys[0];
+
+ // Reallocate ... we need just ONE element, it makes no sense to reuse the array
+ delete[] anim->mScalingKeys;
+ anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys = 1];
+ anim->mScalingKeys[0] = v;
+ i = 1;
+ }
+ if ( 1 == i ) {
+ ASSIMP_LOG_WARN("Simplified dummy tracks with just one key");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Search a mesh for invalid contents
+int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh)
+{
+ bool ret = false;
+ std::vector<bool> dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0);
+
+ // Ignore elements that are not referenced by vertices.
+ // (they are, for example, caused by the FindDegenerates step)
+ for (unsigned int m = 0; m < pMesh->mNumFaces; ++m) {
+ const aiFace& f = pMesh->mFaces[m];
+
+ for (unsigned int i = 0; i < f.mNumIndices; ++i) {
+ dirtyMask[f.mIndices[i]] = false;
+ }
+ }
+
+ // Process vertex positions
+ if (pMesh->mVertices && ProcessArray(pMesh->mVertices, pMesh->mNumVertices, "positions", dirtyMask)) {
+ ASSIMP_LOG_ERROR("Deleting mesh: Unable to continue without vertex positions");
+
+ return 2;
+ }
+
+ // process texture coordinates
+ if (!mIgnoreTexCoods) {
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i]; ++i) {
+ if (ProcessArray(pMesh->mTextureCoords[i], pMesh->mNumVertices, "uvcoords", dirtyMask)) {
+ pMesh->mNumUVComponents[i] = 0;
+
+ // delete all subsequent texture coordinate sets.
+ for (unsigned int a = i + 1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+ delete[] pMesh->mTextureCoords[a];
+ pMesh->mTextureCoords[a] = NULL;
+ pMesh->mNumUVComponents[a] = 0;
+ }
+
+ ret = true;
+ }
+ }
+ }
+
+ // -- we don't validate vertex colors, it's difficult to say whether
+ // they are invalid or not.
+
+ // Normals and tangents are undefined for point and line faces.
+ if (pMesh->mNormals || pMesh->mTangents) {
+
+ if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes ||
+ aiPrimitiveType_LINE & pMesh->mPrimitiveTypes)
+ {
+ if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes ||
+ aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes)
+ {
+ // We need to update the lookup-table
+ for (unsigned int m = 0; m < pMesh->mNumFaces;++m) {
+ const aiFace& f = pMesh->mFaces[ m ];
+
+ if (f.mNumIndices < 3) {
+ dirtyMask[f.mIndices[0]] = true;
+ if (f.mNumIndices == 2) {
+ dirtyMask[f.mIndices[1]] = true;
+ }
+ }
+ }
+ }
+ // Normals, tangents and bitangents are undefined for
+ // the whole mesh (and should not even be there)
+ else {
+ return ret;
+ }
+ }
+
+ // Process mesh normals
+ if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices,
+ "normals",dirtyMask,true,false))
+ ret = true;
+
+ // Process mesh tangents
+ if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) {
+ delete[] pMesh->mBitangents; pMesh->mBitangents = NULL;
+ ret = true;
+ }
+
+ // Process mesh bitangents
+ if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) {
+ delete[] pMesh->mTangents; pMesh->mTangents = NULL;
+ ret = true;
+ }
+ }
+ return ret ? 1 : 0;
+}
+
+#endif // !! ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
diff --git a/thirdparty/assimp/code/FindInvalidDataProcess.h b/thirdparty/assimp/code/FindInvalidDataProcess.h
new file mode 100644
index 0000000000..8504fb7b1f
--- /dev/null
+++ b/thirdparty/assimp/code/FindInvalidDataProcess.h
@@ -0,0 +1,105 @@
+/*
+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 an importer's output
+ * for data that is obviously invalid
+ */
+#ifndef AI_FINDINVALIDDATA_H_INC
+#define AI_FINDINVALIDDATA_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/types.h>
+#include <assimp/anim.h>
+
+struct aiMesh;
+
+class FindInvalidDataProcessTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The FindInvalidData post-processing step. It searches the mesh data
+ * for parts that are obviously invalid and removes them.
+ *
+ * Originally this was a workaround for some models written by Blender
+ * which have zero normal vectors. */
+class ASSIMP_API FindInvalidDataProcess : public BaseProcess {
+public:
+ FindInvalidDataProcess();
+ ~FindInvalidDataProcess();
+
+ // -------------------------------------------------------------------
+ //
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ // Run the step
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ /** Executes the post-processing step on the given mesh
+ * @param pMesh The mesh to process.
+ * @return 0 - nothing, 1 - removed sth, 2 - please delete me */
+ int ProcessMesh( aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** Executes the post-processing step on the given animation
+ * @param anim The animation to process. */
+ void ProcessAnimation (aiAnimation* anim);
+
+ // -------------------------------------------------------------------
+ /** Executes the post-processing step on the given anim channel
+ * @param anim The animation channel to process.*/
+ void ProcessAnimationChannel (aiNodeAnim* anim);
+
+private:
+ ai_real configEpsilon;
+ bool mIgnoreTexCoods;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_AI_FINDINVALIDDATA_H_INC
diff --git a/thirdparty/assimp/code/FixNormalsStep.cpp b/thirdparty/assimp/code/FixNormalsStep.cpp
new file mode 100644
index 0000000000..bbbe6899b4
--- /dev/null
+++ b/thirdparty/assimp/code/FixNormalsStep.cpp
@@ -0,0 +1,184 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to invert
+ * all normals in meshes with infacing normals.
+ */
+
+// internal headers
+#include "FixNormalsStep.h"
+#include <assimp/StringUtils.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <stdio.h>
+
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FixInfacingNormalsProcess::FixInfacingNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FixInfacingNormalsProcess::~FixInfacingNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_FixInfacingNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FixInfacingNormalsProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess begin");
+
+ bool bHas( false );
+ for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+ if (ProcessMesh(pScene->mMeshes[a], a)) {
+ bHas = true;
+ }
+ }
+
+ if (bHas) {
+ ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. Found issues.");
+ } else {
+ ASSIMP_LOG_DEBUG("FixInfacingNormalsProcess finished. No changes to the scene.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the step to the mesh
+bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index)
+{
+ ai_assert(nullptr != pcMesh);
+
+ // Nothing to do if there are no model normals
+ if (!pcMesh->HasNormals()) {
+ return false;
+ }
+
+ // Compute the bounding box of both the model vertices + normals and
+ // the unmodified model vertices. Then check whether the first BB
+ // is smaller than the second. In this case we can assume that the
+ // normals need to be flipped, although there are a few special cases ..
+ // convex, concave, planar models ...
+
+ aiVector3D vMin0 (1e10f,1e10f,1e10f);
+ aiVector3D vMin1 (1e10f,1e10f,1e10f);
+ aiVector3D vMax0 (-1e10f,-1e10f,-1e10f);
+ aiVector3D vMax1 (-1e10f,-1e10f,-1e10f);
+
+ for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+ {
+ vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x);
+ vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y);
+ vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z);
+
+ vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x);
+ vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y);
+ vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z);
+
+ const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i];
+
+ vMin0.x = std::min(vMin0.x,vWithNormal.x);
+ vMin0.y = std::min(vMin0.y,vWithNormal.y);
+ vMin0.z = std::min(vMin0.z,vWithNormal.z);
+
+ vMax0.x = std::max(vMax0.x,vWithNormal.x);
+ vMax0.y = std::max(vMax0.y,vWithNormal.y);
+ vMax0.z = std::max(vMax0.z,vWithNormal.z);
+ }
+
+ const float fDelta0_x = (vMax0.x - vMin0.x);
+ const float fDelta0_y = (vMax0.y - vMin0.y);
+ const float fDelta0_z = (vMax0.z - vMin0.z);
+
+ const float fDelta1_x = (vMax1.x - vMin1.x);
+ const float fDelta1_y = (vMax1.y - vMin1.y);
+ const float fDelta1_z = (vMax1.z - vMin1.z);
+
+ // Check whether the boxes are overlapping
+ if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false;
+ if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false;
+ if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false;
+
+ // Check whether this is a planar surface
+ const float fDelta1_yz = fDelta1_y * fDelta1_z;
+
+ if (fDelta1_x < 0.05f * std::sqrt( fDelta1_yz ))return false;
+ if (fDelta1_y < 0.05f * std::sqrt( fDelta1_z * fDelta1_x ))return false;
+ if (fDelta1_z < 0.05f * std::sqrt( fDelta1_y * fDelta1_x ))return false;
+
+ // now compare the volumes of the bounding boxes
+ if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < std::fabs(fDelta1_x * fDelta1_yz)) {
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_INFO_F("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index);
+ }
+
+ // Invert normals
+ for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+ pcMesh->mNormals[i] *= -1.0f;
+
+ // ... and flip faces
+ for (unsigned int i = 0; i < pcMesh->mNumFaces;++i)
+ {
+ aiFace& face = pcMesh->mFaces[i];
+ for( unsigned int b = 0; b < face.mNumIndices / 2; b++)
+ std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]);
+ }
+ return true;
+ }
+ return false;
+}
diff --git a/thirdparty/assimp/code/FixNormalsStep.h b/thirdparty/assimp/code/FixNormalsStep.h
new file mode 100644
index 0000000000..6be27faef6
--- /dev/null
+++ b/thirdparty/assimp/code/FixNormalsStep.h
@@ -0,0 +1,91 @@
+/*
+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 fix infacing normals */
+#ifndef AI_FIXNORMALSPROCESS_H_INC
+#define AI_FIXNORMALSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The FixInfacingNormalsProcess tries to determine whether the normal
+ * vectors of an object are facing inwards. In this case they will be
+ * flipped.
+ */
+class FixInfacingNormalsProcess : public BaseProcess {
+public:
+ FixInfacingNormalsProcess();
+ ~FixInfacingNormalsProcess();
+
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @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;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Executes the step on the given mesh
+ * @param pMesh The mesh to process.
+ */
+ bool ProcessMesh( aiMesh* pMesh, unsigned int index);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_FIXNORMALSPROCESS_H_INC
diff --git a/thirdparty/assimp/code/GenFaceNormalsProcess.cpp b/thirdparty/assimp/code/GenFaceNormalsProcess.cpp
new file mode 100644
index 0000000000..028334dec7
--- /dev/null
+++ b/thirdparty/assimp/code/GenFaceNormalsProcess.cpp
@@ -0,0 +1,146 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to generate face
+* normals for all imported faces.
+*/
+
+
+#include "GenFaceNormalsProcess.h"
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Exceptional.h>
+#include <assimp/qnan.h>
+
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+GenFaceNormalsProcess::GenFaceNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+GenFaceNormalsProcess::~GenFaceNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const {
+ force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
+ return (pFlags & aiProcess_GenNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenFaceNormalsProcess::Execute( aiScene* pScene) {
+ ASSIMP_LOG_DEBUG("GenFaceNormalsProcess begin");
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
+ throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
+ }
+
+ bool bHas = false;
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ if(this->GenMeshFaceNormals( pScene->mMeshes[a])) {
+ bHas = true;
+ }
+ }
+ if (bHas) {
+ ASSIMP_LOG_INFO("GenFaceNormalsProcess finished. "
+ "Face normals have been calculated");
+ } else {
+ ASSIMP_LOG_DEBUG("GenFaceNormalsProcess finished. "
+ "Normals are already there");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
+{
+ if (NULL != pMesh->mNormals) {
+ if (force_) delete[] pMesh->mNormals;
+ else return false;
+ }
+
+ // If the mesh consists of lines and/or points but not of
+ // triangles or higher-order polygons the normal vectors
+ // are undefined.
+ if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) {
+ ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes");
+ return false;
+ }
+
+ // allocate an array to hold the output normals
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ const float qnan = get_qnan();
+
+ // iterate through all faces and compute per-face normals but store them per-vertex.
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ const aiFace& face = pMesh->mFaces[a];
+ if (face.mNumIndices < 3) {
+ // either a point or a line -> no well-defined normal vector
+ for (unsigned int i = 0;i < face.mNumIndices;++i) {
+ pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
+ }
+ continue;
+ }
+
+ const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
+ const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
+ const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
+ const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
+
+ for (unsigned int i = 0;i < face.mNumIndices;++i) {
+ pMesh->mNormals[face.mIndices[i]] = vNor;
+ }
+ }
+ return true;
+}
diff --git a/thirdparty/assimp/code/GenFaceNormalsProcess.h b/thirdparty/assimp/code/GenFaceNormalsProcess.h
new file mode 100644
index 0000000000..c80ec9fddc
--- /dev/null
+++ b/thirdparty/assimp/code/GenFaceNormalsProcess.h
@@ -0,0 +1,87 @@
+/*
+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 compute face normals for all loaded faces*/
+#ifndef AI_GENFACENORMALPROCESS_H_INC
+#define AI_GENFACENORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The GenFaceNormalsProcess computes face normals for all faces of all meshes
+*/
+class ASSIMP_API_WINONLY GenFaceNormalsProcess : public BaseProcess
+{
+public:
+
+ GenFaceNormalsProcess();
+ ~GenFaceNormalsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+private:
+ bool GenMeshFaceNormals(aiMesh* pcMesh);
+ mutable bool force_ = false;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENFACENORMALPROCESS_H_INC
diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.cpp b/thirdparty/assimp/code/GenVertexNormalsProcess.cpp
new file mode 100644
index 0000000000..3f6c2f86bd
--- /dev/null
+++ b/thirdparty/assimp/code/GenVertexNormalsProcess.cpp
@@ -0,0 +1,239 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to generate face
+* normals for all imported faces.
+*/
+
+
+
+// internal headers
+#include "GenVertexNormalsProcess.h"
+#include "ProcessHelper.h"
+#include <assimp/Exceptional.h>
+#include <assimp/qnan.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+GenVertexNormalsProcess::GenVertexNormalsProcess()
+: configMaxAngle( AI_DEG_TO_RAD( 175.f ) ) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+GenVertexNormalsProcess::~GenVertexNormalsProcess() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
+{
+ force_ = (pFlags & aiProcess_ForceGenNormals) != 0;
+ return (pFlags & aiProcess_GenSmoothNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
+{
+ // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property
+ configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,(ai_real)175.0);
+ configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,(ai_real)175.0),(ai_real)0.0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenVertexNormalsProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("GenVertexNormalsProcess begin");
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
+ throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
+ }
+
+ bool bHas = false;
+ for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+ if(GenMeshVertexNormals( pScene->mMeshes[a],a))
+ bHas = true;
+ }
+
+ if (bHas) {
+ ASSIMP_LOG_INFO("GenVertexNormalsProcess finished. "
+ "Vertex normals have been calculated");
+ } else {
+ ASSIMP_LOG_DEBUG("GenVertexNormalsProcess finished. "
+ "Normals are already there");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex)
+{
+ if (NULL != pMesh->mNormals) {
+ if (force_) delete[] pMesh->mNormals;
+ else return false;
+ }
+
+ // If the mesh consists of lines and/or points but not of
+ // triangles or higher-order polygons the normal vectors
+ // are undefined.
+ if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
+ {
+ ASSIMP_LOG_INFO("Normal vectors are undefined for line and point meshes");
+ return false;
+ }
+
+ // Allocate the array to hold the output normals
+ const float qnan = std::numeric_limits<ai_real>::quiet_NaN();
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+ // Compute per-face normals but store them per-vertex
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ const aiFace& face = pMesh->mFaces[a];
+ if (face.mNumIndices < 3)
+ {
+ // either a point or a line -> no normal vector
+ for (unsigned int i = 0;i < face.mNumIndices;++i) {
+ pMesh->mNormals[face.mIndices[i]] = aiVector3D(qnan);
+ }
+
+ continue;
+ }
+
+ const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
+ const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
+ const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
+ const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe();
+
+ for (unsigned int i = 0;i < face.mNumIndices;++i) {
+ pMesh->mNormals[face.mIndices[i]] = vNor;
+ }
+ }
+
+ // Set up a SpatialSort to quickly find all vertices close to a given position
+ // check whether we can reuse the SpatialSort of a previous step.
+ SpatialSort* vertexFinder = NULL;
+ SpatialSort _vertexFinder;
+ ai_real posEpsilon = ai_real( 1e-5 );
+ if (shared) {
+ std::vector<std::pair<SpatialSort,ai_real> >* avf;
+ shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
+ if (avf)
+ {
+ std::pair<SpatialSort,ai_real>& blubb = avf->operator [] (meshIndex);
+ vertexFinder = &blubb.first;
+ posEpsilon = blubb.second;
+ }
+ }
+ if (!vertexFinder) {
+ _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+ vertexFinder = &_vertexFinder;
+ posEpsilon = ComputePositionEpsilon(pMesh);
+ }
+ std::vector<unsigned int> verticesFound;
+ aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
+
+ if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) {
+ // There is no angle limit. Thus all vertices with positions close
+ // to each other will receive the same vertex normal. This allows us
+ // to optimize the whole algorithm a little bit ...
+ std::vector<bool> abHad(pMesh->mNumVertices,false);
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
+ if (abHad[i]) {
+ continue;
+ }
+
+ // Get all vertices that share this one ...
+ vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound);
+
+ aiVector3D pcNor;
+ for (unsigned int a = 0; a < verticesFound.size(); ++a) {
+ const aiVector3D& v = pMesh->mNormals[verticesFound[a]];
+ if (is_not_qnan(v.x))pcNor += v;
+ }
+ pcNor.NormalizeSafe();
+
+ // Write the smoothed normal back to all affected normals
+ for (unsigned int a = 0; a < verticesFound.size(); ++a)
+ {
+ unsigned int vidx = verticesFound[a];
+ pcNew[vidx] = pcNor;
+ abHad[vidx] = true;
+ }
+ }
+ }
+ // Slower code path if a smooth angle is set. There are many ways to achieve
+ // the effect, this one is the most straightforward one.
+ else {
+ const ai_real fLimit = std::cos(configMaxAngle);
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
+ // Get all vertices that share this one ...
+ vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound);
+
+ aiVector3D vr = pMesh->mNormals[i];
+
+ aiVector3D pcNor;
+ for (unsigned int a = 0; a < verticesFound.size(); ++a) {
+ aiVector3D v = pMesh->mNormals[verticesFound[a]];
+
+ // Check whether the angle between the two normals is not too large.
+ // Skip the angle check on our own normal to avoid false negatives
+ // (v*v is not guaranteed to be 1.0 for all unit vectors v)
+ if (is_not_qnan(v.x) && (verticesFound[a] == i || (v * vr >= fLimit)))
+ pcNor += v;
+ }
+ pcNew[i] = pcNor.NormalizeSafe();
+ }
+ }
+
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = pcNew;
+
+ return true;
+}
diff --git a/thirdparty/assimp/code/GenVertexNormalsProcess.h b/thirdparty/assimp/code/GenVertexNormalsProcess.h
new file mode 100644
index 0000000000..9142ad26f5
--- /dev/null
+++ b/thirdparty/assimp/code/GenVertexNormalsProcess.h
@@ -0,0 +1,115 @@
+/*
+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 compute vertex normals
+ for all loaded vertizes */
+#ifndef AI_GENVERTEXNORMALPROCESS_H_INC
+#define AI_GENVERTEXNORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+
+class GenNormalsTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The GenFaceNormalsProcess computes vertex normals for all vertizes
+*/
+class ASSIMP_API GenVertexNormalsProcess : public BaseProcess
+{
+public:
+
+ GenVertexNormalsProcess();
+ ~GenVertexNormalsProcess();
+
+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.
+ */
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+ // setter for configMaxAngle
+ inline void SetMaxSmoothAngle(ai_real f)
+ {
+ configMaxAngle =f;
+ }
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Computes normals for a specific mesh
+ * @param pcMesh Mesh
+ * @param meshIndex Index of the mesh
+ * @return true if vertex normals have been computed
+ */
+ bool GenMeshVertexNormals (aiMesh* pcMesh, unsigned int meshIndex);
+
+private:
+
+ /** Configuration option: maximum smoothing angle, in radians*/
+ ai_real configMaxAngle;
+ mutable bool force_ = false;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC
diff --git a/thirdparty/assimp/code/Importer.cpp b/thirdparty/assimp/code/Importer.cpp
new file mode 100644
index 0000000000..65b16471cc
--- /dev/null
+++ b/thirdparty/assimp/code/Importer.cpp
@@ -0,0 +1,1171 @@
+/*
+---------------------------------------------------------------------------
+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 "Importer.h"
+#include <assimp/BaseImporter.h>
+#include "BaseProcess.h"
+
+#include "DefaultProgressHandler.h"
+#include <assimp/GenericProperty.h>
+#include "ProcessHelper.h"
+#include "ScenePreprocessor.h"
+#include "ScenePrivate.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 "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;
+ for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
+
+ if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
+ imp = pimpl->mImporter[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];
+ 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;
+}
diff --git a/thirdparty/assimp/code/Importer.h b/thirdparty/assimp/code/Importer.h
new file mode 100644
index 0000000000..a439d99c2f
--- /dev/null
+++ b/thirdparty/assimp/code/Importer.h
@@ -0,0 +1,247 @@
+/*
+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
diff --git a/thirdparty/assimp/code/ImporterRegistry.cpp b/thirdparty/assimp/code/ImporterRegistry.cpp
new file mode 100644
index 0000000000..747815fa6f
--- /dev/null
+++ b/thirdparty/assimp/code/ImporterRegistry.cpp
@@ -0,0 +1,371 @@
+/*
+---------------------------------------------------------------------------
+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 "XFileImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+# include "AMFImporter.hpp"
+#endif
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+# include "3DSLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
+# include "MD3Loader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
+# include "MDLLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
+# include "MD2Loader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
+# include "PlyLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
+# include "ASELoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+# include "ObjFileImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
+# include "HMPLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
+# include "SMDLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
+# include "MDCLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
+# include "MD5Loader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
+# include "STLLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+# include "LWOLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
+# include "DXFLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
+# include "NFFLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
+# include "RawLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
+# include "SIBImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
+# include "OFFLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
+# include "ACLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
+# include "BVHLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
+# include "IRRMeshLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
+# include "IRRLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
+# include "Q3DLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
+# include "B3DImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
+# include "ColladaLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
+# include "TerragenLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
+# include "CSMLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
+# include "UnrealLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
+# include "LWSLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
+# include "OgreImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
+# include "OpenGEXImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
+# include "MS3DLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
+# include "COBLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+# include "BlenderLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
+# include "Q3BSPFileImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
+# include "NDOLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
+# include "Importer/IFC/IFCLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
+# include "XGLLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+# include "FBXImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
+# include "AssbinLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+# include "glTFImporter.h"
+# include "glTF2Importer.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
+# include "C4DImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
+# include "D3MFImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+# include "X3DImporter.hpp"
+#endif
+#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
+# include "MMDImporter.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_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]=NULL;
+ }//for
+}
+
+} // namespace Assimp
diff --git a/thirdparty/assimp/code/ImproveCacheLocality.cpp b/thirdparty/assimp/code/ImproveCacheLocality.cpp
new file mode 100644
index 0000000000..ace9d95ff8
--- /dev/null
+++ b/thirdparty/assimp/code/ImproveCacheLocality.cpp
@@ -0,0 +1,386 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to improve the cache locality of a mesh.
+ * <br>
+ * The algorithm is roughly basing on this paper:
+ * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
+ * .. although overdraw rduction isn't implemented yet ...
+ */
+
+
+
+// internal headers
+#include "ImproveCacheLocality.h"
+#include "VertexTriangleAdjacency.h"
+#include <assimp/StringUtils.h>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <stdio.h>
+#include <stack>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() {
+ configCacheDepth = PP_ICL_PTCACHE_SIZE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ImproveCacheLocalityProcess::~ImproveCacheLocalityProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_ImproveCacheLocality) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration
+void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_PP_ICL_PTCACHE_SIZE controls the target cache size for the optimizer
+ configCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
+{
+ if (!pScene->mNumMeshes) {
+ ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess skipped; there are no meshes");
+ return;
+ }
+
+ ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess begin");
+
+ float out = 0.f;
+ unsigned int numf = 0, numm = 0;
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++){
+ const float res = ProcessMesh( pScene->mMeshes[a],a);
+ if (res) {
+ numf += pScene->mMeshes[a]->mNumFaces;
+ out += res;
+ ++numm;
+ }
+ }
+ if (!DefaultLogger::isNullLogger()) {
+ if (numf > 0) {
+ ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf);
+ }
+ ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. ");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Improves the cache coherency of a specific mesh
+float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum)
+{
+ // TODO: rewrite this to use std::vector or boost::shared_array
+ ai_assert(NULL != pMesh);
+
+ // Check whether the input data is valid
+ // - there must be vertices and faces
+ // - all faces must be triangulated or we can't operate on them
+ if (!pMesh->HasFaces() || !pMesh->HasPositions())
+ return 0.f;
+
+ if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) {
+ ASSIMP_LOG_ERROR("This algorithm works on triangle meshes only");
+ return 0.f;
+ }
+
+ if(pMesh->mNumVertices <= configCacheDepth) {
+ return 0.f;
+ }
+
+ float fACMR = 3.f;
+ const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
+
+ // Input ACMR is for logging purposes only
+ if (!DefaultLogger::isNullLogger()) {
+
+ unsigned int* piFIFOStack = new unsigned int[configCacheDepth];
+ memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int));
+ unsigned int* piCur = piFIFOStack;
+ const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth;
+
+ // count the number of cache misses
+ unsigned int iCacheMisses = 0;
+ for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace) {
+
+ for (unsigned int qq = 0; qq < 3;++qq) {
+ bool bInCache = false;
+
+ for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp) {
+ if (*pp == pcFace->mIndices[qq]) {
+ // the vertex is in cache
+ bInCache = true;
+ break;
+ }
+ }
+ if (!bInCache) {
+ ++iCacheMisses;
+ if (piCurEnd == piCur) {
+ piCur = piFIFOStack;
+ }
+ *piCur++ = pcFace->mIndices[qq];
+ }
+ }
+ }
+ delete[] piFIFOStack;
+ fACMR = (float)iCacheMisses / pMesh->mNumFaces;
+ if (3.0 == fACMR) {
+ char szBuff[128]; // should be sufficiently large in every case
+
+ // the JoinIdenticalVertices process has not been executed on this
+ // mesh, otherwise this value would normally be at least minimally
+ // smaller than 3.0 ...
+ ai_snprintf(szBuff,128,"Mesh %u: Not suitable for vcache optimization",meshNum);
+ ASSIMP_LOG_WARN(szBuff);
+ return 0.f;
+ }
+ }
+
+ // first we need to build a vertex-triangle adjacency list
+ VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true);
+
+ // build a list to store per-vertex caching time stamps
+ unsigned int* const piCachingStamps = new unsigned int[pMesh->mNumVertices];
+ memset(piCachingStamps,0x0,pMesh->mNumVertices*sizeof(unsigned int));
+
+ // allocate an empty output index buffer. We store the output indices in one large array.
+ // Since the number of triangles won't change the input faces can be reused. This is how
+ // we save thousands of redundant mini allocations for aiFace::mIndices
+ const unsigned int iIdxCnt = pMesh->mNumFaces*3;
+ unsigned int* const piIBOutput = new unsigned int[iIdxCnt];
+ unsigned int* piCSIter = piIBOutput;
+
+ // allocate the flag array to hold the information
+ // whether a face has already been emitted or not
+ std::vector<bool> abEmitted(pMesh->mNumFaces,false);
+
+ // dead-end vertex index stack
+ std::stack<unsigned int, std::vector<unsigned int> > sDeadEndVStack;
+
+ // create a copy of the piNumTriPtr buffer
+ unsigned int* const piNumTriPtr = adj.mLiveTriangles;
+ const std::vector<unsigned int> piNumTriPtrNoModify(piNumTriPtr, piNumTriPtr + pMesh->mNumVertices);
+
+ // get the largest number of referenced triangles and allocate the "candidate buffer"
+ unsigned int iMaxRefTris = 0; {
+ const unsigned int* piCur = adj.mLiveTriangles;
+ const unsigned int* const piCurEnd = adj.mLiveTriangles+pMesh->mNumVertices;
+ for (;piCur != piCurEnd;++piCur) {
+ iMaxRefTris = std::max(iMaxRefTris,*piCur);
+ }
+ }
+ ai_assert(iMaxRefTris > 0);
+ unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
+ unsigned int iCacheMisses = 0;
+
+ // ...................................................................................
+ /** PSEUDOCODE for the algorithm
+
+ A = Build-Adjacency(I) Vertex-triangle adjacency
+ L = Get-Triangle-Counts(A) Per-vertex live triangle counts
+ C = Zero(Vertex-Count(I)) Per-vertex caching time stamps
+ D = Empty-Stack() Dead-end vertex stack
+ E = False(Triangle-Count(I)) Per triangle emitted flag
+ O = Empty-Index-Buffer() Empty output buffer
+ f = 0 Arbitrary starting vertex
+ s = k+1, i = 1 Time stamp and cursor
+ while f >= 0 For all valid fanning vertices
+ N = Empty-Set() 1-ring of next candidates
+ for each Triangle t in Neighbors(A, f)
+ if !Emitted(E,t)
+ for each Vertex v in t
+ Append(O,v) Output vertex
+ Push(D,v) Add to dead-end stack
+ Insert(N,v) Register as candidate
+ L[v] = L[v]-1 Decrease live triangle count
+ if s-C[v] > k If not in cache
+ C[v] = s Set time stamp
+ s = s+1 Increment time stamp
+ E[t] = true Flag triangle as emitted
+ Select next fanning vertex
+ f = Get-Next-Vertex(I,i,k,N,C,s,L,D)
+ return O
+ */
+ // ...................................................................................
+
+ int ivdx = 0;
+ int ics = 1;
+ int iStampCnt = configCacheDepth+1;
+ while (ivdx >= 0) {
+
+ unsigned int icnt = piNumTriPtrNoModify[ivdx];
+ unsigned int* piList = adj.GetAdjacentTriangles(ivdx);
+ unsigned int* piCurCandidate = piCandidates;
+
+ // get all triangles in the neighborhood
+ for (unsigned int tri = 0; tri < icnt;++tri) {
+
+ // if they have not yet been emitted, add them to the output IB
+ const unsigned int fidx = *piList++;
+ if (!abEmitted[fidx]) {
+
+ // so iterate through all vertices of the current triangle
+ const aiFace* pcFace = &pMesh->mFaces[ fidx ];
+ unsigned nind = pcFace->mNumIndices;
+ for (unsigned ind = 0; ind < nind; ind++) {
+ unsigned dp = pcFace->mIndices[ind];
+
+ // the current vertex won't have any free triangles after this step
+ if (ivdx != (int)dp) {
+ // append the vertex to the dead-end stack
+ sDeadEndVStack.push(dp);
+
+ // register as candidate for the next step
+ *piCurCandidate++ = dp;
+
+ // decrease the per-vertex triangle counts
+ piNumTriPtr[dp]--;
+ }
+
+ // append the vertex to the output index buffer
+ *piCSIter++ = dp;
+
+ // if the vertex is not yet in cache, set its cache count
+ if (iStampCnt-piCachingStamps[dp] > configCacheDepth) {
+ piCachingStamps[dp] = iStampCnt++;
+ ++iCacheMisses;
+ }
+ }
+ // flag triangle as emitted
+ abEmitted[fidx] = true;
+ }
+ }
+
+ // the vertex has now no living adjacent triangles anymore
+ piNumTriPtr[ivdx] = 0;
+
+ // get next fanning vertex
+ ivdx = -1;
+ int max_priority = -1;
+ for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) {
+ const unsigned int dp = *piCur;
+
+ // must have live triangles
+ if (piNumTriPtr[dp] > 0) {
+ int priority = 0;
+
+ // will the vertex be in cache, even after fanning occurs?
+ unsigned int tmp;
+ if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth) {
+ priority = tmp;
+ }
+
+ // keep best candidate
+ if (priority > max_priority) {
+ max_priority = priority;
+ ivdx = dp;
+ }
+ }
+ }
+ // did we reach a dead end?
+ if (-1 == ivdx) {
+ // need to get a non-local vertex for which we have a good chance that it is still
+ // in the cache ...
+ while (!sDeadEndVStack.empty()) {
+ unsigned int iCachedIdx = sDeadEndVStack.top();
+ sDeadEndVStack.pop();
+ if (piNumTriPtr[ iCachedIdx ] > 0) {
+ ivdx = iCachedIdx;
+ break;
+ }
+ }
+
+ if (-1 == ivdx) {
+ // well, there isn't such a vertex. Simply get the next vertex in input order and
+ // hope it is not too bad ...
+ while (ics < (int)pMesh->mNumVertices) {
+ ++ics;
+ if (piNumTriPtr[ics] > 0) {
+ ivdx = ics;
+ break;
+ }
+ }
+ }
+ }
+ }
+ float fACMR2 = 0.0f;
+ if (!DefaultLogger::isNullLogger()) {
+ fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
+
+ // very intense verbose logging ... prepare for much text if there are many meshes
+ if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
+ ASSIMP_LOG_DEBUG_F("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f);
+ }
+
+ fACMR2 *= pMesh->mNumFaces;
+ }
+ // sort the output index buffer back to the input array
+ piCSIter = piIBOutput;
+ for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) {
+ unsigned nind = pcFace->mNumIndices;
+ unsigned * ind = pcFace->mIndices;
+ if (nind > 0) ind[0] = *piCSIter++;
+ if (nind > 1) ind[1] = *piCSIter++;
+ if (nind > 2) ind[2] = *piCSIter++;
+ }
+
+ // delete temporary storage
+ delete[] piCachingStamps;
+ delete[] piIBOutput;
+ delete[] piCandidates;
+
+ return fACMR2;
+}
diff --git a/thirdparty/assimp/code/ImproveCacheLocality.h b/thirdparty/assimp/code/ImproveCacheLocality.h
new file mode 100644
index 0000000000..1b29ee0d6e
--- /dev/null
+++ b/thirdparty/assimp/code/ImproveCacheLocality.h
@@ -0,0 +1,100 @@
+/*
+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 reorder faces for
+ better cache locality*/
+#ifndef AI_IMPROVECACHELOCALITY_H_INC
+#define AI_IMPROVECACHELOCALITY_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/types.h>
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The ImproveCacheLocalityProcess reorders all faces for improved vertex
+ * cache locality. It tries to arrange all faces to fans and to render
+ * faces which share vertices directly one after the other.
+ *
+ * @note This step expects triagulated input data.
+ */
+class ImproveCacheLocalityProcess : public BaseProcess
+{
+public:
+
+ ImproveCacheLocalityProcess();
+ ~ImproveCacheLocalityProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ // Check whether the pp step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Executes the pp step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Configures the pp step
+ void SetupProperties(const Importer* pImp);
+
+protected:
+ // -------------------------------------------------------------------
+ /** Executes the postprocessing step on the given mesh
+ * @param pMesh The mesh to process.
+ * @param meshNum Index of the mesh to process
+ */
+ float ProcessMesh( aiMesh* pMesh, unsigned int meshNum);
+
+private:
+ //! Configuration parameter: specifies the size of the cache to
+ //! optimize the vertex data for.
+ unsigned int configCacheDepth;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_IMPROVECACHELOCALITY_H_INC
diff --git a/thirdparty/assimp/code/JoinVerticesProcess.cpp b/thirdparty/assimp/code/JoinVerticesProcess.cpp
new file mode 100644
index 0000000000..914ec05b46
--- /dev/null
+++ b/thirdparty/assimp/code/JoinVerticesProcess.cpp
@@ -0,0 +1,463 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to join identical vertices
+ * for all imported meshes
+ */
+
+
+#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
+
+#include "JoinVerticesProcess.h"
+#include "ProcessHelper.h"
+#include <assimp/Vertex.h>
+#include <assimp/TinyFormatter.h>
+#include <stdio.h>
+#include <unordered_set>
+
+using namespace Assimp;
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+JoinVerticesProcess::JoinVerticesProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+JoinVerticesProcess::~JoinVerticesProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
+}
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void JoinVerticesProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("JoinVerticesProcess begin");
+
+ // get the total number of vertices BEFORE the step is executed
+ int iNumOldVertices = 0;
+ if (!DefaultLogger::isNullLogger()) {
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ iNumOldVertices += pScene->mMeshes[a]->mNumVertices;
+ }
+ }
+
+ // execute the step
+ int iNumVertices = 0;
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
+
+ // if logging is active, print detailed statistics
+ if (!DefaultLogger::isNullLogger()) {
+ if (iNumOldVertices == iNumVertices) {
+ ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
+ } else {
+ ASSIMP_LOG_INFO_F("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
+ " out: ", iNumVertices, " | ~",
+ ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
+ }
+ }
+
+ pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+}
+
+namespace {
+
+bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex)
+{
+ // A little helper to find locally close vertices faster.
+ // Try to reuse the lookup table from the last step.
+ const static float epsilon = 1e-5f;
+ // Squared because we check against squared length of the vector difference
+ static const float squareEpsilon = epsilon * epsilon;
+
+ // Square compare is useful for animeshes vertices compare
+ if ((lhs.position - rhs.position).SquareLength() > squareEpsilon) {
+ return false;
+ }
+
+ // We just test the other attributes even if they're not present in the mesh.
+ // In this case they're initialized to 0 so the comparison succeeds.
+ // By this method the non-present attributes are effectively ignored in the comparison.
+ if ((lhs.normal - rhs.normal).SquareLength() > squareEpsilon) {
+ return false;
+ }
+
+ if ((lhs.texcoords[0] - rhs.texcoords[0]).SquareLength() > squareEpsilon) {
+ return false;
+ }
+
+ if ((lhs.tangent - rhs.tangent).SquareLength() > squareEpsilon) {
+ return false;
+ }
+
+ if ((lhs.bitangent - rhs.bitangent).SquareLength() > squareEpsilon) {
+ return false;
+ }
+
+ // Usually we won't have vertex colors or multiple UVs, so we can skip from here
+ // Actually this increases runtime performance slightly, at least if branch
+ // prediction is on our side.
+ if (complex) {
+ for (int i = 0; i < 8; i++) {
+ if (i > 0 && (lhs.texcoords[i] - rhs.texcoords[i]).SquareLength() > squareEpsilon) {
+ return false;
+ }
+ if (GetColorDifference(lhs.colors[i], rhs.colors[i]) > squareEpsilon) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+template<class XMesh>
+void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
+ // replace vertex data with the unique data sets
+ pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
+
+ // ----------------------------------------------------------------------------
+ // NOTE - we're *not* calling Vertex::SortBack() because it would check for
+ // presence of every single vertex component once PER VERTEX. And our CPU
+ // dislikes branches, even if they're easily predictable.
+ // ----------------------------------------------------------------------------
+
+ // Position, if present (check made for aiAnimMesh)
+ if (pMesh->mVertices)
+ {
+ delete [] pMesh->mVertices;
+ pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ pMesh->mVertices[a] = uniqueVertices[a].position;
+ }
+ }
+
+ // Normals, if present
+ if (pMesh->mNormals)
+ {
+ delete [] pMesh->mNormals;
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ pMesh->mNormals[a] = uniqueVertices[a].normal;
+ }
+ }
+ // Tangents, if present
+ if (pMesh->mTangents)
+ {
+ delete [] pMesh->mTangents;
+ pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
+ for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ pMesh->mTangents[a] = uniqueVertices[a].tangent;
+ }
+ }
+ // Bitangents as well
+ if (pMesh->mBitangents)
+ {
+ delete [] pMesh->mBitangents;
+ pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
+ for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
+ }
+ }
+ // Vertex colors
+ for (unsigned int a = 0; pMesh->HasVertexColors(a); a++)
+ {
+ delete [] pMesh->mColors[a];
+ pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
+ for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
+ pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
+ }
+ }
+ // Texture coords
+ for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
+ {
+ delete [] pMesh->mTextureCoords[a];
+ pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
+ for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
+ pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
+ }
+ }
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+// Unites identical vertices in the given mesh
+int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
+{
+ static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8");
+ static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8");
+
+ // Return early if we don't have any positions
+ if (!pMesh->HasPositions() || !pMesh->HasFaces()) {
+ return 0;
+ }
+
+ // We should care only about used vertices, not all of them
+ // (this can happen due to original file vertices buffer being used by
+ // multiple meshes)
+ std::unordered_set<unsigned int> usedVertexIndices;
+ usedVertexIndices.reserve(pMesh->mNumVertices);
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ aiFace& face = pMesh->mFaces[a];
+ for( unsigned int b = 0; b < face.mNumIndices; b++) {
+ usedVertexIndices.insert(face.mIndices[b]);
+ }
+ }
+
+ // We'll never have more vertices afterwards.
+ std::vector<Vertex> uniqueVertices;
+ uniqueVertices.reserve( pMesh->mNumVertices);
+
+ // For each vertex the index of the vertex it was replaced by.
+ // Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark
+ // whether a new vertex was created for the index (true) or if it was replaced by an existing
+ // unique vertex (false). This saves an additional std::vector<bool> and greatly enhances
+ // branching performance.
+ static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff");
+ std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
+
+ // float posEpsilonSqr;
+ SpatialSort* vertexFinder = NULL;
+ SpatialSort _vertexFinder;
+
+ typedef std::pair<SpatialSort,float> SpatPair;
+ if (shared) {
+ std::vector<SpatPair >* avf;
+ shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
+ if (avf) {
+ SpatPair& blubb = (*avf)[meshIndex];
+ vertexFinder = &blubb.first;
+ // posEpsilonSqr = blubb.second;
+ }
+ }
+ if (!vertexFinder) {
+ // bad, need to compute it.
+ _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+ vertexFinder = &_vertexFinder;
+ // posEpsilonSqr = ComputePositionEpsilon(pMesh);
+ }
+
+ // Again, better waste some bytes than a realloc ...
+ std::vector<unsigned int> verticesFound;
+ verticesFound.reserve(10);
+
+ // Run an optimized code path if we don't have multiple UVs or vertex colors.
+ // This should yield false in more than 99% of all imports ...
+ const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
+ const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
+
+ // We'll never have more vertices afterwards.
+ std::vector<std::vector<Vertex>> uniqueAnimatedVertices;
+ if (hasAnimMeshes) {
+ uniqueAnimatedVertices.resize(pMesh->mNumAnimMeshes);
+ for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
+ uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
+ }
+ }
+
+ // Now check each vertex if it brings something new to the table
+ for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
+ continue;
+ }
+
+ // collect the vertex data
+ Vertex v(pMesh,a);
+
+ // collect all vertices that are close enough to the given position
+ vertexFinder->FindIdenticalPositions( v.position, verticesFound);
+ unsigned int matchIndex = 0xffffffff;
+
+ // check all unique vertices close to the position if this vertex is already present among them
+ for( unsigned int b = 0; b < verticesFound.size(); b++) {
+ const unsigned int vidx = verticesFound[b];
+ const unsigned int uidx = replaceIndex[ vidx];
+ if( uidx & 0x80000000)
+ continue;
+
+ const Vertex& uv = uniqueVertices[ uidx];
+
+ if (!areVerticesEqual(v, uv, complex)) {
+ continue;
+ }
+
+ if (hasAnimMeshes) {
+ // If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology)
+ // NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces)
+ bool breaksAnimMesh = false;
+ for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
+ const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx];
+ Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
+ if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) {
+ breaksAnimMesh = true;
+ break;
+ }
+ }
+ if (breaksAnimMesh) {
+ continue;
+ }
+ }
+
+ // we're still here -> this vertex perfectly matches our given vertex
+ matchIndex = uidx;
+ break;
+ }
+
+ // found a replacement vertex among the uniques?
+ if( matchIndex != 0xffffffff)
+ {
+ // store where to found the matching unique vertex
+ replaceIndex[a] = matchIndex | 0x80000000;
+ }
+ else
+ {
+ // no unique vertex matches it up to now -> so add it
+ replaceIndex[a] = (unsigned int)uniqueVertices.size();
+ uniqueVertices.push_back( v);
+ if (hasAnimMeshes) {
+ for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
+ Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
+ uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex);
+ }
+ }
+ }
+ }
+
+ if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
+ ASSIMP_LOG_DEBUG_F(
+ "Mesh ",meshIndex,
+ " (",
+ (pMesh->mName.length ? pMesh->mName.data : "unnamed"),
+ ") | Verts in: ",pMesh->mNumVertices,
+ " out: ",
+ uniqueVertices.size(),
+ " | ~",
+ ((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f,
+ "%"
+ );
+ }
+
+ updateXMeshVertices(pMesh, uniqueVertices);
+ if (hasAnimMeshes) {
+ for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
+ updateXMeshVertices(pMesh->mAnimMeshes[animMeshIndex], uniqueAnimatedVertices[animMeshIndex]);
+ }
+ }
+
+ // adjust the indices in all faces
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ aiFace& face = pMesh->mFaces[a];
+ for( unsigned int b = 0; b < face.mNumIndices; b++) {
+ face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;
+ }
+ }
+
+ // adjust bone vertex weights.
+ for( int a = 0; a < (int)pMesh->mNumBones; a++) {
+ aiBone* bone = pMesh->mBones[a];
+ std::vector<aiVertexWeight> newWeights;
+ newWeights.reserve( bone->mNumWeights);
+
+ if ( NULL != bone->mWeights ) {
+ for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) {
+ const aiVertexWeight& ow = bone->mWeights[ b ];
+ // if the vertex is a unique one, translate it
+ if ( !( replaceIndex[ ow.mVertexId ] & 0x80000000 ) ) {
+ aiVertexWeight nw;
+ nw.mVertexId = replaceIndex[ ow.mVertexId ];
+ nw.mWeight = ow.mWeight;
+ newWeights.push_back( nw );
+ }
+ }
+ } else {
+ ASSIMP_LOG_ERROR( "X-Export: aiBone shall contain weights, but pointer to them is NULL." );
+ }
+
+ if (newWeights.size() > 0) {
+ // kill the old and replace them with the translated weights
+ delete [] bone->mWeights;
+ bone->mNumWeights = (unsigned int)newWeights.size();
+
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+ memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
+ }
+ else {
+
+ /* NOTE:
+ *
+ * In the algorithm above we're assuming that there are no vertices
+ * with a different bone weight setup at the same position. That wouldn't
+ * make sense, but it is not absolutely impossible. SkeletonMeshBuilder
+ * for example generates such input data if two skeleton points
+ * share the same position. Again this doesn't make sense but is
+ * reality for some model formats (MD5 for example uses these special
+ * nodes as attachment tags for its weapons).
+ *
+ * Then it is possible that a bone has no weights anymore .... as a quick
+ * workaround, we're just removing these bones. If they're animated,
+ * model geometry might be modified but at least there's no risk of a crash.
+ */
+ delete bone;
+ --pMesh->mNumBones;
+ for (unsigned int n = a; n < pMesh->mNumBones; ++n) {
+ pMesh->mBones[n] = pMesh->mBones[n+1];
+ }
+
+ --a;
+ ASSIMP_LOG_WARN("Removing bone -> no weights remaining");
+ }
+ }
+ return pMesh->mNumVertices;
+}
+
+#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
diff --git a/thirdparty/assimp/code/JoinVerticesProcess.h b/thirdparty/assimp/code/JoinVerticesProcess.h
new file mode 100644
index 0000000000..66fa362de2
--- /dev/null
+++ b/thirdparty/assimp/code/JoinVerticesProcess.h
@@ -0,0 +1,99 @@
+/*
+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 join identical vertices
+ on all imported meshes.*/
+#ifndef AI_JOINVERTICESPROCESS_H_INC
+#define AI_JOINVERTICESPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/types.h>
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The JoinVerticesProcess unites identical vertices in all imported meshes.
+ * By default the importer returns meshes where each face addressed its own
+ * set of vertices even if that means that identical vertices are stored multiple
+ * times. The JoinVerticesProcess finds these identical vertices and
+ * erases all but one of the copies. This usually reduces the number of vertices
+ * in a mesh by a serious amount and is the standard form to render a mesh.
+ */
+class ASSIMP_API JoinVerticesProcess : public BaseProcess
+{
+public:
+ JoinVerticesProcess();
+ ~JoinVerticesProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+public:
+ // -------------------------------------------------------------------
+ /** Unites identical vertices in the given mesh.
+ * @param pMesh The mesh to process.
+ * @param meshIndex Index of the mesh to process
+ */
+ int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
+
+private:
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC
diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp b/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp
new file mode 100644
index 0000000000..d560f19287
--- /dev/null
+++ b/thirdparty/assimp/code/LimitBoneWeightsProcess.cpp
@@ -0,0 +1,201 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+/** Implementation of the LimitBoneWeightsProcess post processing step */
+
+
+#include "LimitBoneWeightsProcess.h"
+#include <assimp/StringUtils.h>
+#include <assimp/postprocess.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/scene.h>
+#include <stdio.h>
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+LimitBoneWeightsProcess::LimitBoneWeightsProcess()
+{
+ mMaxWeights = AI_LMW_MAX_WEIGHTS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+LimitBoneWeightsProcess::~LimitBoneWeightsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_LimitBoneWeights) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void LimitBoneWeightsProcess::Execute( aiScene* pScene) {
+ ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
+ for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
+ ProcessMesh(pScene->mMeshes[a]);
+ }
+
+ ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
+{
+ // get the current value of the property
+ this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unites identical vertices in the given mesh
+void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
+{
+ if( !pMesh->HasBones())
+ return;
+
+ // collect all bone weights per vertex
+ typedef std::vector< std::vector< Weight > > WeightsPerVertex;
+ WeightsPerVertex vertexWeights( pMesh->mNumVertices);
+
+ // collect all weights per vertex
+ for( unsigned int a = 0; a < pMesh->mNumBones; a++)
+ {
+ const aiBone* bone = pMesh->mBones[a];
+ for( unsigned int b = 0; b < bone->mNumWeights; b++)
+ {
+ const aiVertexWeight& w = bone->mWeights[b];
+ vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
+ }
+ }
+
+ unsigned int removed = 0, old_bones = pMesh->mNumBones;
+
+ // now cut the weight count if it exceeds the maximum
+ bool bChanged = false;
+ for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
+ {
+ if( vit->size() <= mMaxWeights)
+ continue;
+
+ bChanged = true;
+
+ // more than the defined maximum -> first sort by weight in descending order. That's
+ // why we defined the < operator in such a weird way.
+ std::sort( vit->begin(), vit->end());
+
+ // now kill everything beyond the maximum count
+ unsigned int m = static_cast<unsigned int>(vit->size());
+ vit->erase( vit->begin() + mMaxWeights, vit->end());
+ removed += static_cast<unsigned int>(m-vit->size());
+
+ // and renormalize the weights
+ float sum = 0.0f;
+ for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it ) {
+ sum += it->mWeight;
+ }
+ if( 0.0f != sum ) {
+ const float invSum = 1.0f / sum;
+ for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it ) {
+ it->mWeight *= invSum;
+ }
+ }
+ }
+
+ if (bChanged) {
+ // rebuild the vertex weight array for all bones
+ typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
+ WeightsPerBone boneWeights( pMesh->mNumBones);
+ for( unsigned int a = 0; a < vertexWeights.size(); a++)
+ {
+ const std::vector<Weight>& vw = vertexWeights[a];
+ for( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it)
+ boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight));
+ }
+
+ // and finally copy the vertex weight list over to the mesh's bones
+ std::vector<bool> abNoNeed(pMesh->mNumBones,false);
+ bChanged = false;
+
+ for( unsigned int a = 0; a < pMesh->mNumBones; a++)
+ {
+ const std::vector<aiVertexWeight>& bw = boneWeights[a];
+ aiBone* bone = pMesh->mBones[a];
+
+ if ( bw.empty() )
+ {
+ abNoNeed[a] = bChanged = true;
+ continue;
+ }
+
+ // copy the weight list. should always be less weights than before, so we don't need a new allocation
+ ai_assert( bw.size() <= bone->mNumWeights);
+ bone->mNumWeights = static_cast<unsigned int>( bw.size() );
+ ::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
+ }
+
+ if (bChanged) {
+ // the number of new bones is smaller than before, so we can reuse the old array
+ aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur;
+
+ for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) {
+ if (*iter) {
+ delete *ppcSrc;
+ --pMesh->mNumBones;
+ }
+ else *ppcCur++ = *ppcSrc;
+ ++ppcSrc;
+ }
+ }
+
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones );
+ }
+ }
+}
diff --git a/thirdparty/assimp/code/LimitBoneWeightsProcess.h b/thirdparty/assimp/code/LimitBoneWeightsProcess.h
new file mode 100644
index 0000000000..3602fd8edf
--- /dev/null
+++ b/thirdparty/assimp/code/LimitBoneWeightsProcess.h
@@ -0,0 +1,148 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+/** Defines a post processing step to limit the number of bones affecting a single vertex. */
+#ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
+#define AI_LIMITBONEWEIGHTSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+class LimitBoneWeightsTest;
+
+namespace Assimp
+{
+
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
+
+// **********************************************************
+// Java: ConfigProperty.java,
+// ConfigProperty.DEFAULT_BONE_WEIGHT_LIMIT
+// **********************************************************
+
+#if (!defined AI_LMW_MAX_WEIGHTS)
+# define AI_LMW_MAX_WEIGHTS 0x4
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+// ---------------------------------------------------------------------------
+/** This post processing step limits the number of bones affecting a vertex
+* to a certain maximum value. If a vertex is affected by more than that number
+* of bones, the bone weight with the least influence on this vertex are removed.
+* The other weights on this bone are then renormalized to assure the sum weight
+* to be 1.
+*/
+class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess
+{
+public:
+
+ LimitBoneWeightsProcess();
+ ~LimitBoneWeightsProcess();
+
+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.
+ */
+ void SetupProperties(const Importer* pImp);
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Limits the bone weight count for all vertices in the given mesh.
+ * @param pMesh The mesh to process.
+ */
+ void ProcessMesh( aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Describes a bone weight on a vertex */
+ struct Weight
+ {
+ unsigned int mBone; ///< Index of the bone
+ float mWeight; ///< Weight of that bone on this vertex
+ Weight() AI_NO_EXCEPT
+ : mBone(0)
+ , mWeight(0.0f)
+ { }
+
+ Weight( unsigned int pBone, float pWeight)
+ {
+ mBone = pBone;
+ mWeight = pWeight;
+ }
+
+ /** Comparison operator to sort bone weights by descending weight */
+ bool operator < (const Weight& pWeight) const
+ {
+ return mWeight > pWeight.mWeight;
+ }
+ };
+
+public:
+ /** Maximum number of bones influencing any single vertex. */
+ unsigned int mMaxWeights;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC
diff --git a/thirdparty/assimp/code/MMDCpp14.h b/thirdparty/assimp/code/MMDCpp14.h
new file mode 100644
index 0000000000..638b0bfd2f
--- /dev/null
+++ b/thirdparty/assimp/code/MMDCpp14.h
@@ -0,0 +1,83 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#ifndef MMD_CPP14_H
+#define MMD_CPP14_H
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace mmd {
+ template<class T> struct _Unique_if {
+ typedef std::unique_ptr<T> _Single_object;
+ };
+
+ template<class T> struct _Unique_if<T[]> {
+ typedef std::unique_ptr<T[]> _Unknown_bound;
+ };
+
+ template<class T, size_t N> struct _Unique_if<T[N]> {
+ typedef void _Known_bound;
+ };
+
+ template<class T, class... Args>
+ typename _Unique_if<T>::_Single_object
+ make_unique(Args&&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+
+ template<class T>
+ typename _Unique_if<T>::_Unknown_bound
+ make_unique(size_t n) {
+ typedef typename std::remove_extent<T>::type U;
+ return std::unique_ptr<T>(new U[n]());
+ }
+
+ template<class T, class... Args>
+ typename _Unique_if<T>::_Known_bound
+ make_unique(Args&&...) = delete;
+}
+
+#endif
diff --git a/thirdparty/assimp/code/MMDImporter.cpp b/thirdparty/assimp/code/MMDImporter.cpp
new file mode 100644
index 0000000000..84b9e35a6b
--- /dev/null
+++ b/thirdparty/assimp/code/MMDImporter.cpp
@@ -0,0 +1,370 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
+
+#include "MMDImporter.h"
+#include "MMDPmdParser.h"
+#include "MMDPmxParser.h"
+#include "MMDVmdParser.h"
+#include "ConvertToLHProcess.h"
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/Importer.hpp>
+#include <assimp/ai_assert.h>
+#include <assimp/scene.h>
+#include <fstream>
+#include <iomanip>
+#include <memory>
+
+static const aiImporterDesc desc = {"MMD Importer",
+ "",
+ "",
+ "surfaces supported?",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "pmx"};
+
+namespace Assimp {
+
+using namespace std;
+
+// ------------------------------------------------------------------------------------------------
+// Default constructor
+MMDImporter::MMDImporter()
+: m_Buffer()
+, m_strAbsPath("") {
+ DefaultIOSystem io;
+ m_strAbsPath = io.getOsSeparator();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+MMDImporter::~MMDImporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if file is an pmx file.
+bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler,
+ bool checkSig) const {
+ if (!checkSig) // Check File Extension
+ {
+ return SimpleExtensionCheck(pFile, "pmx");
+ } else // Check file Header
+ {
+ static const char *pTokens[] = {"PMX "};
+ return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc *MMDImporter::GetInfo() const { return &desc; }
+
+// ------------------------------------------------------------------------------------------------
+// MMD import implementation
+void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
+ IOSystem * /*pIOHandler*/) {
+ // Read file by istream
+ std::filebuf fb;
+ if (!fb.open(file, std::ios::in | std::ios::binary)) {
+ throw DeadlyImportError("Failed to open file " + file + ".");
+ }
+
+ std::istream fileStream(&fb);
+
+ // Get the file-size and validate it, throwing an exception when fails
+ fileStream.seekg(0, fileStream.end);
+ size_t fileSize = static_cast<size_t>(fileStream.tellg());
+ fileStream.seekg(0, fileStream.beg);
+
+ if (fileSize < sizeof(pmx::PmxModel)) {
+ throw DeadlyImportError(file + " is too small.");
+ }
+
+ pmx::PmxModel model;
+ model.Read(&fileStream);
+
+ CreateDataFromImport(&model, pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+void MMDImporter::CreateDataFromImport(const pmx::PmxModel *pModel,
+ aiScene *pScene) {
+ if (pModel == NULL) {
+ return;
+ }
+
+ aiNode *pNode = new aiNode;
+ if (!pModel->model_name.empty()) {
+ pNode->mName.Set(pModel->model_name);
+ }
+
+ pScene->mRootNode = pNode;
+
+ pNode = new aiNode;
+ pScene->mRootNode->addChildren(1, &pNode);
+ pNode->mName.Set(string(pModel->model_name) + string("_mesh"));
+
+ // split mesh by materials
+ pNode->mNumMeshes = pModel->material_count;
+ pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
+ for (unsigned int index = 0; index < pNode->mNumMeshes; index++) {
+ pNode->mMeshes[index] = index;
+ }
+
+ pScene->mNumMeshes = pModel->material_count;
+ pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
+ for (unsigned int i = 0, indexStart = 0; i < pScene->mNumMeshes; i++) {
+ const int indexCount = pModel->materials[i].index_count;
+
+ pScene->mMeshes[i] = CreateMesh(pModel, indexStart, indexCount);
+ pScene->mMeshes[i]->mName = pModel->materials[i].material_name;
+ pScene->mMeshes[i]->mMaterialIndex = i;
+ indexStart += indexCount;
+ }
+
+ // create node hierarchy for bone position
+ std::unique_ptr<aiNode *[]> ppNode(new aiNode *[pModel->bone_count]);
+ for (auto i = 0; i < pModel->bone_count; i++) {
+ ppNode[i] = new aiNode(pModel->bones[i].bone_name);
+ }
+
+ for (auto i = 0; i < pModel->bone_count; i++) {
+ const pmx::PmxBone &bone = pModel->bones[i];
+
+ if (bone.parent_index < 0) {
+ pScene->mRootNode->addChildren(1, ppNode.get() + i);
+ } else {
+ ppNode[bone.parent_index]->addChildren(1, ppNode.get() + i);
+
+ aiVector3D v3 = aiVector3D(
+ bone.position[0] - pModel->bones[bone.parent_index].position[0],
+ bone.position[1] - pModel->bones[bone.parent_index].position[1],
+ bone.position[2] - pModel->bones[bone.parent_index].position[2]);
+ aiMatrix4x4::Translation(v3, ppNode[i]->mTransformation);
+ }
+ }
+
+ // create materials
+ pScene->mNumMaterials = pModel->material_count;
+ pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials; i++) {
+ pScene->mMaterials[i] = CreateMaterial(&pModel->materials[i], pModel);
+ }
+
+ // Convert everything to OpenGL space
+ MakeLeftHandedProcess convertProcess;
+ convertProcess.Execute(pScene);
+
+ FlipUVsProcess uvFlipper;
+ uvFlipper.Execute(pScene);
+
+ FlipWindingOrderProcess windingFlipper;
+ windingFlipper.Execute(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMesh *MMDImporter::CreateMesh(const pmx::PmxModel *pModel,
+ const int indexStart, const int indexCount) {
+ aiMesh *pMesh = new aiMesh;
+
+ pMesh->mNumVertices = indexCount;
+
+ pMesh->mNumFaces = indexCount / 3;
+ pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+
+ const int numIndices = 3; // triangular face
+ for (unsigned int index = 0; index < pMesh->mNumFaces; index++) {
+ pMesh->mFaces[index].mNumIndices = numIndices;
+ unsigned int *indices = new unsigned int[numIndices];
+ indices[0] = numIndices * index;
+ indices[1] = numIndices * index + 1;
+ indices[2] = numIndices * index + 2;
+ pMesh->mFaces[index].mIndices = indices;
+ }
+
+ pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mNumUVComponents[0] = 2;
+
+ // additional UVs
+ for (int i = 1; i <= pModel->setting.uv; i++) {
+ pMesh->mTextureCoords[i] = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mNumUVComponents[i] = 4;
+ }
+
+ map<int, vector<aiVertexWeight>> bone_vertex_map;
+
+ // fill in contents and create bones
+ for (int index = 0; index < indexCount; index++) {
+ const pmx::PmxVertex *v =
+ &pModel->vertices[pModel->indices[indexStart + index]];
+ const float *position = v->position;
+ pMesh->mVertices[index].Set(position[0], position[1], position[2]);
+ const float *normal = v->normal;
+
+ pMesh->mNormals[index].Set(normal[0], normal[1], normal[2]);
+ pMesh->mTextureCoords[0][index].x = v->uv[0];
+ pMesh->mTextureCoords[0][index].y = v->uv[1];
+
+ for (int i = 1; i <= pModel->setting.uv; i++) {
+ // TODO: wrong here? use quaternion transform?
+ pMesh->mTextureCoords[i][index].x = v->uva[i][0];
+ pMesh->mTextureCoords[i][index].y = v->uva[i][1];
+ }
+
+ // handle bone map
+ const auto vsBDEF1_ptr =
+ dynamic_cast<pmx::PmxVertexSkinningBDEF1 *>(v->skinning.get());
+ const auto vsBDEF2_ptr =
+ dynamic_cast<pmx::PmxVertexSkinningBDEF2 *>(v->skinning.get());
+ const auto vsBDEF4_ptr =
+ dynamic_cast<pmx::PmxVertexSkinningBDEF4 *>(v->skinning.get());
+ const auto vsSDEF_ptr =
+ dynamic_cast<pmx::PmxVertexSkinningSDEF *>(v->skinning.get());
+ switch (v->skinning_type) {
+ case pmx::PmxVertexSkinningType::BDEF1:
+ bone_vertex_map[vsBDEF1_ptr->bone_index].push_back(
+ aiVertexWeight(index, 1.0));
+ break;
+ case pmx::PmxVertexSkinningType::BDEF2:
+ bone_vertex_map[vsBDEF2_ptr->bone_index1].push_back(
+ aiVertexWeight(index, vsBDEF2_ptr->bone_weight));
+ bone_vertex_map[vsBDEF2_ptr->bone_index2].push_back(
+ aiVertexWeight(index, 1.0f - vsBDEF2_ptr->bone_weight));
+ break;
+ case pmx::PmxVertexSkinningType::BDEF4:
+ bone_vertex_map[vsBDEF4_ptr->bone_index1].push_back(
+ aiVertexWeight(index, vsBDEF4_ptr->bone_weight1));
+ bone_vertex_map[vsBDEF4_ptr->bone_index2].push_back(
+ aiVertexWeight(index, vsBDEF4_ptr->bone_weight2));
+ bone_vertex_map[vsBDEF4_ptr->bone_index3].push_back(
+ aiVertexWeight(index, vsBDEF4_ptr->bone_weight3));
+ bone_vertex_map[vsBDEF4_ptr->bone_index4].push_back(
+ aiVertexWeight(index, vsBDEF4_ptr->bone_weight4));
+ break;
+ case pmx::PmxVertexSkinningType::SDEF: // TODO: how to use sdef_c, sdef_r0,
+ // sdef_r1?
+ bone_vertex_map[vsSDEF_ptr->bone_index1].push_back(
+ aiVertexWeight(index, vsSDEF_ptr->bone_weight));
+ bone_vertex_map[vsSDEF_ptr->bone_index2].push_back(
+ aiVertexWeight(index, 1.0f - vsSDEF_ptr->bone_weight));
+ break;
+ case pmx::PmxVertexSkinningType::QDEF:
+ const auto vsQDEF_ptr =
+ dynamic_cast<pmx::PmxVertexSkinningQDEF *>(v->skinning.get());
+ bone_vertex_map[vsQDEF_ptr->bone_index1].push_back(
+ aiVertexWeight(index, vsQDEF_ptr->bone_weight1));
+ bone_vertex_map[vsQDEF_ptr->bone_index2].push_back(
+ aiVertexWeight(index, vsQDEF_ptr->bone_weight2));
+ bone_vertex_map[vsQDEF_ptr->bone_index3].push_back(
+ aiVertexWeight(index, vsQDEF_ptr->bone_weight3));
+ bone_vertex_map[vsQDEF_ptr->bone_index4].push_back(
+ aiVertexWeight(index, vsQDEF_ptr->bone_weight4));
+ break;
+ }
+ }
+
+ // make all bones for each mesh
+ // assign bone weights to skinned bones (otherwise just initialize)
+ auto bone_ptr_ptr = new aiBone *[pModel->bone_count];
+ pMesh->mNumBones = pModel->bone_count;
+ pMesh->mBones = bone_ptr_ptr;
+ for (auto ii = 0; ii < pModel->bone_count; ++ii) {
+ auto pBone = new aiBone;
+ const auto &pmxBone = pModel->bones[ii];
+ pBone->mName = pmxBone.bone_name;
+ aiVector3D pos(pmxBone.position[0], pmxBone.position[1], pmxBone.position[2]);
+ aiMatrix4x4::Translation(-pos, pBone->mOffsetMatrix);
+ auto it = bone_vertex_map.find(ii);
+ if (it != bone_vertex_map.end()) {
+ pBone->mNumWeights = static_cast<unsigned int>(it->second.size());
+ pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
+ for (unsigned int j = 0; j < pBone->mNumWeights; j++) {
+ pBone->mWeights[j] = it->second[j];
+ }
+ }
+ bone_ptr_ptr[ii] = pBone;
+ }
+
+ return pMesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMaterial *MMDImporter::CreateMaterial(const pmx::PmxMaterial *pMat,
+ const pmx::PmxModel *pModel) {
+ aiMaterial *mat = new aiMaterial();
+ aiString name(pMat->material_english_name);
+ mat->AddProperty(&name, AI_MATKEY_NAME);
+
+ aiColor3D diffuse(pMat->diffuse[0], pMat->diffuse[1], pMat->diffuse[2]);
+ mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ aiColor3D specular(pMat->specular[0], pMat->specular[1], pMat->specular[2]);
+ mat->AddProperty(&specular, 1, AI_MATKEY_COLOR_SPECULAR);
+ aiColor3D ambient(pMat->ambient[0], pMat->ambient[1], pMat->ambient[2]);
+ mat->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT);
+
+ float opacity = pMat->diffuse[3];
+ mat->AddProperty(&opacity, 1, AI_MATKEY_OPACITY);
+ float shininess = pMat->specularlity;
+ mat->AddProperty(&shininess, 1, AI_MATKEY_SHININESS_STRENGTH);
+
+ if(pMat->diffuse_texture_index >= 0) {
+ aiString texture_path(pModel->textures[pMat->diffuse_texture_index]);
+ mat->AddProperty(&texture_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
+ }
+
+ int mapping_uvwsrc = 0;
+ mat->AddProperty(&mapping_uvwsrc, 1,
+ AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE, 0));
+
+ return mat;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER
diff --git a/thirdparty/assimp/code/MMDImporter.h b/thirdparty/assimp/code/MMDImporter.h
new file mode 100644
index 0000000000..4ee94eeb00
--- /dev/null
+++ b/thirdparty/assimp/code/MMDImporter.h
@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2016, 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 MMD_FILE_IMPORTER_H_INC
+#define MMD_FILE_IMPORTER_H_INC
+
+#include <assimp/BaseImporter.h>
+#include "MMDPmxParser.h"
+#include <assimp/material.h>
+#include <vector>
+
+struct aiMesh;
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+/// \class MMDImporter
+/// \brief Imports MMD a pmx/pmd/vmd file
+// ------------------------------------------------------------------------------------------------
+class MMDImporter : public BaseImporter {
+public:
+ /// \brief Default constructor
+ MMDImporter();
+
+ /// \brief Destructor
+ ~MMDImporter();
+
+public:
+ /// \brief Returns whether the class can handle the format of the given file.
+ /// \remark See BaseImporter::CanRead() for details.
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+
+private:
+ //! \brief Appends the supported extension.
+ const aiImporterDesc* GetInfo () const;
+
+ //! \brief File import implementation.
+ void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+ //! \brief Create the data from imported content.
+ void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene);
+
+ //! \brief Create the mesh
+ aiMesh* CreateMesh(const pmx::PmxModel* pModel, const int indexStart, const int indexCount);
+
+ //! \brief Create the material
+ aiMaterial* CreateMaterial(const pmx::PmxMaterial* pMat, const pmx::PmxModel* pModel);
+
+private:
+ //! Data buffer
+ std::vector<char> m_Buffer;
+ //! Absolute pathname of model in file system
+ std::string m_strAbsPath;
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif \ No newline at end of file
diff --git a/thirdparty/assimp/code/MMDPmdParser.h b/thirdparty/assimp/code/MMDPmdParser.h
new file mode 100644
index 0000000000..d2f2224aa1
--- /dev/null
+++ b/thirdparty/assimp/code/MMDPmdParser.h
@@ -0,0 +1,597 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <vector>
+#include <string>
+#include <memory>
+#include <iostream>
+#include <fstream>
+#include "MMDCpp14.h"
+
+namespace pmd
+{
+ class PmdHeader
+ {
+ public:
+ std::string name;
+ std::string name_english;
+ std::string comment;
+ std::string comment_english;
+
+ bool Read(std::ifstream* stream)
+ {
+ char buffer[256];
+ stream->read(buffer, 20);
+ name = std::string(buffer);
+ stream->read(buffer, 256);
+ comment = std::string(buffer);
+ return true;
+ }
+
+ bool ReadExtension(std::ifstream* stream)
+ {
+ char buffer[256];
+ stream->read(buffer, 20);
+ name_english = std::string(buffer);
+ stream->read(buffer, 256);
+ comment_english = std::string(buffer);
+ return true;
+ }
+ };
+
+ class PmdVertex
+ {
+ public:
+ float position[3];
+
+ float normal[3];
+
+ float uv[2];
+
+ uint16_t bone_index[2];
+
+ uint8_t bone_weight;
+
+ bool edge_invisible;
+
+ bool Read(std::ifstream* stream)
+ {
+ stream->read((char*) position, sizeof(float) * 3);
+ stream->read((char*) normal, sizeof(float) * 3);
+ stream->read((char*) uv, sizeof(float) * 2);
+ stream->read((char*) bone_index, sizeof(uint16_t) * 2);
+ stream->read((char*) &bone_weight, sizeof(uint8_t));
+ stream->read((char*) &edge_invisible, sizeof(uint8_t));
+ return true;
+ }
+ };
+
+ class PmdMaterial
+ {
+ public:
+ float diffuse[4];
+ float power;
+ float specular[3];
+ float ambient[3];
+ uint8_t toon_index;
+ uint8_t edge_flag;
+ uint32_t index_count;
+ std::string texture_filename;
+ std::string sphere_filename;
+
+ bool Read(std::ifstream* stream)
+ {
+ char buffer[20];
+ stream->read((char*) &diffuse, sizeof(float) * 4);
+ stream->read((char*) &power, sizeof(float));
+ stream->read((char*) &specular, sizeof(float) * 3);
+ stream->read((char*) &ambient, sizeof(float) * 3);
+ stream->read((char*) &toon_index, sizeof(uint8_t));
+ stream->read((char*) &edge_flag, sizeof(uint8_t));
+ stream->read((char*) &index_count, sizeof(uint32_t));
+ stream->read((char*) &buffer, sizeof(char) * 20);
+ char* pstar = strchr(buffer, '*');
+ if (NULL == pstar)
+ {
+ texture_filename = std::string(buffer);
+ sphere_filename.clear();
+ }
+ else {
+ *pstar = 0;
+ texture_filename = std::string(buffer);
+ sphere_filename = std::string(pstar+1);
+ }
+ return true;
+ }
+ };
+
+ enum class BoneType : uint8_t
+ {
+ Rotation,
+ RotationAndMove,
+ IkEffector,
+ Unknown,
+ IkEffectable,
+ RotationEffectable,
+ IkTarget,
+ Invisible,
+ Twist,
+ RotationMovement
+ };
+
+ class PmdBone
+ {
+ public:
+ std::string name;
+ std::string name_english;
+ uint16_t parent_bone_index;
+ uint16_t tail_pos_bone_index;
+ BoneType bone_type;
+ uint16_t ik_parent_bone_index;
+ float bone_head_pos[3];
+
+ void Read(std::istream *stream)
+ {
+ char buffer[20];
+ stream->read(buffer, 20);
+ name = std::string(buffer);
+ stream->read((char*) &parent_bone_index, sizeof(uint16_t));
+ stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t));
+ stream->read((char*) &bone_type, sizeof(uint8_t));
+ stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t));
+ stream->read((char*) &bone_head_pos, sizeof(float) * 3);
+ }
+
+ void ReadExpantion(std::istream *stream)
+ {
+ char buffer[20];
+ stream->read(buffer, 20);
+ name_english = std::string(buffer);
+ }
+ };
+
+ class PmdIk
+ {
+ public:
+ uint16_t ik_bone_index;
+ uint16_t target_bone_index;
+ uint16_t interations;
+ float angle_limit;
+ std::vector<uint16_t> ik_child_bone_index;
+
+ void Read(std::istream *stream)
+ {
+ stream->read((char *) &ik_bone_index, sizeof(uint16_t));
+ stream->read((char *) &target_bone_index, sizeof(uint16_t));
+ uint8_t ik_chain_length;
+ stream->read((char*) &ik_chain_length, sizeof(uint8_t));
+ stream->read((char *) &interations, sizeof(uint16_t));
+ stream->read((char *) &angle_limit, sizeof(float));
+ ik_child_bone_index.resize(ik_chain_length);
+ for (int i = 0; i < ik_chain_length; i++)
+ {
+ stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t));
+ }
+ }
+ };
+
+ class PmdFaceVertex
+ {
+ public:
+ int vertex_index;
+ float position[3];
+
+ void Read(std::istream *stream)
+ {
+ stream->read((char *) &vertex_index, sizeof(int));
+ stream->read((char *) position, sizeof(float) * 3);
+ }
+ };
+
+ enum class FaceCategory : uint8_t
+ {
+ Base,
+ Eyebrow,
+ Eye,
+ Mouth,
+ Other
+ };
+
+ class PmdFace
+ {
+ public:
+ std::string name;
+ FaceCategory type;
+ std::vector<PmdFaceVertex> vertices;
+ std::string name_english;
+
+ void Read(std::istream *stream)
+ {
+ char buffer[20];
+ stream->read(buffer, 20);
+ name = std::string(buffer);
+ int vertex_count;
+ stream->read((char*) &vertex_count, sizeof(int));
+ stream->read((char*) &type, sizeof(uint8_t));
+ vertices.resize(vertex_count);
+ for (int i = 0; i < vertex_count; i++)
+ {
+ vertices[i].Read(stream);
+ }
+ }
+
+ void ReadExpantion(std::istream *stream)
+ {
+ char buffer[20];
+ stream->read(buffer, 20);
+ name_english = std::string(buffer);
+ }
+ };
+
+ class PmdBoneDispName
+ {
+ public:
+ std::string bone_disp_name;
+ std::string bone_disp_name_english;
+
+ void Read(std::istream *stream)
+ {
+ char buffer[50];
+ stream->read(buffer, 50);
+ bone_disp_name = std::string(buffer);
+ bone_disp_name_english.clear();
+ }
+ void ReadExpantion(std::istream *stream)
+ {
+ char buffer[50];
+ stream->read(buffer, 50);
+ bone_disp_name_english = std::string(buffer);
+ }
+ };
+
+ class PmdBoneDisp
+ {
+ public:
+ uint16_t bone_index;
+ uint8_t bone_disp_index;
+
+ void Read(std::istream *stream)
+ {
+ stream->read((char*) &bone_index, sizeof(uint16_t));
+ stream->read((char*) &bone_disp_index, sizeof(uint8_t));
+ }
+ };
+
+ enum class RigidBodyShape : uint8_t
+ {
+ Sphere = 0,
+ Box = 1,
+ Cpusel = 2
+ };
+
+ enum class RigidBodyType : uint8_t
+ {
+ BoneConnected = 0,
+ Physics = 1,
+ ConnectedPhysics = 2
+ };
+
+ class PmdRigidBody
+ {
+ public:
+ std::string name;
+ uint16_t related_bone_index;
+ uint8_t group_index;
+ uint16_t mask;
+ RigidBodyShape shape;
+ float size[3];
+ float position[3];
+ float orientation[3];
+ float weight;
+ float linear_damping;
+ float anglar_damping;
+ float restitution;
+ float friction;
+ RigidBodyType rigid_type;
+
+ void Read(std::istream *stream)
+ {
+ char buffer[20];
+ stream->read(buffer, sizeof(char) * 20);
+ name = (std::string(buffer));
+ stream->read((char*) &related_bone_index, sizeof(uint16_t));
+ stream->read((char*) &group_index, sizeof(uint8_t));
+ stream->read((char*) &mask, sizeof(uint16_t));
+ stream->read((char*) &shape, sizeof(uint8_t));
+ stream->read((char*) size, sizeof(float) * 3);
+ stream->read((char*) position, sizeof(float) * 3);
+ stream->read((char*) orientation, sizeof(float) * 3);
+ stream->read((char*) &weight, sizeof(float));
+ stream->read((char*) &linear_damping, sizeof(float));
+ stream->read((char*) &anglar_damping, sizeof(float));
+ stream->read((char*) &restitution, sizeof(float));
+ stream->read((char*) &friction, sizeof(float));
+ stream->read((char*) &rigid_type, sizeof(char));
+ }
+ };
+
+ class PmdConstraint
+ {
+ public:
+ std::string name;
+ uint32_t rigid_body_index_a;
+ uint32_t rigid_body_index_b;
+ float position[3];
+ float orientation[3];
+ float linear_lower_limit[3];
+ float linear_upper_limit[3];
+ float angular_lower_limit[3];
+ float angular_upper_limit[3];
+ float linear_stiffness[3];
+ float angular_stiffness[3];
+
+ void Read(std::istream *stream)
+ {
+ char buffer[20];
+ stream->read(buffer, 20);
+ name = std::string(buffer);
+ stream->read((char *) &rigid_body_index_a, sizeof(uint32_t));
+ stream->read((char *) &rigid_body_index_b, sizeof(uint32_t));
+ stream->read((char *) position, sizeof(float) * 3);
+ stream->read((char *) orientation, sizeof(float) * 3);
+ stream->read((char *) linear_lower_limit, sizeof(float) * 3);
+ stream->read((char *) linear_upper_limit, sizeof(float) * 3);
+ stream->read((char *) angular_lower_limit, sizeof(float) * 3);
+ stream->read((char *) angular_upper_limit, sizeof(float) * 3);
+ stream->read((char *) linear_stiffness, sizeof(float) * 3);
+ stream->read((char *) angular_stiffness, sizeof(float) * 3);
+ }
+ };
+
+ class PmdModel
+ {
+ public:
+ float version;
+ PmdHeader header;
+ std::vector<PmdVertex> vertices;
+ std::vector<uint16_t> indices;
+ std::vector<PmdMaterial> materials;
+ std::vector<PmdBone> bones;
+ std::vector<PmdIk> iks;
+ std::vector<PmdFace> faces;
+ std::vector<uint16_t> faces_indices;
+ std::vector<PmdBoneDispName> bone_disp_name;
+ std::vector<PmdBoneDisp> bone_disp;
+ std::vector<std::string> toon_filenames;
+ std::vector<PmdRigidBody> rigid_bodies;
+ std::vector<PmdConstraint> constraints;
+
+ static std::unique_ptr<PmdModel> LoadFromFile(const char *filename)
+ {
+ std::ifstream stream(filename, std::ios::binary);
+ if (stream.fail())
+ {
+ std::cerr << "could not open \"" << filename << "\"" << std::endl;
+ return nullptr;
+ }
+ auto result = LoadFromStream(&stream);
+ stream.close();
+ return result;
+ }
+
+ static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream)
+ {
+ auto result = mmd::make_unique<PmdModel>();
+ char buffer[100];
+
+ // magic
+ char magic[3];
+ stream->read(magic, 3);
+ if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd')
+ {
+ std::cerr << "invalid file" << std::endl;
+ return nullptr;
+ }
+
+ // version
+ stream->read((char*) &(result->version), sizeof(float));
+ if (result ->version != 1.0f)
+ {
+ std::cerr << "invalid version" << std::endl;
+ return nullptr;
+ }
+
+ // header
+ result->header.Read(stream);
+
+ // vertices
+ uint32_t vertex_num;
+ stream->read((char*) &vertex_num, sizeof(uint32_t));
+ result->vertices.resize(vertex_num);
+ for (uint32_t i = 0; i < vertex_num; i++)
+ {
+ result->vertices[i].Read(stream);
+ }
+
+ // indices
+ uint32_t index_num;
+ stream->read((char*) &index_num, sizeof(uint32_t));
+ result->indices.resize(index_num);
+ for (uint32_t i = 0; i < index_num; i++)
+ {
+ stream->read((char*) &result->indices[i], sizeof(uint16_t));
+ }
+
+ // materials
+ uint32_t material_num;
+ stream->read((char*) &material_num, sizeof(uint32_t));
+ result->materials.resize(material_num);
+ for (uint32_t i = 0; i < material_num; i++)
+ {
+ result->materials[i].Read(stream);
+ }
+
+ // bones
+ uint16_t bone_num;
+ stream->read((char*) &bone_num, sizeof(uint16_t));
+ result->bones.resize(bone_num);
+ for (uint32_t i = 0; i < bone_num; i++)
+ {
+ result->bones[i].Read(stream);
+ }
+
+ // iks
+ uint16_t ik_num;
+ stream->read((char*) &ik_num, sizeof(uint16_t));
+ result->iks.resize(ik_num);
+ for (uint32_t i = 0; i < ik_num; i++)
+ {
+ result->iks[i].Read(stream);
+ }
+
+ // faces
+ uint16_t face_num;
+ stream->read((char*) &face_num, sizeof(uint16_t));
+ result->faces.resize(face_num);
+ for (uint32_t i = 0; i < face_num; i++)
+ {
+ result->faces[i].Read(stream);
+ }
+
+ // face frames
+ uint8_t face_frame_num;
+ stream->read((char*) &face_frame_num, sizeof(uint8_t));
+ result->faces_indices.resize(face_frame_num);
+ for (uint32_t i = 0; i < face_frame_num; i++)
+ {
+ stream->read((char*) &result->faces_indices[i], sizeof(uint16_t));
+ }
+
+ // bone names
+ uint8_t bone_disp_num;
+ stream->read((char*) &bone_disp_num, sizeof(uint8_t));
+ result->bone_disp_name.resize(bone_disp_num);
+ for (uint32_t i = 0; i < bone_disp_num; i++)
+ {
+ result->bone_disp_name[i].Read(stream);
+ }
+
+ // bone frame
+ uint32_t bone_frame_num;
+ stream->read((char*) &bone_frame_num, sizeof(uint32_t));
+ result->bone_disp.resize(bone_frame_num);
+ for (uint32_t i = 0; i < bone_frame_num; i++)
+ {
+ result->bone_disp[i].Read(stream);
+ }
+
+ // english name
+ bool english;
+ stream->read((char*) &english, sizeof(char));
+ if (english)
+ {
+ result->header.ReadExtension(stream);
+ for (uint32_t i = 0; i < bone_num; i++)
+ {
+ result->bones[i].ReadExpantion(stream);
+ }
+ for (uint32_t i = 0; i < face_num; i++)
+ {
+ if (result->faces[i].type == pmd::FaceCategory::Base)
+ {
+ continue;
+ }
+ result->faces[i].ReadExpantion(stream);
+ }
+ for (uint32_t i = 0; i < result->bone_disp_name.size(); i++)
+ {
+ result->bone_disp_name[i].ReadExpantion(stream);
+ }
+ }
+
+ // toon textures
+ if (stream->peek() == std::ios::traits_type::eof())
+ {
+ result->toon_filenames.clear();
+ }
+ else {
+ result->toon_filenames.resize(10);
+ for (uint32_t i = 0; i < 10; i++)
+ {
+ stream->read(buffer, 100);
+ result->toon_filenames[i] = std::string(buffer);
+ }
+ }
+
+ // physics
+ if (stream->peek() == std::ios::traits_type::eof())
+ {
+ result->rigid_bodies.clear();
+ result->constraints.clear();
+ }
+ else {
+ uint32_t rigid_body_num;
+ stream->read((char*) &rigid_body_num, sizeof(uint32_t));
+ result->rigid_bodies.resize(rigid_body_num);
+ for (uint32_t i = 0; i < rigid_body_num; i++)
+ {
+ result->rigid_bodies[i].Read(stream);
+ }
+ uint32_t constraint_num;
+ stream->read((char*) &constraint_num, sizeof(uint32_t));
+ result->constraints.resize(constraint_num);
+ for (uint32_t i = 0; i < constraint_num; i++)
+ {
+ result->constraints[i].Read(stream);
+ }
+ }
+
+ if (stream->peek() != std::ios::traits_type::eof())
+ {
+ std::cerr << "there is unknown data" << std::endl;
+ }
+
+ return result;
+ }
+ };
+}
diff --git a/thirdparty/assimp/code/MMDPmxParser.cpp b/thirdparty/assimp/code/MMDPmxParser.cpp
new file mode 100644
index 0000000000..7425ceac22
--- /dev/null
+++ b/thirdparty/assimp/code/MMDPmxParser.cpp
@@ -0,0 +1,604 @@
+/*
+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 <utility>
+#include "MMDPmxParser.h"
+#include <assimp/StringUtils.h>
+#include "../contrib/utf8cpp/source/utf8.h"
+#include <assimp/Exceptional.h>
+
+namespace pmx
+{
+ int ReadIndex(std::istream *stream, int size)
+ {
+ switch (size)
+ {
+ case 1:
+ uint8_t tmp8;
+ stream->read((char*) &tmp8, sizeof(uint8_t));
+ if (255 == tmp8)
+ {
+ return -1;
+ }
+ else {
+ return (int) tmp8;
+ }
+ case 2:
+ uint16_t tmp16;
+ stream->read((char*) &tmp16, sizeof(uint16_t));
+ if (65535 == tmp16)
+ {
+ return -1;
+ }
+ else {
+ return (int) tmp16;
+ }
+ case 4:
+ int tmp32;
+ stream->read((char*) &tmp32, sizeof(int));
+ return tmp32;
+ default:
+ return -1;
+ }
+ }
+
+ std::string ReadString(std::istream *stream, uint8_t encoding)
+ {
+ int size;
+ stream->read((char*) &size, sizeof(int));
+ std::vector<char> buffer;
+ if (size == 0)
+ {
+ return std::string("");
+ }
+ buffer.reserve(size);
+ stream->read((char*) buffer.data(), size);
+ if (encoding == 0)
+ {
+ // UTF16 to UTF8
+ const uint16_t* sourceStart = (uint16_t*)buffer.data();
+ const unsigned int targetSize = size * 3; // enough to encode
+ char *targetStart = new char[targetSize];
+ std::memset(targetStart, 0, targetSize * sizeof(char));
+
+ utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart );
+
+ std::string result(targetStart);
+ delete [] targetStart;
+ return result;
+ }
+ else
+ {
+ // the name is already UTF8
+ return std::string((const char*)buffer.data(), size);
+ }
+ }
+
+ void PmxSetting::Read(std::istream *stream)
+ {
+ uint8_t count;
+ stream->read((char*) &count, sizeof(uint8_t));
+ if (count < 8)
+ {
+ throw DeadlyImportError("MMD: invalid size");
+ }
+ stream->read((char*) &encoding, sizeof(uint8_t));
+ stream->read((char*) &uv, sizeof(uint8_t));
+ stream->read((char*) &vertex_index_size, sizeof(uint8_t));
+ stream->read((char*) &texture_index_size, sizeof(uint8_t));
+ stream->read((char*) &material_index_size, sizeof(uint8_t));
+ stream->read((char*) &bone_index_size, sizeof(uint8_t));
+ stream->read((char*) &morph_index_size, sizeof(uint8_t));
+ stream->read((char*) &rigidbody_index_size, sizeof(uint8_t));
+ uint8_t temp;
+ for (int i = 8; i < count; i++)
+ {
+ stream->read((char*)&temp, sizeof(uint8_t));
+ }
+ }
+
+ void PmxVertexSkinningBDEF1::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->bone_index = ReadIndex(stream, setting->bone_index_size);
+ }
+
+ void PmxVertexSkinningBDEF2::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->bone_weight, sizeof(float));
+ }
+
+ void PmxVertexSkinningBDEF4::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index3 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index4 = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->bone_weight1, sizeof(float));
+ stream->read((char*) &this->bone_weight2, sizeof(float));
+ stream->read((char*) &this->bone_weight3, sizeof(float));
+ stream->read((char*) &this->bone_weight4, sizeof(float));
+ }
+
+ void PmxVertexSkinningSDEF::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->bone_weight, sizeof(float));
+ stream->read((char*) this->sdef_c, sizeof(float) * 3);
+ stream->read((char*) this->sdef_r0, sizeof(float) * 3);
+ stream->read((char*) this->sdef_r1, sizeof(float) * 3);
+ }
+
+ void PmxVertexSkinningQDEF::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index3 = ReadIndex(stream, setting->bone_index_size);
+ this->bone_index4 = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->bone_weight1, sizeof(float));
+ stream->read((char*) &this->bone_weight2, sizeof(float));
+ stream->read((char*) &this->bone_weight3, sizeof(float));
+ stream->read((char*) &this->bone_weight4, sizeof(float));
+ }
+
+ void PmxVertex::Read(std::istream *stream, PmxSetting *setting)
+ {
+ stream->read((char*) this->position, sizeof(float) * 3);
+ stream->read((char*) this->normal, sizeof(float) * 3);
+ stream->read((char*) this->uv, sizeof(float) * 2);
+ for (int i = 0; i < setting->uv; ++i)
+ {
+ stream->read((char*) this->uva[i], sizeof(float) * 4);
+ }
+ stream->read((char*) &this->skinning_type, sizeof(PmxVertexSkinningType));
+ switch (this->skinning_type)
+ {
+ case PmxVertexSkinningType::BDEF1:
+ this->skinning = mmd::make_unique<PmxVertexSkinningBDEF1>();
+ break;
+ case PmxVertexSkinningType::BDEF2:
+ this->skinning = mmd::make_unique<PmxVertexSkinningBDEF2>();
+ break;
+ case PmxVertexSkinningType::BDEF4:
+ this->skinning = mmd::make_unique<PmxVertexSkinningBDEF4>();
+ break;
+ case PmxVertexSkinningType::SDEF:
+ this->skinning = mmd::make_unique<PmxVertexSkinningSDEF>();
+ break;
+ case PmxVertexSkinningType::QDEF:
+ this->skinning = mmd::make_unique<PmxVertexSkinningQDEF>();
+ break;
+ default:
+ throw "invalid skinning type";
+ }
+ this->skinning->Read(stream, setting);
+ stream->read((char*) &this->edge, sizeof(float));
+ }
+
+ void PmxMaterial::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->material_name = ReadString(stream, setting->encoding);
+ this->material_english_name = ReadString(stream, setting->encoding);
+ stream->read((char*) this->diffuse, sizeof(float) * 4);
+ stream->read((char*) this->specular, sizeof(float) * 3);
+ stream->read((char*) &this->specularlity, sizeof(float));
+ stream->read((char*) this->ambient, sizeof(float) * 3);
+ stream->read((char*) &this->flag, sizeof(uint8_t));
+ stream->read((char*) this->edge_color, sizeof(float) * 4);
+ stream->read((char*) &this->edge_size, sizeof(float));
+ this->diffuse_texture_index = ReadIndex(stream, setting->texture_index_size);
+ this->sphere_texture_index = ReadIndex(stream, setting->texture_index_size);
+ stream->read((char*) &this->sphere_op_mode, sizeof(uint8_t));
+ stream->read((char*) &this->common_toon_flag, sizeof(uint8_t));
+ if (this->common_toon_flag)
+ {
+ stream->read((char*) &this->toon_texture_index, sizeof(uint8_t));
+ }
+ else {
+ this->toon_texture_index = ReadIndex(stream, setting->texture_index_size);
+ }
+ this->memo = ReadString(stream, setting->encoding);
+ stream->read((char*) &this->index_count, sizeof(int));
+ }
+
+ void PmxIkLink::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->link_target = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->angle_lock, sizeof(uint8_t));
+ if (angle_lock == 1)
+ {
+ stream->read((char*) this->max_radian, sizeof(float) * 3);
+ stream->read((char*) this->min_radian, sizeof(float) * 3);
+ }
+ }
+
+ void PmxBone::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->bone_name = ReadString(stream, setting->encoding);
+ this->bone_english_name = ReadString(stream, setting->encoding);
+ stream->read((char*) this->position, sizeof(float) * 3);
+ this->parent_index = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->level, sizeof(int));
+ stream->read((char*) &this->bone_flag, sizeof(uint16_t));
+ if (this->bone_flag & 0x0001) {
+ this->target_index = ReadIndex(stream, setting->bone_index_size);
+ }
+ else {
+ stream->read((char*)this->offset, sizeof(float) * 3);
+ }
+ if (this->bone_flag & (0x0100 | 0x0200)) {
+ this->grant_parent_index = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->grant_weight, sizeof(float));
+ }
+ if (this->bone_flag & 0x0400) {
+ stream->read((char*)this->lock_axis_orientation, sizeof(float) * 3);
+ }
+ if (this->bone_flag & 0x0800) {
+ stream->read((char*)this->local_axis_x_orientation, sizeof(float) * 3);
+ stream->read((char*)this->local_axis_y_orientation, sizeof(float) * 3);
+ }
+ if (this->bone_flag & 0x2000) {
+ stream->read((char*) &this->key, sizeof(int));
+ }
+ if (this->bone_flag & 0x0020) {
+ this->ik_target_bone_index = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &ik_loop, sizeof(int));
+ stream->read((char*) &ik_loop_angle_limit, sizeof(float));
+ stream->read((char*) &ik_link_count, sizeof(int));
+ this->ik_links = mmd::make_unique<PmxIkLink []>(ik_link_count);
+ for (int i = 0; i < ik_link_count; i++) {
+ ik_links[i].Read(stream, setting);
+ }
+ }
+ }
+
+ void PmxMorphVertexOffset::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->vertex_index = ReadIndex(stream, setting->vertex_index_size);
+ stream->read((char*)this->position_offset, sizeof(float) * 3);
+ }
+
+ void PmxMorphUVOffset::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->vertex_index = ReadIndex(stream, setting->vertex_index_size);
+ stream->read((char*)this->uv_offset, sizeof(float) * 4);
+ }
+
+ void PmxMorphBoneOffset::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->bone_index = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*)this->translation, sizeof(float) * 3);
+ stream->read((char*)this->rotation, sizeof(float) * 4);
+ }
+
+ void PmxMorphMaterialOffset::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->material_index = ReadIndex(stream, setting->material_index_size);
+ stream->read((char*) &this->offset_operation, sizeof(uint8_t));
+ stream->read((char*)this->diffuse, sizeof(float) * 4);
+ stream->read((char*)this->specular, sizeof(float) * 3);
+ stream->read((char*) &this->specularity, sizeof(float));
+ stream->read((char*)this->ambient, sizeof(float) * 3);
+ stream->read((char*)this->edge_color, sizeof(float) * 4);
+ stream->read((char*) &this->edge_size, sizeof(float));
+ stream->read((char*)this->texture_argb, sizeof(float) * 4);
+ stream->read((char*)this->sphere_texture_argb, sizeof(float) * 4);
+ stream->read((char*)this->toon_texture_argb, sizeof(float) * 4);
+ }
+
+ void PmxMorphGroupOffset::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->morph_index = ReadIndex(stream, setting->morph_index_size);
+ stream->read((char*) &this->morph_weight, sizeof(float));
+ }
+
+ void PmxMorphFlipOffset::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->morph_index = ReadIndex(stream, setting->morph_index_size);
+ stream->read((char*) &this->morph_value, sizeof(float));
+ }
+
+ void PmxMorphImplusOffset::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->rigid_body_index = ReadIndex(stream, setting->rigidbody_index_size);
+ stream->read((char*) &this->is_local, sizeof(uint8_t));
+ stream->read((char*)this->velocity, sizeof(float) * 3);
+ stream->read((char*)this->angular_torque, sizeof(float) * 3);
+ }
+
+ void PmxMorph::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->morph_name = ReadString(stream, setting->encoding);
+ this->morph_english_name = ReadString(stream, setting->encoding);
+ stream->read((char*) &category, sizeof(MorphCategory));
+ stream->read((char*) &morph_type, sizeof(MorphType));
+ stream->read((char*) &this->offset_count, sizeof(int));
+ switch (this->morph_type)
+ {
+ case MorphType::Group:
+ group_offsets = mmd::make_unique<PmxMorphGroupOffset []>(this->offset_count);
+ for (int i = 0; i < offset_count; i++)
+ {
+ group_offsets[i].Read(stream, setting);
+ }
+ break;
+ case MorphType::Vertex:
+ vertex_offsets = mmd::make_unique<PmxMorphVertexOffset []>(this->offset_count);
+ for (int i = 0; i < offset_count; i++)
+ {
+ vertex_offsets[i].Read(stream, setting);
+ }
+ break;
+ case MorphType::Bone:
+ bone_offsets = mmd::make_unique<PmxMorphBoneOffset []>(this->offset_count);
+ for (int i = 0; i < offset_count; i++)
+ {
+ bone_offsets[i].Read(stream, setting);
+ }
+ break;
+ case MorphType::Matrial:
+ material_offsets = mmd::make_unique<PmxMorphMaterialOffset []>(this->offset_count);
+ for (int i = 0; i < offset_count; i++)
+ {
+ material_offsets[i].Read(stream, setting);
+ }
+ break;
+ case MorphType::UV:
+ case MorphType::AdditionalUV1:
+ case MorphType::AdditionalUV2:
+ case MorphType::AdditionalUV3:
+ case MorphType::AdditionalUV4:
+ uv_offsets = mmd::make_unique<PmxMorphUVOffset []>(this->offset_count);
+ for (int i = 0; i < offset_count; i++)
+ {
+ uv_offsets[i].Read(stream, setting);
+ }
+ break;
+ default:
+ throw DeadlyImportError("MMD: unknown morth type");
+ }
+ }
+
+ void PmxFrameElement::Read(std::istream *stream, PmxSetting *setting)
+ {
+ stream->read((char*) &this->element_target, sizeof(uint8_t));
+ if (this->element_target == 0x00)
+ {
+ this->index = ReadIndex(stream, setting->bone_index_size);
+ }
+ else {
+ this->index = ReadIndex(stream, setting->morph_index_size);
+ }
+ }
+
+ void PmxFrame::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->frame_name = ReadString(stream, setting->encoding);
+ this->frame_english_name = ReadString(stream, setting->encoding);
+ stream->read((char*) &this->frame_flag, sizeof(uint8_t));
+ stream->read((char*) &this->element_count, sizeof(int));
+ this->elements = mmd::make_unique<PmxFrameElement []>(this->element_count);
+ for (int i = 0; i < this->element_count; i++)
+ {
+ this->elements[i].Read(stream, setting);
+ }
+ }
+
+ void PmxRigidBody::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->girid_body_name = ReadString(stream, setting->encoding);
+ this->girid_body_english_name = ReadString(stream, setting->encoding);
+ this->target_bone = ReadIndex(stream, setting->bone_index_size);
+ stream->read((char*) &this->group, sizeof(uint8_t));
+ stream->read((char*) &this->mask, sizeof(uint16_t));
+ stream->read((char*) &this->shape, sizeof(uint8_t));
+ stream->read((char*) this->size, sizeof(float) * 3);
+ stream->read((char*) this->position, sizeof(float) * 3);
+ stream->read((char*) this->orientation, sizeof(float) * 3);
+ stream->read((char*) &this->mass, sizeof(float));
+ stream->read((char*) &this->move_attenuation, sizeof(float));
+ stream->read((char*) &this->rotation_attenuation, sizeof(float));
+ stream->read((char*) &this->repulsion, sizeof(float));
+ stream->read((char*) &this->friction, sizeof(float));
+ stream->read((char*) &this->physics_calc_type, sizeof(uint8_t));
+ }
+
+ void PmxJointParam::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->rigid_body1 = ReadIndex(stream, setting->rigidbody_index_size);
+ this->rigid_body2 = ReadIndex(stream, setting->rigidbody_index_size);
+ stream->read((char*) this->position, sizeof(float) * 3);
+ stream->read((char*) this->orientaiton, sizeof(float) * 3);
+ stream->read((char*) this->move_limitation_min, sizeof(float) * 3);
+ stream->read((char*) this->move_limitation_max, sizeof(float) * 3);
+ stream->read((char*) this->rotation_limitation_min, sizeof(float) * 3);
+ stream->read((char*) this->rotation_limitation_max, sizeof(float) * 3);
+ stream->read((char*) this->spring_move_coefficient, sizeof(float) * 3);
+ stream->read((char*) this->spring_rotation_coefficient, sizeof(float) * 3);
+ }
+
+ void PmxJoint::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->joint_name = ReadString(stream, setting->encoding);
+ this->joint_english_name = ReadString(stream, setting->encoding);
+ stream->read((char*) &this->joint_type, sizeof(uint8_t));
+ this->param.Read(stream, setting);
+ }
+
+ void PmxAncherRigidBody::Read(std::istream *stream, PmxSetting *setting)
+ {
+ this->related_rigid_body = ReadIndex(stream, setting->rigidbody_index_size);
+ this->related_vertex = ReadIndex(stream, setting->vertex_index_size);
+ stream->read((char*) &this->is_near, sizeof(uint8_t));
+ }
+
+ void PmxSoftBody::Read(std::istream * /*stream*/, PmxSetting * /*setting*/)
+ {
+ std::cerr << "Not Implemented Exception" << std::endl;
+ throw DeadlyImportError("MMD: Not Implemented Exception");
+ }
+
+ void PmxModel::Init()
+ {
+ this->version = 0.0f;
+ this->model_name.clear();
+ this->model_english_name.clear();
+ this->model_comment.clear();
+ this->model_english_comment.clear();
+ this->vertex_count = 0;
+ this->vertices = nullptr;
+ this->index_count = 0;
+ this->indices = nullptr;
+ this->texture_count = 0;
+ this->textures = nullptr;
+ this->material_count = 0;
+ this->materials = nullptr;
+ this->bone_count = 0;
+ this->bones = nullptr;
+ this->morph_count = 0;
+ this->morphs = nullptr;
+ this->frame_count = 0;
+ this->frames = nullptr;
+ this->rigid_body_count = 0;
+ this->rigid_bodies = nullptr;
+ this->joint_count = 0;
+ this->joints = nullptr;
+ this->soft_body_count = 0;
+ this->soft_bodies = nullptr;
+ }
+
+ void PmxModel::Read(std::istream *stream)
+ {
+ char magic[4];
+ stream->read((char*) magic, sizeof(char) * 4);
+ if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20)
+ {
+ std::cerr << "invalid magic number." << std::endl;
+ throw DeadlyImportError("MMD: invalid magic number.");
+ }
+ stream->read((char*) &version, sizeof(float));
+ if (version != 2.0f && version != 2.1f)
+ {
+ std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
+ throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version));
+ }
+ this->setting.Read(stream);
+
+ this->model_name = ReadString(stream, setting.encoding);
+ this->model_english_name = ReadString(stream, setting.encoding);
+ this->model_comment = ReadString(stream, setting.encoding);
+ this->model_english_comment = ReadString(stream, setting.encoding);
+
+ // read vertices
+ stream->read((char*) &vertex_count, sizeof(int));
+ this->vertices = mmd::make_unique<PmxVertex []>(vertex_count);
+ for (int i = 0; i < vertex_count; i++)
+ {
+ vertices[i].Read(stream, &setting);
+ }
+
+ // read indices
+ stream->read((char*) &index_count, sizeof(int));
+ this->indices = mmd::make_unique<int []>(index_count);
+ for (int i = 0; i < index_count; i++)
+ {
+ this->indices[i] = ReadIndex(stream, setting.vertex_index_size);
+ }
+
+ // read texture names
+ stream->read((char*) &texture_count, sizeof(int));
+ this->textures = mmd::make_unique<std::string []>(texture_count);
+ for (int i = 0; i < texture_count; i++)
+ {
+ this->textures[i] = ReadString(stream, setting.encoding);
+ }
+
+ // read materials
+ stream->read((char*) &material_count, sizeof(int));
+ this->materials = mmd::make_unique<PmxMaterial []>(material_count);
+ for (int i = 0; i < material_count; i++)
+ {
+ this->materials[i].Read(stream, &setting);
+ }
+
+ // read bones
+ stream->read((char*) &this->bone_count, sizeof(int));
+ this->bones = mmd::make_unique<PmxBone []>(this->bone_count);
+ for (int i = 0; i < this->bone_count; i++)
+ {
+ this->bones[i].Read(stream, &setting);
+ }
+
+ // read morphs
+ stream->read((char*) &this->morph_count, sizeof(int));
+ this->morphs = mmd::make_unique<PmxMorph []>(this->morph_count);
+ for (int i = 0; i < this->morph_count; i++)
+ {
+ this->morphs[i].Read(stream, &setting);
+ }
+
+ // read display frames
+ stream->read((char*) &this->frame_count, sizeof(int));
+ this->frames = mmd::make_unique<PmxFrame []>(this->frame_count);
+ for (int i = 0; i < this->frame_count; i++)
+ {
+ this->frames[i].Read(stream, &setting);
+ }
+
+ // read rigid bodies
+ stream->read((char*) &this->rigid_body_count, sizeof(int));
+ this->rigid_bodies = mmd::make_unique<PmxRigidBody []>(this->rigid_body_count);
+ for (int i = 0; i < this->rigid_body_count; i++)
+ {
+ this->rigid_bodies[i].Read(stream, &setting);
+ }
+
+ // read joints
+ stream->read((char*) &this->joint_count, sizeof(int));
+ this->joints = mmd::make_unique<PmxJoint []>(this->joint_count);
+ for (int i = 0; i < this->joint_count; i++)
+ {
+ this->joints[i].Read(stream, &setting);
+ }
+ }
+}
diff --git a/thirdparty/assimp/code/MMDPmxParser.h b/thirdparty/assimp/code/MMDPmxParser.h
new file mode 100644
index 0000000000..cf523a1298
--- /dev/null
+++ b/thirdparty/assimp/code/MMDPmxParser.h
@@ -0,0 +1,782 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <memory>
+#include "MMDCpp14.h"
+
+namespace pmx
+{
+ class PmxSetting
+ {
+ public:
+ PmxSetting()
+ : encoding(0)
+ , uv(0)
+ , vertex_index_size(0)
+ , texture_index_size(0)
+ , material_index_size(0)
+ , bone_index_size(0)
+ , morph_index_size(0)
+ , rigidbody_index_size(0)
+ {}
+
+ uint8_t encoding;
+ uint8_t uv;
+ uint8_t vertex_index_size;
+ uint8_t texture_index_size;
+ uint8_t material_index_size;
+ uint8_t bone_index_size;
+ uint8_t morph_index_size;
+ uint8_t rigidbody_index_size;
+ void Read(std::istream *stream);
+ };
+
+ enum class PmxVertexSkinningType : uint8_t
+ {
+ BDEF1 = 0,
+ BDEF2 = 1,
+ BDEF4 = 2,
+ SDEF = 3,
+ QDEF = 4,
+ };
+
+ class PmxVertexSkinning
+ {
+ public:
+ virtual void Read(std::istream *stream, PmxSetting *setting) = 0;
+ virtual ~PmxVertexSkinning() {}
+ };
+
+ class PmxVertexSkinningBDEF1 : public PmxVertexSkinning
+ {
+ public:
+ PmxVertexSkinningBDEF1()
+ : bone_index(0)
+ {}
+
+ int bone_index;
+ void Read(std::istream *stresam, PmxSetting *setting);
+ };
+
+ class PmxVertexSkinningBDEF2 : public PmxVertexSkinning
+ {
+ public:
+ PmxVertexSkinningBDEF2()
+ : bone_index1(0)
+ , bone_index2(0)
+ , bone_weight(0.0f)
+ {}
+
+ int bone_index1;
+ int bone_index2;
+ float bone_weight;
+ void Read(std::istream *stresam, PmxSetting *setting);
+ };
+
+ class PmxVertexSkinningBDEF4 : public PmxVertexSkinning
+ {
+ public:
+ PmxVertexSkinningBDEF4()
+ : bone_index1(0)
+ , bone_index2(0)
+ , bone_index3(0)
+ , bone_index4(0)
+ , bone_weight1(0.0f)
+ , bone_weight2(0.0f)
+ , bone_weight3(0.0f)
+ , bone_weight4(0.0f)
+ {}
+
+ int bone_index1;
+ int bone_index2;
+ int bone_index3;
+ int bone_index4;
+ float bone_weight1;
+ float bone_weight2;
+ float bone_weight3;
+ float bone_weight4;
+ void Read(std::istream *stresam, PmxSetting *setting);
+ };
+
+ class PmxVertexSkinningSDEF : public PmxVertexSkinning
+ {
+ public:
+ PmxVertexSkinningSDEF()
+ : bone_index1(0)
+ , bone_index2(0)
+ , bone_weight(0.0f)
+ {
+ for (int i = 0; i < 3; ++i) {
+ sdef_c[i] = 0.0f;
+ sdef_r0[i] = 0.0f;
+ sdef_r1[i] = 0.0f;
+ }
+ }
+
+ int bone_index1;
+ int bone_index2;
+ float bone_weight;
+ float sdef_c[3];
+ float sdef_r0[3];
+ float sdef_r1[3];
+ void Read(std::istream *stresam, PmxSetting *setting);
+ };
+
+ class PmxVertexSkinningQDEF : public PmxVertexSkinning
+ {
+ public:
+ PmxVertexSkinningQDEF()
+ : bone_index1(0)
+ , bone_index2(0)
+ , bone_index3(0)
+ , bone_index4(0)
+ , bone_weight1(0.0f)
+ , bone_weight2(0.0f)
+ , bone_weight3(0.0f)
+ , bone_weight4(0.0f)
+ {}
+
+ int bone_index1;
+ int bone_index2;
+ int bone_index3;
+ int bone_index4;
+ float bone_weight1;
+ float bone_weight2;
+ float bone_weight3;
+ float bone_weight4;
+ void Read(std::istream *stresam, PmxSetting *setting);
+ };
+
+ class PmxVertex
+ {
+ public:
+ PmxVertex()
+ : edge(0.0f)
+ {
+ uv[0] = uv[1] = 0.0f;
+ for (int i = 0; i < 3; ++i) {
+ position[i] = 0.0f;
+ normal[i] = 0.0f;
+ }
+ for (int i = 0; i < 4; ++i) {
+ for (int k = 0; k < 4; ++k) {
+ uva[i][k] = 0.0f;
+ }
+ }
+ }
+
+ float position[3];
+ float normal[3];
+ float uv[2];
+ float uva[4][4];
+ PmxVertexSkinningType skinning_type;
+ std::unique_ptr<PmxVertexSkinning> skinning;
+ float edge;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxMaterial
+ {
+ public:
+ PmxMaterial()
+ : specularlity(0.0f)
+ , flag(0)
+ , edge_size(0.0f)
+ , diffuse_texture_index(0)
+ , sphere_texture_index(0)
+ , sphere_op_mode(0)
+ , common_toon_flag(0)
+ , toon_texture_index(0)
+ , index_count(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ specular[i] = 0.0f;
+ ambient[i] = 0.0f;
+ edge_color[i] = 0.0f;
+ }
+ for (int i = 0; i < 4; ++i) {
+ diffuse[i] = 0.0f;
+ }
+ }
+
+ std::string material_name;
+ std::string material_english_name;
+ float diffuse[4];
+ float specular[3];
+ float specularlity;
+ float ambient[3];
+ uint8_t flag;
+ float edge_color[4];
+ float edge_size;
+ int diffuse_texture_index;
+ int sphere_texture_index;
+ uint8_t sphere_op_mode;
+ uint8_t common_toon_flag;
+ int toon_texture_index;
+ std::string memo;
+ int index_count;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxIkLink
+ {
+ public:
+ PmxIkLink()
+ : link_target(0)
+ , angle_lock(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ max_radian[i] = 0.0f;
+ min_radian[i] = 0.0f;
+ }
+ }
+
+ int link_target;
+ uint8_t angle_lock;
+ float max_radian[3];
+ float min_radian[3];
+ void Read(std::istream *stream, PmxSetting *settingn);
+ };
+
+ class PmxBone
+ {
+ public:
+ PmxBone()
+ : parent_index(0)
+ , level(0)
+ , bone_flag(0)
+ , target_index(0)
+ , grant_parent_index(0)
+ , grant_weight(0.0f)
+ , key(0)
+ , ik_target_bone_index(0)
+ , ik_loop(0)
+ , ik_loop_angle_limit(0.0f)
+ , ik_link_count(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ position[i] = 0.0f;
+ offset[i] = 0.0f;
+ lock_axis_orientation[i] = 0.0f;
+ local_axis_x_orientation[i] = 0.0f;
+ local_axis_y_orientation[i] = 0.0f;
+ }
+ }
+
+ std::string bone_name;
+ std::string bone_english_name;
+ float position[3];
+ int parent_index;
+ int level;
+ uint16_t bone_flag;
+ float offset[3];
+ int target_index;
+ int grant_parent_index;
+ float grant_weight;
+ float lock_axis_orientation[3];
+ float local_axis_x_orientation[3];
+ float local_axis_y_orientation[3];
+ int key;
+ int ik_target_bone_index;
+ int ik_loop;
+ float ik_loop_angle_limit;
+ int ik_link_count;
+ std::unique_ptr<PmxIkLink []> ik_links;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ enum class MorphType : uint8_t
+ {
+ Group = 0,
+ Vertex = 1,
+ Bone = 2,
+ UV = 3,
+ AdditionalUV1 = 4,
+ AdditionalUV2 = 5,
+ AdditionalUV3 = 6,
+ AdditionalUV4 = 7,
+ Matrial = 8,
+ Flip = 9,
+ Implus = 10,
+ };
+
+ enum class MorphCategory : uint8_t
+ {
+ ReservedCategory = 0,
+ Eyebrow = 1,
+ Eye = 2,
+ Mouth = 3,
+ Other = 4,
+ };
+
+ class PmxMorphOffset
+ {
+ public:
+ void virtual Read(std::istream *stream, PmxSetting *setting) = 0;
+ };
+
+ class PmxMorphVertexOffset : public PmxMorphOffset
+ {
+ public:
+ PmxMorphVertexOffset()
+ : vertex_index(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ position_offset[i] = 0.0f;
+ }
+ }
+ int vertex_index;
+ float position_offset[3];
+ void Read(std::istream *stream, PmxSetting *setting); //override;
+ };
+
+ class PmxMorphUVOffset : public PmxMorphOffset
+ {
+ public:
+ PmxMorphUVOffset()
+ : vertex_index(0)
+ {
+ for (int i = 0; i < 4; ++i) {
+ uv_offset[i] = 0.0f;
+ }
+ }
+ int vertex_index;
+ float uv_offset[4];
+ void Read(std::istream *stream, PmxSetting *setting); //override;
+ };
+
+ class PmxMorphBoneOffset : public PmxMorphOffset
+ {
+ public:
+ PmxMorphBoneOffset()
+ : bone_index(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ translation[i] = 0.0f;
+ }
+ for (int i = 0; i < 4; ++i) {
+ rotation[i] = 0.0f;
+ }
+ }
+ int bone_index;
+ float translation[3];
+ float rotation[4];
+ void Read(std::istream *stream, PmxSetting *setting); //override;
+ };
+
+ class PmxMorphMaterialOffset : public PmxMorphOffset
+ {
+ public:
+ PmxMorphMaterialOffset()
+ : specularity(0.0f)
+ , edge_size(0.0f)
+ {
+ for (int i = 0; i < 3; ++i) {
+ specular[i] = 0.0f;
+ ambient[i] = 0.0f;
+ }
+ for (int i = 0; i < 4; ++i) {
+ diffuse[i] = 0.0f;
+ edge_color[i] = 0.0f;
+ texture_argb[i] = 0.0f;
+ sphere_texture_argb[i] = 0.0f;
+ toon_texture_argb[i] = 0.0f;
+ }
+ }
+ int material_index;
+ uint8_t offset_operation;
+ float diffuse[4];
+ float specular[3];
+ float specularity;
+ float ambient[3];
+ float edge_color[4];
+ float edge_size;
+ float texture_argb[4];
+ float sphere_texture_argb[4];
+ float toon_texture_argb[4];
+ void Read(std::istream *stream, PmxSetting *setting); //override;
+ };
+
+ class PmxMorphGroupOffset : public PmxMorphOffset
+ {
+ public:
+ PmxMorphGroupOffset()
+ : morph_index(0)
+ , morph_weight(0.0f)
+ {}
+ int morph_index;
+ float morph_weight;
+ void Read(std::istream *stream, PmxSetting *setting); //override;
+ };
+
+ class PmxMorphFlipOffset : public PmxMorphOffset
+ {
+ public:
+ PmxMorphFlipOffset()
+ : morph_index(0)
+ , morph_value(0.0f)
+ {}
+ int morph_index;
+ float morph_value;
+ void Read(std::istream *stream, PmxSetting *setting); //override;
+ };
+
+ class PmxMorphImplusOffset : public PmxMorphOffset
+ {
+ public:
+ PmxMorphImplusOffset()
+ : rigid_body_index(0)
+ , is_local(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ velocity[i] = 0.0f;
+ angular_torque[i] = 0.0f;
+ }
+ }
+ int rigid_body_index;
+ uint8_t is_local;
+ float velocity[3];
+ float angular_torque[3];
+ void Read(std::istream *stream, PmxSetting *setting); //override;
+ };
+
+ class PmxMorph
+ {
+ public:
+ PmxMorph()
+ : offset_count(0)
+ {
+ }
+ std::string morph_name;
+ std::string morph_english_name;
+ MorphCategory category;
+ MorphType morph_type;
+ int offset_count;
+ std::unique_ptr<PmxMorphVertexOffset []> vertex_offsets;
+ std::unique_ptr<PmxMorphUVOffset []> uv_offsets;
+ std::unique_ptr<PmxMorphBoneOffset []> bone_offsets;
+ std::unique_ptr<PmxMorphMaterialOffset []> material_offsets;
+ std::unique_ptr<PmxMorphGroupOffset []> group_offsets;
+ std::unique_ptr<PmxMorphFlipOffset []> flip_offsets;
+ std::unique_ptr<PmxMorphImplusOffset []> implus_offsets;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxFrameElement
+ {
+ public:
+ PmxFrameElement()
+ : element_target(0)
+ , index(0)
+ {
+ }
+ uint8_t element_target;
+ int index;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxFrame
+ {
+ public:
+ PmxFrame()
+ : frame_flag(0)
+ , element_count(0)
+ {
+ }
+ std::string frame_name;
+ std::string frame_english_name;
+ uint8_t frame_flag;
+ int element_count;
+ std::unique_ptr<PmxFrameElement []> elements;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxRigidBody
+ {
+ public:
+ PmxRigidBody()
+ : target_bone(0)
+ , group(0)
+ , mask(0)
+ , shape(0)
+ , mass(0.0f)
+ , move_attenuation(0.0f)
+ , rotation_attenuation(0.0f)
+ , repulsion(0.0f)
+ , friction(0.0f)
+ , physics_calc_type(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ size[i] = 0.0f;
+ position[i] = 0.0f;
+ orientation[i] = 0.0f;
+ }
+ }
+ std::string girid_body_name;
+ std::string girid_body_english_name;
+ int target_bone;
+ uint8_t group;
+ uint16_t mask;
+ uint8_t shape;
+ float size[3];
+ float position[3];
+ float orientation[3];
+ float mass;
+ float move_attenuation;
+ float rotation_attenuation;
+ float repulsion;
+ float friction;
+ uint8_t physics_calc_type;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ enum class PmxJointType : uint8_t
+ {
+ Generic6DofSpring = 0,
+ Generic6Dof = 1,
+ Point2Point = 2,
+ ConeTwist = 3,
+ Slider = 5,
+ Hinge = 6
+ };
+
+ class PmxJointParam
+ {
+ public:
+ PmxJointParam()
+ : rigid_body1(0)
+ , rigid_body2(0)
+ {
+ for (int i = 0; i < 3; ++i) {
+ position[i] = 0.0f;
+ orientaiton[i] = 0.0f;
+ move_limitation_min[i] = 0.0f;
+ move_limitation_max[i] = 0.0f;
+ rotation_limitation_min[i] = 0.0f;
+ rotation_limitation_max[i] = 0.0f;
+ spring_move_coefficient[i] = 0.0f;
+ spring_rotation_coefficient[i] = 0.0f;
+ }
+ }
+ int rigid_body1;
+ int rigid_body2;
+ float position[3];
+ float orientaiton[3];
+ float move_limitation_min[3];
+ float move_limitation_max[3];
+ float rotation_limitation_min[3];
+ float rotation_limitation_max[3];
+ float spring_move_coefficient[3];
+ float spring_rotation_coefficient[3];
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxJoint
+ {
+ public:
+ std::string joint_name;
+ std::string joint_english_name;
+ PmxJointType joint_type;
+ PmxJointParam param;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ enum PmxSoftBodyFlag : uint8_t
+ {
+ BLink = 0x01,
+ Cluster = 0x02,
+ Link = 0x04
+ };
+
+ class PmxAncherRigidBody
+ {
+ public:
+ PmxAncherRigidBody()
+ : related_rigid_body(0)
+ , related_vertex(0)
+ , is_near(false)
+ {}
+ int related_rigid_body;
+ int related_vertex;
+ bool is_near;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxSoftBody
+ {
+ public:
+ PmxSoftBody()
+ : shape(0)
+ , target_material(0)
+ , group(0)
+ , mask(0)
+ , blink_distance(0)
+ , cluster_count(0)
+ , mass(0.0)
+ , collisioni_margin(0.0)
+ , aero_model(0)
+ , VCF(0.0f)
+ , DP(0.0f)
+ , DG(0.0f)
+ , LF(0.0f)
+ , PR(0.0f)
+ , VC(0.0f)
+ , DF(0.0f)
+ , MT(0.0f)
+ , CHR(0.0f)
+ , KHR(0.0f)
+ , SHR(0.0f)
+ , AHR(0.0f)
+ , SRHR_CL(0.0f)
+ , SKHR_CL(0.0f)
+ , SSHR_CL(0.0f)
+ , SR_SPLT_CL(0.0f)
+ , SK_SPLT_CL(0.0f)
+ , SS_SPLT_CL(0.0f)
+ , V_IT(0)
+ , P_IT(0)
+ , D_IT(0)
+ , C_IT(0)
+ , LST(0.0f)
+ , AST(0.0f)
+ , VST(0.0f)
+ , anchor_count(0)
+ , pin_vertex_count(0)
+ {}
+ std::string soft_body_name;
+ std::string soft_body_english_name;
+ uint8_t shape;
+ int target_material;
+ uint8_t group;
+ uint16_t mask;
+ PmxSoftBodyFlag flag;
+ int blink_distance;
+ int cluster_count;
+ float mass;
+ float collisioni_margin;
+ int aero_model;
+ float VCF;
+ float DP;
+ float DG;
+ float LF;
+ float PR;
+ float VC;
+ float DF;
+ float MT;
+ float CHR;
+ float KHR;
+ float SHR;
+ float AHR;
+ float SRHR_CL;
+ float SKHR_CL;
+ float SSHR_CL;
+ float SR_SPLT_CL;
+ float SK_SPLT_CL;
+ float SS_SPLT_CL;
+ int V_IT;
+ int P_IT;
+ int D_IT;
+ int C_IT;
+ float LST;
+ float AST;
+ float VST;
+ int anchor_count;
+ std::unique_ptr<PmxAncherRigidBody []> anchers;
+ int pin_vertex_count;
+ std::unique_ptr<int []> pin_vertices;
+ void Read(std::istream *stream, PmxSetting *setting);
+ };
+
+ class PmxModel
+ {
+ public:
+ PmxModel()
+ : version(0.0f)
+ , vertex_count(0)
+ , index_count(0)
+ , texture_count(0)
+ , material_count(0)
+ , bone_count(0)
+ , morph_count(0)
+ , frame_count(0)
+ , rigid_body_count(0)
+ , joint_count(0)
+ , soft_body_count(0)
+ {}
+
+ float version;
+ PmxSetting setting;
+ std::string model_name;
+ std::string model_english_name;
+ std::string model_comment;
+ std::string model_english_comment;
+ int vertex_count;
+ std::unique_ptr<PmxVertex []> vertices;
+ int index_count;
+ std::unique_ptr<int []> indices;
+ int texture_count;
+ std::unique_ptr< std::string []> textures;
+ int material_count;
+ std::unique_ptr<PmxMaterial []> materials;
+ int bone_count;
+ std::unique_ptr<PmxBone []> bones;
+ int morph_count;
+ std::unique_ptr<PmxMorph []> morphs;
+ int frame_count;
+ std::unique_ptr<PmxFrame [] > frames;
+ int rigid_body_count;
+ std::unique_ptr<PmxRigidBody []> rigid_bodies;
+ int joint_count;
+ std::unique_ptr<PmxJoint []> joints;
+ int soft_body_count;
+ std::unique_ptr<PmxSoftBody []> soft_bodies;
+ void Init();
+ void Read(std::istream *stream);
+ //static std::unique_ptr<PmxModel> ReadFromFile(const char *filename);
+ //static std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream);
+ };
+}
diff --git a/thirdparty/assimp/code/MMDVmdParser.h b/thirdparty/assimp/code/MMDVmdParser.h
new file mode 100644
index 0000000000..947c3a2422
--- /dev/null
+++ b/thirdparty/assimp/code/MMDVmdParser.h
@@ -0,0 +1,376 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <vector>
+#include <string>
+#include <memory>
+#include <iostream>
+#include <fstream>
+#include <ostream>
+#include "MMDCpp14.h"
+
+namespace vmd
+{
+ class VmdBoneFrame
+ {
+ public:
+ std::string name;
+ int frame;
+ float position[3];
+ float orientation[4];
+ char interpolation[4][4][4];
+
+ void Read(std::istream* stream)
+ {
+ char buffer[15];
+ stream->read((char*) buffer, sizeof(char)*15);
+ name = std::string(buffer);
+ stream->read((char*) &frame, sizeof(int));
+ stream->read((char*) position, sizeof(float)*3);
+ stream->read((char*) orientation, sizeof(float)*4);
+ stream->read((char*) interpolation, sizeof(char) * 4 * 4 * 4);
+ }
+
+ void Write(std::ostream* stream)
+ {
+ stream->write((char*)name.c_str(), sizeof(char) * 15);
+ stream->write((char*)&frame, sizeof(int));
+ stream->write((char*)position, sizeof(float) * 3);
+ stream->write((char*)orientation, sizeof(float) * 4);
+ stream->write((char*)interpolation, sizeof(char) * 4 * 4 * 4);
+ }
+ };
+
+ class VmdFaceFrame
+ {
+ public:
+ std::string face_name;
+ float weight;
+ uint32_t frame;
+
+ void Read(std::istream* stream)
+ {
+ char buffer[15];
+ stream->read((char*) &buffer, sizeof(char) * 15);
+ face_name = std::string(buffer);
+ stream->read((char*) &frame, sizeof(int));
+ stream->read((char*) &weight, sizeof(float));
+ }
+
+ void Write(std::ostream* stream)
+ {
+ stream->write((char*)face_name.c_str(), sizeof(char) * 15);
+ stream->write((char*)&frame, sizeof(int));
+ stream->write((char*)&weight, sizeof(float));
+ }
+ };
+
+ class VmdCameraFrame
+ {
+ public:
+ int frame;
+ float distance;
+ float position[3];
+ float orientation[3];
+ char interpolation[6][4];
+ float angle;
+ char unknown[3];
+
+ void Read(std::istream *stream)
+ {
+ stream->read((char*) &frame, sizeof(int));
+ stream->read((char*) &distance, sizeof(float));
+ stream->read((char*) position, sizeof(float) * 3);
+ stream->read((char*) orientation, sizeof(float) * 3);
+ stream->read((char*) interpolation, sizeof(char) * 24);
+ stream->read((char*) &angle, sizeof(float));
+ stream->read((char*) unknown, sizeof(char) * 3);
+ }
+
+ void Write(std::ostream *stream)
+ {
+ stream->write((char*)&frame, sizeof(int));
+ stream->write((char*)&distance, sizeof(float));
+ stream->write((char*)position, sizeof(float) * 3);
+ stream->write((char*)orientation, sizeof(float) * 3);
+ stream->write((char*)interpolation, sizeof(char) * 24);
+ stream->write((char*)&angle, sizeof(float));
+ stream->write((char*)unknown, sizeof(char) * 3);
+ }
+ };
+
+ class VmdLightFrame
+ {
+ public:
+ int frame;
+ float color[3];
+ float position[3];
+
+ void Read(std::istream* stream)
+ {
+ stream->read((char*) &frame, sizeof(int));
+ stream->read((char*) color, sizeof(float) * 3);
+ stream->read((char*) position, sizeof(float) * 3);
+ }
+
+ void Write(std::ostream* stream)
+ {
+ stream->write((char*)&frame, sizeof(int));
+ stream->write((char*)color, sizeof(float) * 3);
+ stream->write((char*)position, sizeof(float) * 3);
+ }
+ };
+
+ class VmdIkEnable
+ {
+ public:
+ std::string ik_name;
+ bool enable;
+ };
+
+ class VmdIkFrame
+ {
+ public:
+ int frame;
+ bool display;
+ std::vector<VmdIkEnable> ik_enable;
+
+ void Read(std::istream *stream)
+ {
+ char buffer[20];
+ stream->read((char*) &frame, sizeof(int));
+ stream->read((char*) &display, sizeof(uint8_t));
+ int ik_count;
+ stream->read((char*) &ik_count, sizeof(int));
+ ik_enable.resize(ik_count);
+ for (int i = 0; i < ik_count; i++)
+ {
+ stream->read(buffer, 20);
+ ik_enable[i].ik_name = std::string(buffer);
+ stream->read((char*) &ik_enable[i].enable, sizeof(uint8_t));
+ }
+ }
+
+ void Write(std::ostream *stream)
+ {
+ stream->write((char*)&frame, sizeof(int));
+ stream->write((char*)&display, sizeof(uint8_t));
+ int ik_count = static_cast<int>(ik_enable.size());
+ stream->write((char*)&ik_count, sizeof(int));
+ for (int i = 0; i < ik_count; i++)
+ {
+ const VmdIkEnable& ik_enable = this->ik_enable.at(i);
+ stream->write(ik_enable.ik_name.c_str(), 20);
+ stream->write((char*)&ik_enable.enable, sizeof(uint8_t));
+ }
+ }
+ };
+
+ class VmdMotion
+ {
+ public:
+ std::string model_name;
+ int version;
+ std::vector<VmdBoneFrame> bone_frames;
+ std::vector<VmdFaceFrame> face_frames;
+ std::vector<VmdCameraFrame> camera_frames;
+ std::vector<VmdLightFrame> light_frames;
+ std::vector<VmdIkFrame> ik_frames;
+
+ static std::unique_ptr<VmdMotion> LoadFromFile(char const *filename)
+ {
+ std::ifstream stream(filename, std::ios::binary);
+ auto result = LoadFromStream(&stream);
+ stream.close();
+ return result;
+ }
+
+ static std::unique_ptr<VmdMotion> LoadFromStream(std::ifstream *stream)
+ {
+
+ char buffer[30];
+ auto result = mmd::make_unique<VmdMotion>();
+
+ // magic and version
+ stream->read((char*) buffer, 30);
+ if (strncmp(buffer, "Vocaloid Motion Data", 20))
+ {
+ std::cerr << "invalid vmd file." << std::endl;
+ return nullptr;
+ }
+ result->version = std::atoi(buffer + 20);
+
+ // name
+ stream->read(buffer, 20);
+ result->model_name = std::string(buffer);
+
+ // bone frames
+ int bone_frame_num;
+ stream->read((char*) &bone_frame_num, sizeof(int));
+ result->bone_frames.resize(bone_frame_num);
+ for (int i = 0; i < bone_frame_num; i++)
+ {
+ result->bone_frames[i].Read(stream);
+ }
+
+ // face frames
+ int face_frame_num;
+ stream->read((char*) &face_frame_num, sizeof(int));
+ result->face_frames.resize(face_frame_num);
+ for (int i = 0; i < face_frame_num; i++)
+ {
+ result->face_frames[i].Read(stream);
+ }
+
+ // camera frames
+ int camera_frame_num;
+ stream->read((char*) &camera_frame_num, sizeof(int));
+ result->camera_frames.resize(camera_frame_num);
+ for (int i = 0; i < camera_frame_num; i++)
+ {
+ result->camera_frames[i].Read(stream);
+ }
+
+ // light frames
+ int light_frame_num;
+ stream->read((char*) &light_frame_num, sizeof(int));
+ result->light_frames.resize(light_frame_num);
+ for (int i = 0; i < light_frame_num; i++)
+ {
+ result->light_frames[i].Read(stream);
+ }
+
+ // unknown2
+ stream->read(buffer, 4);
+
+ // ik frames
+ if (stream->peek() != std::ios::traits_type::eof())
+ {
+ int ik_num;
+ stream->read((char*) &ik_num, sizeof(int));
+ result->ik_frames.resize(ik_num);
+ for (int i = 0; i < ik_num; i++)
+ {
+ result->ik_frames[i].Read(stream);
+ }
+ }
+
+ if (stream->peek() != std::ios::traits_type::eof())
+ {
+ std::cerr << "vmd stream has unknown data." << std::endl;
+ }
+
+ return result;
+ }
+
+ bool SaveToFile(const std::u16string& /*filename*/)
+ {
+ // TODO: How to adapt u16string to string?
+ /*
+ std::ofstream stream(filename.c_str(), std::ios::binary);
+ auto result = SaveToStream(&stream);
+ stream.close();
+ return result;
+ */
+ return false;
+ }
+
+ bool SaveToStream(std::ofstream *stream)
+ {
+ std::string magic = "Vocaloid Motion Data 0002\0";
+ magic.resize(30);
+
+ // magic and version
+ stream->write(magic.c_str(), 30);
+
+ // name
+ stream->write(model_name.c_str(), 20);
+
+ // bone frames
+ const int bone_frame_num = static_cast<int>(bone_frames.size());
+ stream->write(reinterpret_cast<const char*>(&bone_frame_num), sizeof(int));
+ for (int i = 0; i < bone_frame_num; i++)
+ {
+ bone_frames[i].Write(stream);
+ }
+
+ // face frames
+ const int face_frame_num = static_cast<int>(face_frames.size());
+ stream->write(reinterpret_cast<const char*>(&face_frame_num), sizeof(int));
+ for (int i = 0; i < face_frame_num; i++)
+ {
+ face_frames[i].Write(stream);
+ }
+
+ // camera frames
+ const int camera_frame_num = static_cast<int>(camera_frames.size());
+ stream->write(reinterpret_cast<const char*>(&camera_frame_num), sizeof(int));
+ for (int i = 0; i < camera_frame_num; i++)
+ {
+ camera_frames[i].Write(stream);
+ }
+
+ // light frames
+ const int light_frame_num = static_cast<int>(light_frames.size());
+ stream->write(reinterpret_cast<const char*>(&light_frame_num), sizeof(int));
+ for (int i = 0; i < light_frame_num; i++)
+ {
+ light_frames[i].Write(stream);
+ }
+
+ // self shadow datas
+ const int self_shadow_num = 0;
+ stream->write(reinterpret_cast<const char*>(&self_shadow_num), sizeof(int));
+
+ // ik frames
+ const int ik_num = static_cast<int>(ik_frames.size());
+ stream->write(reinterpret_cast<const char*>(&ik_num), sizeof(int));
+ for (int i = 0; i < ik_num; i++)
+ {
+ ik_frames[i].Write(stream);
+ }
+
+ return true;
+ }
+ };
+}
diff --git a/thirdparty/assimp/code/MakeVerboseFormat.cpp b/thirdparty/assimp/code/MakeVerboseFormat.cpp
new file mode 100644
index 0000000000..50ff5ed93d
--- /dev/null
+++ b/thirdparty/assimp/code/MakeVerboseFormat.cpp
@@ -0,0 +1,226 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step "MakeVerboseFormat"
+*/
+
+
+#include "MakeVerboseFormat.h"
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+MakeVerboseFormatProcess::MakeVerboseFormatProcess()
+{
+ // nothing to do here
+}
+// ------------------------------------------------------------------------------------------------
+MakeVerboseFormatProcess::~MakeVerboseFormatProcess()
+{
+ // nothing to do here
+}
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void MakeVerboseFormatProcess::Execute( aiScene* pScene)
+{
+ ai_assert(NULL != pScene);
+ ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess begin");
+
+ bool bHas = false;
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if( MakeVerboseFormat( pScene->mMeshes[a]))
+ bHas = true;
+ }
+ if (bHas) {
+ ASSIMP_LOG_INFO("MakeVerboseFormatProcess finished. There was much work to do ...");
+ } else {
+ ASSIMP_LOG_DEBUG("MakeVerboseFormatProcess. There was nothing to do.");
+ }
+
+ pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
+{
+ ai_assert(NULL != pcMesh);
+
+ unsigned int iOldNumVertices = pcMesh->mNumVertices;
+ const unsigned int iNumVerts = pcMesh->mNumFaces*3;
+
+ aiVector3D* pvPositions = new aiVector3D[ iNumVerts ];
+
+ aiVector3D* pvNormals = NULL;
+ if (pcMesh->HasNormals())
+ {
+ pvNormals = new aiVector3D[iNumVerts];
+ }
+ aiVector3D* pvTangents = NULL, *pvBitangents = NULL;
+ if (pcMesh->HasTangentsAndBitangents())
+ {
+ pvTangents = new aiVector3D[iNumVerts];
+ pvBitangents = new aiVector3D[iNumVerts];
+ }
+
+ aiVector3D* apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = {0};
+ aiColor4D* apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = {0};
+
+ unsigned int p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ apvTextureCoords[p++] = new aiVector3D[iNumVerts];
+
+ p = 0;
+ while (pcMesh->HasVertexColors(p))
+ apvColorSets[p++] = new aiColor4D[iNumVerts];
+
+ // allocate enough memory to hold output bones and vertex weights ...
+ std::vector<aiVertexWeight>* newWeights = new std::vector<aiVertexWeight>[pcMesh->mNumBones];
+ for (unsigned int i = 0;i < pcMesh->mNumBones;++i) {
+ newWeights[i].reserve(pcMesh->mBones[i]->mNumWeights*3);
+ }
+
+ // iterate through all faces and build a clean list
+ unsigned int iIndex = 0;
+ for (unsigned int a = 0; a< pcMesh->mNumFaces;++a)
+ {
+ aiFace* pcFace = &pcMesh->mFaces[a];
+ for (unsigned int q = 0; q < pcFace->mNumIndices;++q,++iIndex)
+ {
+ // need to build a clean list of bones, too
+ for (unsigned int i = 0;i < pcMesh->mNumBones;++i)
+ {
+ for (unsigned int a = 0; a < pcMesh->mBones[i]->mNumWeights;a++)
+ {
+ const aiVertexWeight& w = pcMesh->mBones[i]->mWeights[a];
+ if(pcFace->mIndices[q] == w.mVertexId)
+ {
+ aiVertexWeight wNew;
+ wNew.mVertexId = iIndex;
+ wNew.mWeight = w.mWeight;
+ newWeights[i].push_back(wNew);
+ }
+ }
+ }
+
+ pvPositions[iIndex] = pcMesh->mVertices[pcFace->mIndices[q]];
+
+ if (pcMesh->HasNormals())
+ {
+ pvNormals[iIndex] = pcMesh->mNormals[pcFace->mIndices[q]];
+ }
+ if (pcMesh->HasTangentsAndBitangents())
+ {
+ pvTangents[iIndex] = pcMesh->mTangents[pcFace->mIndices[q]];
+ pvBitangents[iIndex] = pcMesh->mBitangents[pcFace->mIndices[q]];
+ }
+
+ unsigned int p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ {
+ apvTextureCoords[p][iIndex] = pcMesh->mTextureCoords[p][pcFace->mIndices[q]];
+ ++p;
+ }
+ p = 0;
+ while (pcMesh->HasVertexColors(p))
+ {
+ apvColorSets[p][iIndex] = pcMesh->mColors[p][pcFace->mIndices[q]];
+ ++p;
+ }
+ pcFace->mIndices[q] = iIndex;
+ }
+ }
+
+
+
+ // build output vertex weights
+ for (unsigned int i = 0;i < pcMesh->mNumBones;++i)
+ {
+ delete [] pcMesh->mBones[i]->mWeights;
+ if (!newWeights[i].empty()) {
+ pcMesh->mBones[i]->mWeights = new aiVertexWeight[newWeights[i].size()];
+ aiVertexWeight *weightToCopy = &( newWeights[i][0] );
+ memcpy(pcMesh->mBones[i]->mWeights, weightToCopy,
+ sizeof(aiVertexWeight) * newWeights[i].size());
+ } else {
+ pcMesh->mBones[i]->mWeights = NULL;
+ }
+ }
+ delete[] newWeights;
+
+ // delete the old members
+ delete[] pcMesh->mVertices;
+ pcMesh->mVertices = pvPositions;
+
+ p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ {
+ delete[] pcMesh->mTextureCoords[p];
+ pcMesh->mTextureCoords[p] = apvTextureCoords[p];
+ ++p;
+ }
+ p = 0;
+ while (pcMesh->HasVertexColors(p))
+ {
+ delete[] pcMesh->mColors[p];
+ pcMesh->mColors[p] = apvColorSets[p];
+ ++p;
+ }
+ pcMesh->mNumVertices = iNumVerts;
+
+ if (pcMesh->HasNormals())
+ {
+ delete[] pcMesh->mNormals;
+ pcMesh->mNormals = pvNormals;
+ }
+ if (pcMesh->HasTangentsAndBitangents())
+ {
+ delete[] pcMesh->mTangents;
+ pcMesh->mTangents = pvTangents;
+ delete[] pcMesh->mBitangents;
+ pcMesh->mBitangents = pvBitangents;
+ }
+ return (pcMesh->mNumVertices != iOldNumVertices);
+}
diff --git a/thirdparty/assimp/code/MakeVerboseFormat.h b/thirdparty/assimp/code/MakeVerboseFormat.h
new file mode 100644
index 0000000000..d12db63ae1
--- /dev/null
+++ b/thirdparty/assimp/code/MakeVerboseFormat.h
@@ -0,0 +1,105 @@
+/*
+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 bring a given scene
+ into the verbose format that is expected by most postprocess steps.
+ This is the inverse of the "JoinIdenticalVertices" step. */
+#ifndef AI_MAKEVERBOSEFORMAT_H_INC
+#define AI_MAKEVERBOSEFORMAT_H_INC
+
+#include "BaseProcess.h"
+struct aiMesh;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** MakeVerboseFormatProcess: Class to convert an asset to the verbose
+ * format which is expected by most postprocess steps.
+ *
+ * This is the inverse of what the "JoinIdenticalVertices" step is doing.
+ * This step has no official flag (since it wouldn't make sense to run it
+ * during import). It is intended for applications intending to modify the
+ * returned aiScene. After this step has been executed, they can execute
+ * other postprocess steps on the data. The code might also be useful to
+ * quickly adapt code that doesn't result in a verbose representation of
+ * the scene data.
+ * The step has been added because it was required by the viewer, however
+ * it has been moved to the main library since others might find it
+ * useful, too. */
+class ASSIMP_API_WINONLY MakeVerboseFormatProcess : public BaseProcess
+{
+public:
+
+
+ MakeVerboseFormatProcess();
+ ~MakeVerboseFormatProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not */
+ bool IsActive( unsigned int /*pFlags*/ ) const
+ {
+ // NOTE: There is no direct flag that corresponds to
+ // this postprocess step.
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+private:
+
+ //! Apply the postprocess step to a given submesh
+ bool MakeVerboseFormat (aiMesh* pcMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_KILLNORMALPROCESS_H_INC
diff --git a/thirdparty/assimp/code/MaterialSystem.cpp b/thirdparty/assimp/code/MaterialSystem.cpp
new file mode 100644
index 0000000000..03d5a18a34
--- /dev/null
+++ b/thirdparty/assimp/code/MaterialSystem.cpp
@@ -0,0 +1,647 @@
+/*
+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 MaterialSystem.cpp
+ * @brief Implementation of the material system of the library
+ */
+
+#include <assimp/Hash.h>
+#include <assimp/fast_atof.h>
+#include <assimp/ParsingUtils.h>
+#include "MaterialSystem.h"
+#include <assimp/types.h>
+#include <assimp/material.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Macros.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Get a specific property from a material
+aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ const aiMaterialProperty** pPropOut)
+{
+ ai_assert( pMat != NULL );
+ ai_assert( pKey != NULL );
+ ai_assert( pPropOut != NULL );
+
+ /* Just search for a property with exactly this name ..
+ * could be improved by hashing, but it's possibly
+ * no worth the effort (we're bound to C structures,
+ * thus std::map or derivates are not applicable. */
+ for ( unsigned int i = 0; i < pMat->mNumProperties; ++i ) {
+ aiMaterialProperty* prop = pMat->mProperties[i];
+
+ if (prop /* just for safety ... */
+ && 0 == strcmp( prop->mKey.data, pKey )
+ && (UINT_MAX == type || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */
+ && (UINT_MAX == index || prop->mIndex == index))
+ {
+ *pPropOut = pMat->mProperties[i];
+ return AI_SUCCESS;
+ }
+ }
+ *pPropOut = NULL;
+ return AI_FAILURE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get an array of floating-point values from the material.
+aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ ai_real* pOut,
+ unsigned int* pMax)
+{
+ ai_assert( pOut != NULL );
+ ai_assert( pMat != NULL );
+
+ const aiMaterialProperty* prop;
+ aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop);
+ if (!prop) {
+ return AI_FAILURE;
+ }
+
+ // data is given in floats, convert to ai_real
+ unsigned int iWrite = 0;
+ if( aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(float);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<ai_real> ( reinterpret_cast<float*>(prop->mData)[a] );
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // data is given in doubles, convert to float
+ else if( aiPTI_Double == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(double);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<ai_real> ( reinterpret_cast<double*>(prop->mData)[a] );
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // data is given in ints, convert to float
+ else if( aiPTI_Integer == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(int32_t);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<ai_real> ( reinterpret_cast<int32_t*>(prop->mData)[a] );
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // a string ... read floats separated by spaces
+ else {
+ if (pMax) {
+ iWrite = *pMax;
+ }
+ // strings are zero-terminated with a 32 bit length prefix, so this is safe
+ const char *cur = prop->mData + 4;
+ ai_assert( prop->mDataLength >= 5 );
+ ai_assert( !prop->mData[ prop->mDataLength - 1 ] );
+ for ( unsigned int a = 0; ;++a) {
+ cur = fast_atoreal_move<ai_real>(cur,pOut[a]);
+ if ( a==iWrite-1 ) {
+ break;
+ }
+ if ( !IsSpace(*cur) ) {
+ ASSIMP_LOG_ERROR("Material property" + std::string(pKey) +
+ " is a string; failed to parse a float array out of it.");
+ return AI_FAILURE;
+ }
+ }
+
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get an array if integers from the material
+aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ int* pOut,
+ unsigned int* pMax)
+{
+ ai_assert( pOut != NULL );
+ ai_assert( pMat != NULL );
+
+ const aiMaterialProperty* prop;
+ aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**) &prop);
+ if (!prop) {
+ return AI_FAILURE;
+ }
+
+ // data is given in ints, simply copy it
+ unsigned int iWrite = 0;
+ if( aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) {
+ iWrite = std::max(static_cast<unsigned int>(prop->mDataLength / sizeof(int32_t)), 1u);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite);
+ }
+ if (1 == prop->mDataLength) {
+ // bool type, 1 byte
+ *pOut = static_cast<int>(*prop->mData);
+ }
+ else {
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<int>(reinterpret_cast<int32_t*>(prop->mData)[a]);
+ }
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // data is given in floats convert to int
+ else if( aiPTI_Float == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(float);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<int>(reinterpret_cast<float*>(prop->mData)[a]);
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // it is a string ... no way to read something out of this
+ else {
+ if (pMax) {
+ iWrite = *pMax;
+ }
+ // strings are zero-terminated with a 32 bit length prefix, so this is safe
+ const char *cur = prop->mData+4;
+ ai_assert( prop->mDataLength >= 5 );
+ ai_assert( !prop->mData[ prop->mDataLength - 1 ] );
+ for (unsigned int a = 0; ;++a) {
+ pOut[a] = strtol10(cur,&cur);
+ if(a==iWrite-1) {
+ break;
+ }
+ if(!IsSpace(*cur)) {
+ ASSIMP_LOG_ERROR("Material property" + std::string(pKey) +
+ " is a string; failed to parse an integer array out of it.");
+ return AI_FAILURE;
+ }
+ }
+
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a color (3 or 4 floats) from the material
+aiReturn aiGetMaterialColor(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ aiColor4D* pOut)
+{
+ unsigned int iMax = 4;
+ const aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax);
+
+ // if no alpha channel is defined: set it to 1.0
+ if (3 == iMax) {
+ pOut->a = 1.0;
+ }
+
+ return eRet;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a aiUVTransform (4 floats) from the material
+aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ aiUVTransform* pOut)
+{
+ unsigned int iMax = 4;
+ return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a string from the material
+aiReturn aiGetMaterialString(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ aiString* pOut)
+{
+ ai_assert (pOut != NULL);
+
+ const aiMaterialProperty* prop;
+ aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**)&prop);
+ if (!prop) {
+ return AI_FAILURE;
+ }
+
+ if( aiPTI_String == prop->mType) {
+ ai_assert(prop->mDataLength>=5);
+
+ // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data
+ pOut->length = static_cast<unsigned int>(*reinterpret_cast<uint32_t*>(prop->mData));
+
+ ai_assert( pOut->length+1+4==prop->mDataLength );
+ ai_assert( !prop->mData[ prop->mDataLength - 1 ] );
+ memcpy(pOut->data,prop->mData+4,pOut->length+1);
+ }
+ else {
+ // TODO - implement lexical cast as well
+ ASSIMP_LOG_ERROR("Material property" + std::string(pKey) +
+ " was found, but is no string" );
+ return AI_FAILURE;
+ }
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the number of textures on a particular texture stack
+unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat,
+ C_ENUM aiTextureType type)
+{
+ ai_assert (pMat != NULL);
+
+ // Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again)
+ unsigned int max = 0;
+ for (unsigned int i = 0; i < pMat->mNumProperties;++i) {
+ aiMaterialProperty* prop = pMat->mProperties[i];
+
+ if ( prop /* just a sanity check ... */
+ && 0 == strcmp( prop->mKey.data, _AI_MATKEY_TEXTURE_BASE )
+ && prop->mSemantic == type) {
+
+ max = std::max(max,prop->mIndex+1);
+ }
+ }
+ return max;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat,
+ aiTextureType type,
+ unsigned int index,
+ C_STRUCT aiString* path,
+ aiTextureMapping* _mapping /*= NULL*/,
+ unsigned int* uvindex /*= NULL*/,
+ ai_real* blend /*= NULL*/,
+ aiTextureOp* op /*= NULL*/,
+ aiTextureMapMode* mapmode /*= NULL*/,
+ unsigned int* flags /*= NULL*/
+ )
+{
+ ai_assert( NULL != mat );
+ ai_assert( NULL != path );
+
+ // Get the path to the texture
+ if (AI_SUCCESS != aiGetMaterialString(mat,AI_MATKEY_TEXTURE(type,index),path)) {
+ return AI_FAILURE;
+ }
+
+ // Determine mapping type
+ int mapping_ = static_cast<int>(aiTextureMapping_UV);
+ aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index), &mapping_);
+ aiTextureMapping mapping = static_cast<aiTextureMapping>(mapping_);
+ if (_mapping)
+ *_mapping = mapping;
+
+ // Get UV index
+ if (aiTextureMapping_UV == mapping && uvindex) {
+ aiGetMaterialInteger(mat,AI_MATKEY_UVWSRC(type,index),(int*)uvindex);
+ }
+ // Get blend factor
+ if (blend) {
+ aiGetMaterialFloat(mat,AI_MATKEY_TEXBLEND(type,index),blend);
+ }
+ // Get texture operation
+ if (op){
+ aiGetMaterialInteger(mat,AI_MATKEY_TEXOP(type,index),(int*)op);
+ }
+ // Get texture mapping modes
+ if (mapmode) {
+ aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_U(type,index),(int*)&mapmode[0]);
+ aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_V(type,index),(int*)&mapmode[1]);
+ }
+ // Get texture flags
+ if (flags){
+ aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags);
+ }
+
+ return AI_SUCCESS;
+}
+
+
+static const unsigned int DefaultNumAllocated = 5;
+
+// ------------------------------------------------------------------------------------------------
+// Construction. Actually the one and only way to get an aiMaterial instance
+aiMaterial::aiMaterial()
+: mProperties( nullptr )
+, mNumProperties( 0 )
+, mNumAllocated( DefaultNumAllocated ) {
+ // Allocate 5 entries by default
+ mProperties = new aiMaterialProperty*[ DefaultNumAllocated ];
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMaterial::~aiMaterial()
+{
+ Clear();
+
+ delete[] mProperties;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiString aiMaterial::GetName() {
+ aiString name;
+ Get(AI_MATKEY_NAME, name);
+
+ return name;
+}
+
+// ------------------------------------------------------------------------------------------------
+void aiMaterial::Clear()
+{
+ for ( unsigned int i = 0; i < mNumProperties; ++i ) {
+ // delete this entry
+ delete mProperties[ i ];
+ AI_DEBUG_INVALIDATE_PTR(mProperties[i]);
+ }
+ mNumProperties = 0;
+
+ // The array remains allocated, we just invalidated its contents
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn aiMaterial::RemoveProperty ( const char* pKey,unsigned int type, unsigned int index )
+{
+ ai_assert( nullptr != pKey );
+
+ for (unsigned int i = 0; i < mNumProperties;++i) {
+ aiMaterialProperty* prop = mProperties[i];
+
+ if (prop && !strcmp( prop->mKey.data, pKey ) &&
+ prop->mSemantic == type && prop->mIndex == index)
+ {
+ // Delete this entry
+ delete mProperties[i];
+
+ // collapse the array behind --.
+ --mNumProperties;
+ for (unsigned int a = i; a < mNumProperties;++a) {
+ mProperties[a] = mProperties[a+1];
+ }
+ return AI_SUCCESS;
+ }
+ }
+
+ return AI_FAILURE;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn aiMaterial::AddBinaryProperty (const void* pInput,
+ unsigned int pSizeInBytes,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ aiPropertyTypeInfo pType
+ )
+{
+ ai_assert( pInput != NULL );
+ ai_assert( pKey != NULL );
+ ai_assert( 0 != pSizeInBytes );
+
+ if ( 0 == pSizeInBytes ) {
+
+ }
+
+ // first search the list whether there is already an entry with this key
+ unsigned int iOutIndex( UINT_MAX );
+ for ( unsigned int i = 0; i < mNumProperties; ++i ) {
+ aiMaterialProperty *prop( mProperties[ i ] );
+
+ if (prop /* just for safety */ && !strcmp( prop->mKey.data, pKey ) &&
+ prop->mSemantic == type && prop->mIndex == index){
+
+ delete mProperties[i];
+ iOutIndex = i;
+ }
+ }
+
+ // Allocate a new material property
+ aiMaterialProperty* pcNew = new aiMaterialProperty();
+
+ // .. and fill it
+ pcNew->mType = pType;
+ pcNew->mSemantic = type;
+ pcNew->mIndex = index;
+
+ pcNew->mDataLength = pSizeInBytes;
+ pcNew->mData = new char[pSizeInBytes];
+ memcpy (pcNew->mData,pInput,pSizeInBytes);
+
+ pcNew->mKey.length = ::strlen(pKey);
+ ai_assert ( MAXLEN > pcNew->mKey.length);
+ strcpy( pcNew->mKey.data, pKey );
+
+ if (UINT_MAX != iOutIndex) {
+ mProperties[iOutIndex] = pcNew;
+ return AI_SUCCESS;
+ }
+
+ // resize the array ... double the storage allocated
+ if (mNumProperties == mNumAllocated) {
+ const unsigned int iOld = mNumAllocated;
+ mNumAllocated *= 2;
+
+ aiMaterialProperty** ppTemp;
+ try {
+ ppTemp = new aiMaterialProperty*[mNumAllocated];
+ } catch (std::bad_alloc&) {
+ delete pcNew;
+ return AI_OUTOFMEMORY;
+ }
+
+ // just copy all items over; then replace the old array
+ memcpy (ppTemp,mProperties,iOld * sizeof(void*));
+
+ delete[] mProperties;
+ mProperties = ppTemp;
+ }
+ // push back ...
+ mProperties[mNumProperties++] = pcNew;
+
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn aiMaterial::AddProperty (const aiString* pInput,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ // We don't want to add the whole buffer .. write a 32 bit length
+ // prefix followed by the zero-terminated UTF8 string.
+ // (HACK) I don't want to break the ABI now, but we definitely
+ // ought to change aiString::mLength to uint32_t one day.
+ if (sizeof(size_t) == 8) {
+ aiString copy = *pInput;
+ uint32_t* s = reinterpret_cast<uint32_t*>(&copy.length);
+ s[1] = static_cast<uint32_t>(pInput->length);
+
+ return AddBinaryProperty(s+1,
+ static_cast<unsigned int>(pInput->length+1+4),
+ pKey,
+ type,
+ index,
+ aiPTI_String);
+ }
+ ai_assert(sizeof(size_t)==4);
+ return AddBinaryProperty(pInput,
+ static_cast<unsigned int>(pInput->length+1+4),
+ pKey,
+ type,
+ index,
+ aiPTI_String);
+}
+
+// ------------------------------------------------------------------------------------------------
+uint32_t Assimp::ComputeMaterialHash(const aiMaterial* mat, bool includeMatName /*= false*/)
+{
+ uint32_t hash = 1503; // magic start value, chosen to be my birthday :-)
+ for ( unsigned int i = 0; i < mat->mNumProperties; ++i ) {
+ aiMaterialProperty* prop;
+
+ // Exclude all properties whose first character is '?' from the hash
+ // See doc for aiMaterialProperty.
+ if ((prop = mat->mProperties[i]) && (includeMatName || prop->mKey.data[0] != '?')) {
+
+ hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash);
+ hash = SuperFastHash(prop->mData,prop->mDataLength,hash);
+
+ // Combine the semantic and the index with the hash
+ hash = SuperFastHash((const char*)&prop->mSemantic,sizeof(unsigned int),hash);
+ hash = SuperFastHash((const char*)&prop->mIndex,sizeof(unsigned int),hash);
+ }
+ }
+ return hash;
+}
+
+// ------------------------------------------------------------------------------------------------
+void aiMaterial::CopyPropertyList(aiMaterial* pcDest,
+ const aiMaterial* pcSrc
+ )
+{
+ ai_assert(NULL != pcDest);
+ ai_assert(NULL != pcSrc);
+
+ unsigned int iOldNum = pcDest->mNumProperties;
+ pcDest->mNumAllocated += pcSrc->mNumAllocated;
+ pcDest->mNumProperties += pcSrc->mNumProperties;
+
+ aiMaterialProperty** pcOld = pcDest->mProperties;
+ pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated];
+
+ if (iOldNum && pcOld) {
+ for (unsigned int i = 0; i < iOldNum;++i) {
+ pcDest->mProperties[i] = pcOld[i];
+ }
+ }
+
+ if ( pcOld ) {
+ delete[] pcOld;
+ }
+
+ for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i) {
+ aiMaterialProperty* propSrc = pcSrc->mProperties[i];
+
+ // search whether we have already a property with this name -> if yes, overwrite it
+ aiMaterialProperty* prop;
+ for ( unsigned int q = 0; q < iOldNum; ++q ) {
+ prop = pcDest->mProperties[q];
+ if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic
+ && prop->mIndex == propSrc->mIndex) {
+ delete prop;
+
+ // collapse the whole array ...
+ memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q);
+ i--;
+ pcDest->mNumProperties--;
+ }
+ }
+
+ // Allocate the output property and copy the source property
+ prop = pcDest->mProperties[i] = new aiMaterialProperty();
+ prop->mKey = propSrc->mKey;
+ prop->mDataLength = propSrc->mDataLength;
+ prop->mType = propSrc->mType;
+ prop->mSemantic = propSrc->mSemantic;
+ prop->mIndex = propSrc->mIndex;
+
+ prop->mData = new char[propSrc->mDataLength];
+ memcpy(prop->mData,propSrc->mData,prop->mDataLength);
+ }
+}
diff --git a/thirdparty/assimp/code/MaterialSystem.h b/thirdparty/assimp/code/MaterialSystem.h
new file mode 100644
index 0000000000..67d53578cb
--- /dev/null
+++ b/thirdparty/assimp/code/MaterialSystem.h
@@ -0,0 +1,72 @@
+/*
+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 MaterialSystem.h
+ * Now that #MaterialHelper is gone, this file only contains some
+ * internal material utility functions.
+ */
+#ifndef AI_MATERIALSYSTEM_H_INC
+#define AI_MATERIALSYSTEM_H_INC
+
+#include <stdint.h>
+
+struct aiMaterial;
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------
+/** Computes a hash (hopefully unique) from all material properties
+ * The hash value reflects the current property state, so if you add any
+ * property and call this method again, the resulting hash value will be
+ * different. The hash is not persistent across different builds and platforms.
+ *
+ * @param includeMatName Set to 'true' to take all properties with
+ * '?' as initial character in their name into account.
+ * Currently #AI_MATKEY_NAME is the only example.
+ * @return 32 Bit jash value for the material
+ */
+uint32_t ComputeMaterialHash(const aiMaterial* mat, bool includeMatName = false);
+
+
+} // ! namespace Assimp
+
+#endif //!! AI_MATERIALSYSTEM_H_INC
diff --git a/thirdparty/assimp/code/OptimizeGraph.cpp b/thirdparty/assimp/code/OptimizeGraph.cpp
new file mode 100644
index 0000000000..add9ab79e1
--- /dev/null
+++ b/thirdparty/assimp/code/OptimizeGraph.cpp
@@ -0,0 +1,353 @@
+/*
+---------------------------------------------------------------------------
+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 OptimizeGraph.cpp
+ * @brief Implementation of the aiProcess_OptimizGraph step
+ */
+
+
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
+
+#include "OptimizeGraph.h"
+#include "ProcessHelper.h"
+#include <assimp/SceneCombiner.h>
+#include <assimp/Exceptional.h>
+#include <stdio.h>
+
+using namespace Assimp;
+
+#define AI_RESERVED_NODE_NAME "$Reserved_And_Evil"
+
+/* AI_OG_USE_HASHING enables the use of hashing to speed-up std::set lookups.
+ * The unhashed variant should be faster, except for *very* large data sets
+ */
+#ifdef AI_OG_USE_HASHING
+ // Use our standard hashing function to compute the hash
+# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length)
+#else
+ // Otherwise hope that std::string will utilize a static buffer
+ // for shorter node names. This would avoid endless heap copying.
+# define AI_OG_GETKEY(str) std::string(str.data)
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OptimizeGraphProcess::OptimizeGraphProcess()
+: mScene()
+, nodes_in()
+, nodes_out()
+, count_merged() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OptimizeGraphProcess::~OptimizeGraphProcess() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const {
+ return (0 != (pFlags & aiProcess_OptimizeGraph));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties for the post-processing step
+void OptimizeGraphProcess::SetupProperties(const Importer* pImp) {
+ // Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST
+ std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,"");
+ AddLockedNodeList(tmp);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Collect new children
+void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes) {
+ nodes_in += nd->mNumChildren;
+
+ // Process children
+ std::list<aiNode*> child_nodes;
+ for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
+ CollectNewChildren(nd->mChildren[i],child_nodes);
+ nd->mChildren[i] = nullptr;
+ }
+
+ // Check whether we need this node; if not we can replace it by our own children (warn, danger of incest).
+ if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) {
+ for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
+
+ if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) {
+ (*it)->mTransformation = nd->mTransformation * (*it)->mTransformation;
+ nodes.push_back(*it);
+
+ it = child_nodes.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (nd->mNumMeshes || !child_nodes.empty()) {
+ nodes.push_back(nd);
+ } else {
+ delete nd; /* bye, node */
+ return;
+ }
+ } else {
+
+ // Retain our current position in the hierarchy
+ nodes.push_back(nd);
+
+ // Now check for possible optimizations in our list of child nodes. join as many as possible
+ aiNode* join_master = NULL;
+ aiMatrix4x4 inv;
+
+ const LockedSetType::const_iterator end = locked.end();
+
+ std::list<aiNode*> join;
+ for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
+ aiNode* child = *it;
+ if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) {
+
+ // There may be no instanced meshes
+ unsigned int n = 0;
+ for (; n < child->mNumMeshes;++n) {
+ if (meshes[child->mMeshes[n]] > 1) {
+ break;
+ }
+ }
+ if (n == child->mNumMeshes) {
+ if (!join_master) {
+ join_master = child;
+ inv = join_master->mTransformation;
+ inv.Inverse();
+ } else {
+ child->mTransformation = inv * child->mTransformation ;
+
+ join.push_back(child);
+ it = child_nodes.erase(it);
+ continue;
+ }
+ }
+ }
+ ++it;
+ }
+ if (join_master && !join.empty()) {
+ join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i",count_merged++);
+
+ unsigned int out_meshes = 0;
+ for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) {
+ out_meshes += (*it)->mNumMeshes;
+ }
+
+ // copy all mesh references in one array
+ if (out_meshes) {
+ unsigned int* meshes = new unsigned int[out_meshes+join_master->mNumMeshes], *tmp = meshes;
+ for (unsigned int n = 0; n < join_master->mNumMeshes;++n) {
+ *tmp++ = join_master->mMeshes[n];
+ }
+
+ for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) {
+ for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) {
+
+ *tmp = (*it)->mMeshes[n];
+ aiMesh* mesh = mScene->mMeshes[*tmp++];
+
+ // manually move the mesh into the right coordinate system
+ const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose();
+ for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
+
+ mesh->mVertices[a] *= (*it)->mTransformation;
+
+ if (mesh->HasNormals())
+ mesh->mNormals[a] *= IT;
+
+ if (mesh->HasTangentsAndBitangents()) {
+ mesh->mTangents[a] *= IT;
+ mesh->mBitangents[a] *= IT;
+ }
+ }
+ }
+ delete *it; // bye, node
+ }
+ delete[] join_master->mMeshes;
+ join_master->mMeshes = meshes;
+ join_master->mNumMeshes += out_meshes;
+ }
+ }
+ }
+ // reassign children if something changed
+ if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) {
+
+ delete[] nd->mChildren;
+
+ if (!child_nodes.empty()) {
+ nd->mChildren = new aiNode*[child_nodes.size()];
+ }
+ else nd->mChildren = nullptr;
+ }
+
+ nd->mNumChildren = static_cast<unsigned int>(child_nodes.size());
+
+ if (nd->mChildren) {
+ aiNode** tmp = nd->mChildren;
+ for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
+ aiNode* node = *tmp++ = *it;
+ node->mParent = nd;
+ }
+ }
+
+ nodes_out += static_cast<unsigned int>(child_nodes.size());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Execute the post-processing step on the given scene
+void OptimizeGraphProcess::Execute( aiScene* pScene) {
+ ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin");
+ nodes_in = nodes_out = count_merged = 0;
+ mScene = pScene;
+
+ meshes.resize(pScene->mNumMeshes,0);
+ FindInstancedMeshes(pScene->mRootNode);
+
+ // build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it
+ locked.clear();
+ for (std::list<std::string>::const_iterator it = locked_nodes.begin(); it != locked_nodes.end(); ++it) {
+#ifdef AI_OG_USE_HASHING
+ locked.insert(SuperFastHash((*it).c_str()));
+#else
+ locked.insert(*it);
+#endif
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {
+ for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) {
+ aiNodeAnim* anim = pScene->mAnimations[i]->mChannels[a];
+ locked.insert(AI_OG_GETKEY(anim->mNodeName));
+ }
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) {
+
+ aiBone* bone = pScene->mMeshes[i]->mBones[a];
+ locked.insert(AI_OG_GETKEY(bone->mName));
+
+ // HACK: Meshes referencing bones may not be transformed; we need to look them.
+ // The easiest way to do this is to increase their reference counters ...
+ meshes[i] += 2;
+ }
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
+ aiCamera* cam = pScene->mCameras[i];
+ locked.insert(AI_OG_GETKEY(cam->mName));
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
+ aiLight* lgh = pScene->mLights[i];
+ locked.insert(AI_OG_GETKEY(lgh->mName));
+ }
+
+ // Insert a dummy master node and make it read-only
+ aiNode* dummy_root = new aiNode(AI_RESERVED_NODE_NAME);
+ locked.insert(AI_OG_GETKEY(dummy_root->mName));
+
+ const aiString prev = pScene->mRootNode->mName;
+ pScene->mRootNode->mParent = dummy_root;
+
+ dummy_root->mChildren = new aiNode*[dummy_root->mNumChildren = 1];
+ dummy_root->mChildren[0] = pScene->mRootNode;
+
+ // Do our recursive processing of scenegraph nodes. For each node collect
+ // a fully new list of children and allow their children to place themselves
+ // on the same hierarchy layer as their parents.
+ std::list<aiNode*> nodes;
+ CollectNewChildren (dummy_root,nodes);
+
+ ai_assert(nodes.size() == 1);
+
+ if (dummy_root->mNumChildren == 0) {
+ pScene->mRootNode = NULL;
+ throw DeadlyImportError("After optimizing the scene graph, no data remains");
+ }
+
+ if (dummy_root->mNumChildren > 1) {
+ pScene->mRootNode = dummy_root;
+
+ // Keep the dummy node but assign the name of the old root node to it
+ pScene->mRootNode->mName = prev;
+ }
+ else {
+
+ // Remove the dummy root node again.
+ pScene->mRootNode = dummy_root->mChildren[0];
+
+ dummy_root->mChildren[0] = NULL;
+ delete dummy_root;
+ }
+
+ pScene->mRootNode->mParent = NULL;
+ if (!DefaultLogger::isNullLogger()) {
+ if ( nodes_in != nodes_out) {
+ ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out);
+ } else {
+ ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished");
+ }
+ }
+ meshes.clear();
+ locked.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a LUT of all instanced meshes
+void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode)
+{
+ for (unsigned int i = 0; i < pNode->mNumMeshes;++i) {
+ ++meshes[pNode->mMeshes[i]];
+ }
+
+ for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
+ FindInstancedMeshes(pNode->mChildren[i]);
+}
+
+#endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
diff --git a/thirdparty/assimp/code/OptimizeGraph.h b/thirdparty/assimp/code/OptimizeGraph.h
new file mode 100644
index 0000000000..e5bbed7679
--- /dev/null
+++ b/thirdparty/assimp/code/OptimizeGraph.h
@@ -0,0 +1,145 @@
+/*
+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 OptimizeGraph.h
+ * @brief Declares a post processing step to optimize the scenegraph
+ */
+#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC
+#define AI_OPTIMIZEGRAPHPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "ProcessHelper.h"
+#include <assimp/types.h>
+#include <set>
+
+struct aiMesh;
+class OptimizeGraphProcessTest;
+namespace Assimp {
+
+// -----------------------------------------------------------------------------
+/** @brief Postprocessing step to optimize the scenegraph
+ *
+ * The implementation tries to merge nodes, even if they use different
+ * transformations. Animations are preserved.
+ *
+ * @see aiProcess_OptimizeGraph for a detailed description of the
+ * algorithm being applied.
+ */
+class OptimizeGraphProcess : public BaseProcess
+{
+public:
+
+ OptimizeGraphProcess();
+ ~OptimizeGraphProcess();
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Add a list of node names to be locked and not modified.
+ * @param in List of nodes. See #AI_CONFIG_PP_OG_EXCLUDE_LIST for
+ * format explanations.
+ */
+ inline void AddLockedNodeList(std::string& in)
+ {
+ ConvertListToStrings (in,locked_nodes);
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Add another node to be locked and not modified.
+ * @param name Name to be locked
+ */
+ inline void AddLockedNode(std::string& name)
+ {
+ locked_nodes.push_back(name);
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Remove a node from the list of locked nodes.
+ * @param name Name to be unlocked
+ */
+ inline void RemoveLockedNode(std::string& name)
+ {
+ locked_nodes.remove(name);
+ }
+
+protected:
+
+ void CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes);
+ void FindInstancedMeshes (aiNode* pNode);
+
+private:
+
+#ifdef AI_OG_USE_HASHING
+ typedef std::set<unsigned int> LockedSetType;
+#else
+ typedef std::set<std::string> LockedSetType;
+#endif
+
+
+ //! Scene we're working with
+ aiScene* mScene;
+
+ //! List of locked names. Stored is the hash of the name
+ LockedSetType locked;
+
+ //! List of nodes to be locked in addition to those with animations, lights or cameras assigned.
+ std::list<std::string> locked_nodes;
+
+ //! Node counters for logging purposes
+ unsigned int nodes_in,nodes_out, count_merged;
+
+ //! Reference counters for meshes
+ std::vector<unsigned int> meshes;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_OPTIMIZEGRAPHPROCESS_H_INC
diff --git a/thirdparty/assimp/code/OptimizeMeshes.cpp b/thirdparty/assimp/code/OptimizeMeshes.cpp
new file mode 100644
index 0000000000..3f6765f6ca
--- /dev/null
+++ b/thirdparty/assimp/code/OptimizeMeshes.cpp
@@ -0,0 +1,256 @@
+/*
+---------------------------------------------------------------------------
+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 OptimizeMeshes.cpp
+ * @brief Implementation of the aiProcess_OptimizeMeshes step
+ */
+
+
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
+
+
+#include "OptimizeMeshes.h"
+#include "ProcessHelper.h"
+#include <assimp/SceneCombiner.h>
+#include <assimp/Exceptional.h>
+
+using namespace Assimp;
+
+static const unsigned int NotSet = 0xffffffff;
+static const unsigned int DeadBeef = 0xdeadbeef;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OptimizeMeshesProcess::OptimizeMeshesProcess()
+ : mScene()
+ , pts(false)
+ , max_verts( NotSet )
+ , max_faces( NotSet ) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OptimizeMeshesProcess::~OptimizeMeshesProcess() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const
+{
+ // Our behaviour needs to be different if the SortByPType or SplitLargeMeshes
+ // steps are active. Thus we need to query their flags here and store the
+ // information, although we're breaking const-correctness.
+ // That's a serious design flaw, consider redesign.
+ if( 0 != (pFlags & aiProcess_OptimizeMeshes) ) {
+ pts = (0 != (pFlags & aiProcess_SortByPType));
+ max_verts = ( 0 != ( pFlags & aiProcess_SplitLargeMeshes ) ) ? DeadBeef : max_verts;
+ return true;
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties for the post-processing step
+void OptimizeMeshesProcess::SetupProperties(const Importer* pImp)
+{
+ if( max_verts == DeadBeef /* magic hack */ ) {
+ max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
+ max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Execute step
+void OptimizeMeshesProcess::Execute( aiScene* pScene)
+{
+ const unsigned int num_old = pScene->mNumMeshes;
+ if (num_old <= 1) {
+ ASSIMP_LOG_DEBUG("Skipping OptimizeMeshesProcess");
+ return;
+ }
+
+ ASSIMP_LOG_DEBUG("OptimizeMeshesProcess begin");
+ mScene = pScene;
+
+ // need to clear persistent members from previous runs
+ merge_list.resize( 0 );
+ output.resize( 0 );
+
+ // ensure we have the right sizes
+ merge_list.reserve(pScene->mNumMeshes);
+ output.reserve(pScene->mNumMeshes);
+
+ // Prepare lookup tables
+ meshes.resize(pScene->mNumMeshes);
+ FindInstancedMeshes(pScene->mRootNode);
+ if( max_verts == DeadBeef ) /* undo the magic hack */
+ max_verts = NotSet;
+
+ // ... instanced meshes are immediately processed and added to the output list
+ for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) {
+ meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]);
+
+ if (meshes[i].instance_cnt > 1 && meshes[i].output_id == NotSet ) {
+ meshes[i].output_id = n++;
+ output.push_back(mScene->mMeshes[i]);
+ }
+ }
+
+ // and process all nodes in the scenegraph recursively
+ ProcessNode(pScene->mRootNode);
+ if (!output.size()) {
+ throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong");
+ }
+
+ meshes.resize( 0 );
+ ai_assert(output.size() <= num_old);
+
+ mScene->mNumMeshes = static_cast<unsigned int>(output.size());
+ std::copy(output.begin(),output.end(),mScene->mMeshes);
+
+ if (output.size() != num_old) {
+ ASSIMP_LOG_DEBUG_F("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes);
+ } else {
+ ASSIMP_LOG_DEBUG( "OptimizeMeshesProcess finished" );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Process meshes for a single node
+void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
+{
+ for (unsigned int i = 0; i < pNode->mNumMeshes;++i) {
+ unsigned int& im = pNode->mMeshes[i];
+
+ if (meshes[im].instance_cnt > 1) {
+ im = meshes[im].output_id;
+ }
+ else {
+ merge_list.resize( 0 );
+ unsigned int verts = 0, faces = 0;
+
+ // Find meshes to merge with us
+ for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) {
+ unsigned int am = pNode->mMeshes[a];
+ if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) {
+
+ merge_list.push_back(mScene->mMeshes[am]);
+ verts += mScene->mMeshes[am]->mNumVertices;
+ faces += mScene->mMeshes[am]->mNumFaces;
+
+ pNode->mMeshes[a] = pNode->mMeshes[pNode->mNumMeshes - 1];
+ --pNode->mNumMeshes;
+ --a;
+ }
+ }
+
+ // and merge all meshes which we found, replace the old ones
+ if (!merge_list.empty()) {
+ merge_list.push_back(mScene->mMeshes[im]);
+
+ aiMesh* out;
+ SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end());
+ output.push_back(out);
+ } else {
+ output.push_back(mScene->mMeshes[im]);
+ }
+ im = static_cast<unsigned int>(output.size()-1);
+ }
+ }
+
+
+ for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) {
+ ProcessNode( pNode->mChildren[ i ] );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether two meshes can be joined
+bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned int verts, unsigned int faces )
+{
+ if (meshes[a].vertex_format != meshes[b].vertex_format)
+ return false;
+
+ aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b];
+
+ if ((NotSet != max_verts && verts+mb->mNumVertices > max_verts) ||
+ (NotSet != max_faces && faces+mb->mNumFaces > max_faces)) {
+ return false;
+ }
+
+ // Never merge unskinned meshes with skinned meshes
+ if (ma->mMaterialIndex != mb->mMaterialIndex || ma->HasBones() != mb->HasBones())
+ return false;
+
+ // Never merge meshes with different kinds of primitives if SortByPType did already
+ // do its work. We would destroy everything again ...
+ if (pts && ma->mPrimitiveTypes != mb->mPrimitiveTypes)
+ return false;
+
+ // If both meshes are skinned, check whether we have many bones defined in both meshes.
+ // If yes, we can join them.
+ if (ma->HasBones()) {
+ // TODO
+ return false;
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a LUT of all instanced meshes
+void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode)
+{
+ for( unsigned int i = 0; i < pNode->mNumMeshes; ++i ) {
+ ++meshes[ pNode->mMeshes[ i ] ].instance_cnt;
+ }
+
+ for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) {
+ FindInstancedMeshes( pNode->mChildren[ i ] );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+
+#endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
diff --git a/thirdparty/assimp/code/OptimizeMeshes.h b/thirdparty/assimp/code/OptimizeMeshes.h
new file mode 100644
index 0000000000..9f46f349b4
--- /dev/null
+++ b/thirdparty/assimp/code/OptimizeMeshes.h
@@ -0,0 +1,186 @@
+/*
+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 OptimizeMeshes.h
+ * @brief Declares a post processing step to join meshes, if possible
+ */
+#ifndef AI_OPTIMIZEMESHESPROCESS_H_INC
+#define AI_OPTIMIZEMESHESPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/types.h>
+#include <vector>
+
+struct aiMesh;
+struct aiNode;
+class OptimizeMeshesProcessTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** @brief Postprocessing step to optimize mesh usage
+ *
+ * The implementation looks for meshes that could be joined and joins them.
+ * Usually this will reduce the number of drawcalls.
+ *
+ * @note Instanced meshes are currently not processed.
+ */
+class OptimizeMeshesProcess : public BaseProcess
+{
+public:
+ /// @brief The class constructor.
+ OptimizeMeshesProcess();
+
+ /// @brief The class destcructor,
+ ~OptimizeMeshesProcess();
+
+
+ /** @brief Internal utility to store additional mesh info
+ */
+ struct MeshInfo {
+ MeshInfo() AI_NO_EXCEPT
+ : instance_cnt(0)
+ , vertex_format(0)
+ , output_id(0xffffffff) {
+ // empty
+ }
+
+ //! Number of times this mesh is referenced
+ unsigned int instance_cnt;
+
+ //! Vertex format id
+ unsigned int vertex_format;
+
+ //! Output ID
+ unsigned int output_id;
+ };
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Specify whether you want meshes with different
+ * primitive types to be merged as well.
+ *
+ * IsActive() sets this property automatically to true if the
+ * aiProcess_SortByPType flag is found.
+ */
+ void EnablePrimitiveTypeSorting(bool enable) {
+ pts = enable;
+ }
+
+ // Getter
+ bool IsPrimitiveTypeSortingEnabled () const {
+ return pts;
+ }
+
+
+ // -------------------------------------------------------------------
+ /** @brief Specify a maximum size of a single output mesh.
+ *
+ * If a single input mesh already exceeds this limit, it won't
+ * be split.
+ * @param verts Maximum number of vertices per mesh
+ * @param faces Maximum number of faces per mesh
+ */
+ void SetPreferredMeshSizeLimit (unsigned int verts, unsigned int faces)
+ {
+ max_verts = verts;
+ max_faces = faces;
+ }
+
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** @brief Do the actual optimization on all meshes of this node
+ * @param pNode Node we're working with
+ */
+ void ProcessNode( aiNode* pNode);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns true if b can be joined with a
+ *
+ * @param verts Number of output verts up to now
+ * @param faces Number of output faces up to now
+ */
+ bool CanJoin ( unsigned int a, unsigned int b,
+ unsigned int verts, unsigned int faces );
+
+ // -------------------------------------------------------------------
+ /** @brief Find instanced meshes, for the moment we're excluding
+ * them from all optimizations
+ */
+ void FindInstancedMeshes (aiNode* pNode);
+
+private:
+
+ //! Scene we're working with
+ aiScene* mScene;
+
+ //! Per mesh info
+ std::vector<MeshInfo> meshes;
+
+ //! Output meshes
+ std::vector<aiMesh*> output;
+
+ //! @see EnablePrimitiveTypeSorting
+ mutable bool pts;
+
+ //! @see SetPreferredMeshSizeLimit
+ mutable unsigned int max_verts,max_faces;
+
+ //! Temporary storage
+ std::vector<aiMesh*> merge_list;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC
diff --git a/thirdparty/assimp/code/PolyTools.h b/thirdparty/assimp/code/PolyTools.h
new file mode 100644
index 0000000000..fbbda0e7d1
--- /dev/null
+++ b/thirdparty/assimp/code/PolyTools.h
@@ -0,0 +1,229 @@
+/*
+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
diff --git a/thirdparty/assimp/code/PostStepRegistry.cpp b/thirdparty/assimp/code/PostStepRegistry.cpp
new file mode 100644
index 0000000000..15b4a28843
--- /dev/null
+++ b/thirdparty/assimp/code/PostStepRegistry.cpp
@@ -0,0 +1,251 @@
+/*
+---------------------------------------------------------------------------
+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 "ProcessHelper.h"
+
+#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS
+# include "CalcTangentsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
+# include "JoinVerticesProcess.h"
+#endif
+#if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
+# include "ConvertToLHProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
+# include "TriangulateProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS
+# include "DropFaceNormalsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS
+# include "GenFaceNormalsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS
+# include "GenVertexNormalsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS
+# include "RemoveVCProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS
+# include "SplitLargeMeshes.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS
+# include "PretransformVertices.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS
+# include "LimitBoneWeightsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+# include "ValidateDataStructure.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS
+# include "ImproveCacheLocality.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS
+# include "FixNormalsStep.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS
+# include "RemoveRedundantMaterials.h"
+#endif
+#if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS)
+# include "EmbedTexturesProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
+# include "FindInvalidDataProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS
+# include "FindDegenerates.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS
+# include "SortByPTypeProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
+# include "ComputeUVMappingProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
+# include "TextureTransform.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS
+# include "FindInstancesProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
+# include "OptimizeMeshes.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
+# include "OptimizeGraph.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
+# include "SplitByBoneCountProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
+# include "DeboneProcess.h"
+#endif
+#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
+# include "ScaleProcess.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_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
+}
+
+}
diff --git a/thirdparty/assimp/code/PretransformVertices.cpp b/thirdparty/assimp/code/PretransformVertices.cpp
new file mode 100644
index 0000000000..52001a0578
--- /dev/null
+++ b/thirdparty/assimp/code/PretransformVertices.cpp
@@ -0,0 +1,728 @@
+/*
+---------------------------------------------------------------------------
+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 PretransformVertices.cpp
+ * @brief Implementation of the "PretransformVertices" post processing step
+*/
+
+
+#include "PretransformVertices.h"
+#include "ProcessHelper.h"
+#include <assimp/SceneCombiner.h>
+#include <assimp/Exceptional.h>
+
+using namespace Assimp;
+
+// some array offsets
+#define AI_PTVS_VERTEX 0x0
+#define AI_PTVS_FACE 0x1
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+PretransformVertices::PretransformVertices()
+: configKeepHierarchy (false)
+, configNormalize(false)
+, configTransform(false)
+, configTransformation()
+, mConfigPointCloud( false ) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+PretransformVertices::~PretransformVertices() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool PretransformVertices::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_PreTransformVertices) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import configuration
+void PretransformVertices::SetupProperties(const Importer* pImp)
+{
+ // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
+ // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
+ configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0));
+ configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0));
+ configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION,0));
+
+ configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
+
+ mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Count the number of nodes
+unsigned int PretransformVertices::CountNodes( aiNode* pcNode )
+{
+ unsigned int iRet = 1;
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
+ {
+ iRet += CountNodes(pcNode->mChildren[i]);
+ }
+ return iRet;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a bitwise combination identifying the vertex format of a mesh
+unsigned int PretransformVertices::GetMeshVFormat( aiMesh* pcMesh )
+{
+ // the vertex format is stored in aiMesh::mBones for later retrieval.
+ // there isn't a good reason to compute it a few hundred times
+ // from scratch. The pointer is unused as animations are lost
+ // during PretransformVertices.
+ if (pcMesh->mBones)
+ return (unsigned int)(uint64_t)pcMesh->mBones;
+
+
+ const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
+
+ // store the value for later use
+ pcMesh->mBones = (aiBone**)(uint64_t)iRet;
+ return iRet;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Count the number of vertices in the whole scene and a given
+// material index
+void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
+ unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices)
+{
+ for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
+ {
+ aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
+ if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
+ {
+ *piVertices += pcMesh->mNumVertices;
+ *piFaces += pcMesh->mNumFaces;
+ }
+ }
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
+ {
+ CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat,
+ iVFormat,piFaces,piVertices);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Collect vertex/face data
+void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
+ unsigned int iVFormat, aiMesh* pcMeshOut,
+ unsigned int aiCurrent[2], unsigned int* num_refs)
+{
+ // No need to multiply if there's no transformation
+ const bool identity = pcNode->mTransformation.IsIdentity();
+ for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
+ {
+ aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
+ if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
+ {
+ // Decrement mesh reference counter
+ unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
+ ai_assert(0 != num_ref);
+ --num_ref;
+ // Save the name of the last mesh
+ if (num_ref==0)
+ {
+ pcMeshOut->mName = pcMesh->mName;
+ }
+
+ if (identity) {
+ // copy positions without modifying them
+ ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mVertices,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+
+ if (iVFormat & 0x2) {
+ // copy normals without modifying them
+ ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mNormals,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ }
+ if (iVFormat & 0x4)
+ {
+ // copy tangents without modifying them
+ ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mTangents,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ // copy bitangents without modifying them
+ ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mBitangents,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ }
+ }
+ else
+ {
+ // copy positions, transform them to worldspace
+ for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
+ pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
+ }
+ aiMatrix4x4 mWorldIT = pcNode->mTransformation;
+ mWorldIT.Inverse().Transpose();
+
+ // TODO: implement Inverse() for aiMatrix3x3
+ aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+ if (iVFormat & 0x2)
+ {
+ // copy normals, transform them to worldspace
+ for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
+ pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] =
+ (m * pcMesh->mNormals[n]).Normalize();
+ }
+ }
+ if (iVFormat & 0x4)
+ {
+ // copy tangents and bitangents, transform them to worldspace
+ for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
+ pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mTangents[n]).Normalize();
+ pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = (m * pcMesh->mBitangents[n]).Normalize();
+ }
+ }
+ }
+ unsigned int p = 0;
+ while (iVFormat & (0x100 << p))
+ {
+ // copy texture coordinates
+ memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mTextureCoords[p],
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ ++p;
+ }
+ p = 0;
+ while (iVFormat & (0x1000000 << p))
+ {
+ // copy vertex colors
+ memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mColors[p],
+ pcMesh->mNumVertices * sizeof(aiColor4D));
+ ++p;
+ }
+ // now we need to copy all faces. since we will delete the source mesh afterwards,
+ // we don't need to reallocate the array of indices except if this mesh is
+ // referenced multiple times.
+ for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck)
+ {
+ aiFace& f_src = pcMesh->mFaces[planck];
+ aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck];
+
+ const unsigned int num_idx = f_src.mNumIndices;
+
+ f_dst.mNumIndices = num_idx;
+
+ unsigned int* pi;
+ if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
+ pi = f_dst.mIndices = f_src.mIndices;
+
+ // offset all vertex indices
+ for (unsigned int hahn = 0; hahn < num_idx;++hahn){
+ pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
+ }
+ }
+ else {
+ pi = f_dst.mIndices = new unsigned int[num_idx];
+
+ // copy and offset all vertex indices
+ for (unsigned int hahn = 0; hahn < num_idx;++hahn){
+ pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
+ }
+ }
+
+ // Update the mPrimitiveTypes member of the mesh
+ switch (pcMesh->mFaces[planck].mNumIndices)
+ {
+ case 0x1:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 0x2:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 0x3:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ break;
+ };
+ }
+ aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices;
+ aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces;
+ }
+ }
+
+ // append all children of us
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i) {
+ CollectData(pcScene,pcNode->mChildren[i],iMat,
+ iVFormat,pcMeshOut,aiCurrent,num_refs);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all vertex formats that occur for a given material index
+// The output list contains duplicate elements
+void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat,
+ std::list<unsigned int>& aiOut)
+{
+ for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
+ {
+ aiMesh* pcMesh = pcScene->mMeshes[ i ];
+ if (iMat == pcMesh->mMaterialIndex) {
+ aiOut.push_back(GetMeshVFormat(pcMesh));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Compute the absolute transformation matrices of each node
+void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode )
+{
+ if (pcNode->mParent) {
+ pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation;
+ }
+
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i) {
+ ComputeAbsoluteTransform(pcNode->mChildren[i]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the node transformation to a mesh
+void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)
+{
+ // Check whether we need to transform the coordinates at all
+ if (!mat.IsIdentity()) {
+
+ if (mesh->HasPositions()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mVertices[i] = mat * mesh->mVertices[i];
+ }
+ }
+ if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
+ aiMatrix4x4 mWorldIT = mat;
+ mWorldIT.Inverse().Transpose();
+
+ // TODO: implement Inverse() for aiMatrix3x3
+ aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+ if (mesh->HasNormals()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
+ }
+ }
+ if (mesh->HasTangentsAndBitangents()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
+ mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Simple routine to build meshes in worldspace, no further optimization
+void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
+ unsigned int numIn, aiNode* node)
+{
+ // NOTE:
+ // aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy
+ // aiMesh::mBones store reference to abs. transform we multiplied with
+
+ // process meshes
+ for (unsigned int i = 0; i < node->mNumMeshes;++i) {
+ aiMesh* mesh = in[node->mMeshes[i]];
+
+ // check whether we can operate on this mesh
+ if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4*>(mesh->mBones) == node->mTransformation) {
+ // yes, we can.
+ mesh->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
+ mesh->mNumBones = UINT_MAX;
+ }
+ else {
+
+ // try to find us in the list of newly created meshes
+ for (unsigned int n = 0; n < out.size(); ++n) {
+ aiMesh* ctz = out[n];
+ if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4*>(ctz->mBones) == node->mTransformation) {
+
+ // ok, use this one. Update node mesh index
+ node->mMeshes[i] = numIn + n;
+ }
+ }
+ if (node->mMeshes[i] < numIn) {
+ // Worst case. Need to operate on a full copy of the mesh
+ ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
+ aiMesh* ntz;
+
+ const unsigned int tmp = mesh->mNumBones; //
+ mesh->mNumBones = 0;
+ SceneCombiner::Copy(&ntz,mesh);
+ mesh->mNumBones = tmp;
+
+ ntz->mNumBones = node->mMeshes[i];
+ ntz->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
+
+ out.push_back(ntz);
+
+ node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
+ }
+ }
+ }
+
+ // call children
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ BuildWCSMeshes(out,in,numIn,node->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reset transformation matrices to identity
+void PretransformVertices::MakeIdentityTransform(aiNode* nd)
+{
+ nd->mTransformation = aiMatrix4x4();
+
+ // call children
+ for (unsigned int i = 0; i < nd->mNumChildren;++i)
+ MakeIdentityTransform(nd->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build reference counters for all meshes
+void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs)
+{
+ for (unsigned int i = 0; i< nd->mNumMeshes;++i)
+ refs[nd->mMeshes[i]]++;
+
+ // call children
+ for (unsigned int i = 0; i < nd->mNumChildren;++i)
+ BuildMeshRefCountArray(nd->mChildren[i],refs);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void PretransformVertices::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin");
+
+ // Return immediately if we have no meshes
+ if (!pScene->mNumMeshes)
+ return;
+
+ const unsigned int iOldMeshes = pScene->mNumMeshes;
+ const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
+ const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
+
+ if(configTransform) {
+ pScene->mRootNode->mTransformation = configTransformation;
+ }
+
+ // first compute absolute transformation matrices for all nodes
+ ComputeAbsoluteTransform(pScene->mRootNode);
+
+ // Delete aiMesh::mBones for all meshes. The bones are
+ // removed during this step and we need the pointer as
+ // temporary storage
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
+ aiMesh* mesh = pScene->mMeshes[i];
+
+ for (unsigned int a = 0; a < mesh->mNumBones;++a)
+ delete mesh->mBones[a];
+
+ delete[] mesh->mBones;
+ mesh->mBones = NULL;
+ }
+
+ // now build a list of output meshes
+ std::vector<aiMesh*> apcOutMeshes;
+
+ // Keep scene hierarchy? It's an easy job in this case ...
+ // we go on and transform all meshes, if one is referenced by nodes
+ // with different absolute transformations a depth copy of the mesh
+ // is required.
+ if( configKeepHierarchy ) {
+
+ // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
+ BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode);
+
+ // ... if new meshes have been generated, append them to the end of the scene
+ if (apcOutMeshes.size() > 0) {
+ aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()];
+
+ memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes);
+ memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size());
+
+ pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
+ delete[] pScene->mMeshes; pScene->mMeshes = npp;
+ }
+
+ // now iterate through all meshes and transform them to worldspace
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones ));
+
+ // prevent improper destruction
+ pScene->mMeshes[i]->mBones = NULL;
+ pScene->mMeshes[i]->mNumBones = 0;
+ }
+ } else {
+ apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
+ std::list<unsigned int> aiVFormats;
+
+ std::vector<unsigned int> s(pScene->mNumMeshes,0);
+ BuildMeshRefCountArray(pScene->mRootNode,&s[0]);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+ // get the list of all vertex formats for this material
+ aiVFormats.clear();
+ GetVFormatList(pScene,i,aiVFormats);
+ aiVFormats.sort();
+ aiVFormats.unique();
+ for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) {
+ unsigned int iVertices = 0;
+ unsigned int iFaces = 0;
+ CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
+ if (0 != iFaces && 0 != iVertices)
+ {
+ apcOutMeshes.push_back(new aiMesh());
+ aiMesh* pcMesh = apcOutMeshes.back();
+ pcMesh->mNumFaces = iFaces;
+ pcMesh->mNumVertices = iVertices;
+ pcMesh->mFaces = new aiFace[iFaces];
+ pcMesh->mVertices = new aiVector3D[iVertices];
+ pcMesh->mMaterialIndex = i;
+ if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
+ if ((*j) & 0x4)
+ {
+ pcMesh->mTangents = new aiVector3D[iVertices];
+ pcMesh->mBitangents = new aiVector3D[iVertices];
+ }
+ iFaces = 0;
+ while ((*j) & (0x100 << iFaces))
+ {
+ pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
+ if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
+ else pcMesh->mNumUVComponents[iFaces] = 2;
+ iFaces++;
+ }
+ iFaces = 0;
+ while ((*j) & (0x1000000 << iFaces))
+ pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
+
+ // fill the mesh ...
+ unsigned int aiTemp[2] = {0,0};
+ CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]);
+ }
+ }
+ }
+
+ // If no meshes are referenced in the node graph it is possible that we get no output meshes.
+ if (apcOutMeshes.empty()) {
+
+ throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes");
+ }
+ else
+ {
+ // now delete all meshes in the scene and build a new mesh list
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiMesh* mesh = pScene->mMeshes[i];
+ mesh->mNumBones = 0;
+ mesh->mBones = NULL;
+
+ // we're reusing the face index arrays. avoid destruction
+ for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+ mesh->mFaces[a].mNumIndices = 0;
+ mesh->mFaces[a].mIndices = NULL;
+ }
+
+ delete mesh;
+
+ // Invalidate the contents of the old mesh array. We will most
+ // likely have less output meshes now, so the last entries of
+ // the mesh array are not overridden. We set them to NULL to
+ // make sure the developer gets notified when his application
+ // attempts to access these fields ...
+ mesh = NULL;
+ }
+
+ // It is impossible that we have more output meshes than
+ // input meshes, so we can easily reuse the old mesh array
+ pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
+ pScene->mMeshes[i] = apcOutMeshes[i];
+ }
+ }
+ }
+
+ // remove all animations from the scene
+ for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
+ delete pScene->mAnimations[i];
+ delete[] pScene->mAnimations;
+
+ pScene->mAnimations = NULL;
+ pScene->mNumAnimations = 0;
+
+ // --- we need to keep all cameras and lights
+ for (unsigned int i = 0; i < pScene->mNumCameras;++i)
+ {
+ aiCamera* cam = pScene->mCameras[i];
+ const aiNode* nd = pScene->mRootNode->FindNode(cam->mName);
+ ai_assert(NULL != nd);
+
+ // multiply all properties of the camera with the absolute
+ // transformation of the corresponding node
+ cam->mPosition = nd->mTransformation * cam->mPosition;
+ cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt;
+ cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp;
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumLights;++i)
+ {
+ aiLight* l = pScene->mLights[i];
+ const aiNode* nd = pScene->mRootNode->FindNode(l->mName);
+ ai_assert(NULL != nd);
+
+ // multiply all properties of the camera with the absolute
+ // transformation of the corresponding node
+ l->mPosition = nd->mTransformation * l->mPosition;
+ l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
+ l->mUp = aiMatrix3x3( nd->mTransformation ) * l->mUp;
+ }
+
+ if( !configKeepHierarchy ) {
+
+ // now delete all nodes in the scene and build a new
+ // flat node graph with a root node and some level 1 children
+ aiNode* newRoot = new aiNode();
+ newRoot->mName = pScene->mRootNode->mName;
+ delete pScene->mRootNode;
+ pScene->mRootNode = newRoot;
+
+ if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
+ {
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ }
+ else
+ {
+ pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
+ aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
+
+ // generate mesh nodes
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
+ {
+ aiNode* pcNode = new aiNode();
+ *nodes = pcNode;
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName = pScene->mMeshes[i]->mName;
+
+ // setup mesh indices
+ pcNode->mNumMeshes = 1;
+ pcNode->mMeshes = new unsigned int[1];
+ pcNode->mMeshes[0] = i;
+ }
+ // generate light nodes
+ for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
+ {
+ aiNode* pcNode = new aiNode();
+ *nodes = pcNode;
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i);
+ pScene->mLights[i]->mName = pcNode->mName;
+ }
+ // generate camera nodes
+ for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
+ {
+ aiNode* pcNode = new aiNode();
+ *nodes = pcNode;
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i);
+ pScene->mCameras[i]->mName = pcNode->mName;
+ }
+ }
+ }
+ else {
+ // ... and finally set the transformation matrix of all nodes to identity
+ MakeIdentityTransform(pScene->mRootNode);
+ }
+
+ if (configNormalize) {
+ // compute the boundary of all meshes
+ aiVector3D min,max;
+ MinMaxChooser<aiVector3D> ()(min,max);
+
+ for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+ aiMesh* m = pScene->mMeshes[a];
+ for (unsigned int i = 0; i < m->mNumVertices;++i) {
+ min = std::min(m->mVertices[i],min);
+ max = std::max(m->mVertices[i],max);
+ }
+ }
+
+ // find the dominant axis
+ aiVector3D d = max-min;
+ const ai_real div = std::max(d.x,std::max(d.y,d.z))*ai_real( 0.5);
+
+ d = min + d * (ai_real)0.5;
+ for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+ aiMesh* m = pScene->mMeshes[a];
+ for (unsigned int i = 0; i < m->mNumVertices;++i) {
+ m->mVertices[i] = (m->mVertices[i]-d)/div;
+ }
+ }
+ }
+
+ // print statistics
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
+
+ ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (",
+ CountNodes(pScene->mRootNode) ," output nodes)" );
+ ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras." );
+ ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
+ }
+}
diff --git a/thirdparty/assimp/code/PretransformVertices.h b/thirdparty/assimp/code/PretransformVertices.h
new file mode 100644
index 0000000000..b7329af130
--- /dev/null
+++ b/thirdparty/assimp/code/PretransformVertices.h
@@ -0,0 +1,163 @@
+/*
+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 PretransformVertices.h
+ * @brief Defines a post processing step to pretransform all
+ * vertices in the scenegraph
+ */
+#ifndef AI_PRETRANSFORMVERTICES_H_INC
+#define AI_PRETRANSFORMVERTICES_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+#include <list>
+#include <vector>
+
+struct aiNode;
+class PretransformVerticesTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The PretransformVertices pre-transforms all vertices in the node tree
+ * and removes the whole graph. The output is a list of meshes, one for
+ * each material.
+*/
+class ASSIMP_API PretransformVertices : public BaseProcess {
+public:
+ PretransformVertices ();
+ ~PretransformVertices ();
+
+ // -------------------------------------------------------------------
+ // Check whether step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** @brief Toggle the 'keep hierarchy' option
+ * @param d hm ... difficult to guess what this means, hu!?
+ */
+ void KeepHierarchy(bool d) {
+ configKeepHierarchy = d;
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Check whether 'keep hierarchy' is currently enabled.
+ * @return ...
+ */
+ bool IsHierarchyKept() const {
+ return configKeepHierarchy;
+ }
+
+private:
+ // -------------------------------------------------------------------
+ // Count the number of nodes
+ unsigned int CountNodes( aiNode* pcNode );
+
+ // -------------------------------------------------------------------
+ // Get a bitwise combination identifying the vertex format of a mesh
+ unsigned int GetMeshVFormat(aiMesh* pcMesh);
+
+ // -------------------------------------------------------------------
+ // Count the number of vertices in the whole scene and a given
+ // material index
+ void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode,
+ unsigned int iMat,
+ unsigned int iVFormat,
+ unsigned int* piFaces,
+ unsigned int* piVertices);
+
+ // -------------------------------------------------------------------
+ // Collect vertex/face data
+ void CollectData( aiScene* pcScene, aiNode* pcNode,
+ unsigned int iMat,
+ unsigned int iVFormat,
+ aiMesh* pcMeshOut,
+ unsigned int aiCurrent[2],
+ unsigned int* num_refs);
+
+ // -------------------------------------------------------------------
+ // Get a list of all vertex formats that occur for a given material
+ // The output list contains duplicate elements
+ void GetVFormatList( aiScene* pcScene, unsigned int iMat,
+ std::list<unsigned int>& aiOut);
+
+ // -------------------------------------------------------------------
+ // Compute the absolute transformation matrices of each node
+ void ComputeAbsoluteTransform( aiNode* pcNode );
+
+ // -------------------------------------------------------------------
+ // Simple routine to build meshes in worldspace, no further optimization
+ void BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
+ unsigned int numIn, aiNode* node);
+
+ // -------------------------------------------------------------------
+ // Apply the node transformation to a mesh
+ void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat);
+
+ // -------------------------------------------------------------------
+ // Reset transformation matrices to identity
+ void MakeIdentityTransform(aiNode* nd);
+
+ // -------------------------------------------------------------------
+ // Build reference counters for all meshes
+ void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs);
+
+
+
+ //! Configuration option: keep scene hierarchy as long as possible
+ bool configKeepHierarchy;
+ bool configNormalize;
+ bool configTransform;
+ aiMatrix4x4 configTransformation;
+ bool mConfigPointCloud;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENFACENORMALPROCESS_H_INC
diff --git a/thirdparty/assimp/code/ProcessHelper.cpp b/thirdparty/assimp/code/ProcessHelper.cpp
new file mode 100644
index 0000000000..59869fdff7
--- /dev/null
+++ b/thirdparty/assimp/code/ProcessHelper.cpp
@@ -0,0 +1,443 @@
+/*
+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 ProcessHelper.cpp
+/** Implement shared utility functions for postprocessing steps */
+
+
+#include "ProcessHelper.h"
+
+
+#include <limits>
+
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
+{
+ const char* s = in.c_str();
+ while (*s) {
+ SkipSpacesAndLineEnd(&s);
+ if (*s == '\'') {
+ const char* base = ++s;
+ while (*s != '\'') {
+ ++s;
+ if (*s == '\0') {
+ ASSIMP_LOG_ERROR("ConvertListToString: String list is ill-formatted");
+ return;
+ }
+ }
+ out.push_back(std::string(base,(size_t)(s-base)));
+ ++s;
+ }
+ else {
+ out.push_back(GetNextToken(s));
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------
+void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
+ const aiMatrix4x4& m)
+{
+ min = aiVector3D ( ai_real( 10e10 ), ai_real( 10e10 ), ai_real( 10e10 ) );
+ max = aiVector3D ( ai_real( -10e10 ), ai_real( -10e10 ), ai_real( -10e10 ) );
+ for (unsigned int i = 0;i < mesh->mNumVertices;++i)
+ {
+ const aiVector3D v = m * mesh->mVertices[i];
+ min = std::min(v,min);
+ max = std::max(v,max);
+ }
+}
+
+// -------------------------------------------------------------------------------
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
+{
+ ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
+ out = min + (max-min)*(ai_real)0.5;
+}
+
+// -------------------------------------------------------------------------------
+void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max) {
+ if ( NULL == scene ) {
+ return;
+ }
+
+ if ( 0 == scene->mNumMeshes ) {
+ return;
+ }
+ FindMeshCenter(scene->mMeshes[0], out, min, max);
+ for (unsigned int i = 1; i < scene->mNumMeshes; ++i) {
+ aiVector3D tout, tmin, tmax;
+ FindMeshCenter(scene->mMeshes[i], tout, tmin, tmax);
+ if (min[0] > tmin[0]) min[0] = tmin[0];
+ if (min[1] > tmin[1]) min[1] = tmin[1];
+ if (min[2] > tmin[2]) min[2] = tmin[2];
+ if (max[0] < tmax[0]) max[0] = tmax[0];
+ if (max[1] < tmax[1]) max[1] = tmax[1];
+ if (max[2] < tmax[2]) max[2] = tmax[2];
+ }
+ out = min + (max-min)*(ai_real)0.5;
+}
+
+
+// -------------------------------------------------------------------------------
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
+ aiVector3D& max, const aiMatrix4x4& m)
+{
+ FindAABBTransformed(mesh,min,max,m);
+ out = min + (max-min)*(ai_real)0.5;
+}
+
+// -------------------------------------------------------------------------------
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
+{
+ aiVector3D min,max;
+ FindMeshCenter(mesh,out,min,max);
+}
+
+// -------------------------------------------------------------------------------
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
+ const aiMatrix4x4& m)
+{
+ aiVector3D min,max;
+ FindMeshCenterTransformed(mesh,out,min,max,m);
+}
+
+// -------------------------------------------------------------------------------
+ai_real ComputePositionEpsilon(const aiMesh* pMesh)
+{
+ const ai_real epsilon = ai_real( 1e-4 );
+
+ // calculate the position bounds so we have a reliable epsilon to check position differences against
+ aiVector3D minVec, maxVec;
+ ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
+ return (maxVec - minVec).Length() * epsilon;
+}
+
+// -------------------------------------------------------------------------------
+ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
+{
+ ai_assert( NULL != pMeshes );
+
+ const ai_real epsilon = ai_real( 1e-4 );
+
+ // calculate the position bounds so we have a reliable epsilon to check position differences against
+ aiVector3D minVec, maxVec, mi, ma;
+ MinMaxChooser<aiVector3D>()(minVec,maxVec);
+
+ for (size_t a = 0; a < num; ++a) {
+ const aiMesh* pMesh = pMeshes[a];
+ ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma);
+
+ minVec = std::min(minVec,mi);
+ maxVec = std::max(maxVec,ma);
+ }
+ return (maxVec - minVec).Length() * epsilon;
+}
+
+
+// -------------------------------------------------------------------------------
+unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh)
+{
+ ai_assert(NULL != pcMesh);
+
+ // FIX: the hash may never be 0. Otherwise a comparison against
+ // nullptr could be successful
+ unsigned int iRet = 1;
+
+ // normals
+ if (pcMesh->HasNormals())iRet |= 0x2;
+ // tangents and bitangents
+ if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
+
+#ifdef BOOST_STATIC_ASSERT
+ BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
+ BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
+#endif
+
+ // texture coordinates
+ unsigned int p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ {
+ iRet |= (0x100 << p);
+ if (3 == pcMesh->mNumUVComponents[p])
+ iRet |= (0x10000 << p);
+
+ ++p;
+ }
+ // vertex colors
+ p = 0;
+ while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
+ return iRet;
+}
+
+// -------------------------------------------------------------------------------
+VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh)
+{
+ if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
+ return NULL;
+ }
+
+ VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
+ for (unsigned int i = 0; i < pMesh->mNumBones;++i) {
+
+ aiBone* bone = pMesh->mBones[i];
+ for (unsigned int a = 0; a < bone->mNumWeights;++a) {
+ const aiVertexWeight& weight = bone->mWeights[a];
+ avPerVertexWeights[weight.mVertexId].push_back( std::pair<unsigned int,float>(i,weight.mWeight) );
+ }
+ }
+ return avPerVertexWeights;
+}
+
+
+// -------------------------------------------------------------------------------
+const char* TextureTypeToString(aiTextureType in)
+{
+ switch (in)
+ {
+ case aiTextureType_NONE:
+ return "n/a";
+ case aiTextureType_DIFFUSE:
+ return "Diffuse";
+ case aiTextureType_SPECULAR:
+ return "Specular";
+ case aiTextureType_AMBIENT:
+ return "Ambient";
+ case aiTextureType_EMISSIVE:
+ return "Emissive";
+ case aiTextureType_OPACITY:
+ return "Opacity";
+ case aiTextureType_NORMALS:
+ return "Normals";
+ case aiTextureType_HEIGHT:
+ return "Height";
+ case aiTextureType_SHININESS:
+ return "Shininess";
+ case aiTextureType_DISPLACEMENT:
+ return "Displacement";
+ case aiTextureType_LIGHTMAP:
+ return "Lightmap";
+ case aiTextureType_REFLECTION:
+ return "Reflection";
+ case aiTextureType_UNKNOWN:
+ return "Unknown";
+ default:
+ break;
+ }
+
+ ai_assert(false);
+ return "BUG";
+}
+
+// -------------------------------------------------------------------------------
+const char* MappingTypeToString(aiTextureMapping in)
+{
+ switch (in)
+ {
+ case aiTextureMapping_UV:
+ return "UV";
+ case aiTextureMapping_BOX:
+ return "Box";
+ case aiTextureMapping_SPHERE:
+ return "Sphere";
+ case aiTextureMapping_CYLINDER:
+ return "Cylinder";
+ case aiTextureMapping_PLANE:
+ return "Plane";
+ case aiTextureMapping_OTHER:
+ return "Other";
+ default:
+ break;
+ }
+
+ ai_assert(false);
+ return "BUG";
+}
+
+
+// -------------------------------------------------------------------------------
+aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags)
+{
+ aiMesh *oMesh = new aiMesh();
+ std::vector<unsigned int> vMap(pMesh->mNumVertices,UINT_MAX);
+
+ size_t numSubVerts = 0;
+ size_t numSubFaces = subMeshFaces.size();
+
+ for(unsigned int i=0;i<numSubFaces;i++) {
+ const aiFace &f = pMesh->mFaces[subMeshFaces[i]];
+
+ for(unsigned int j=0;j<f.mNumIndices;j++) {
+ if(vMap[f.mIndices[j]]==UINT_MAX) {
+ vMap[f.mIndices[j]] = static_cast<unsigned int>(numSubVerts++);
+ }
+ }
+ }
+
+ oMesh->mName = pMesh->mName;
+
+ oMesh->mMaterialIndex = pMesh->mMaterialIndex;
+ oMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
+
+ // create all the arrays for this mesh if the old mesh contained them
+
+ oMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size());
+ oMesh->mNumVertices = static_cast<unsigned int>(numSubVerts);
+ oMesh->mVertices = new aiVector3D[numSubVerts];
+ if( pMesh->HasNormals() ) {
+ oMesh->mNormals = new aiVector3D[numSubVerts];
+ }
+
+ if( pMesh->HasTangentsAndBitangents() ) {
+ oMesh->mTangents = new aiVector3D[numSubVerts];
+ oMesh->mBitangents = new aiVector3D[numSubVerts];
+ }
+
+ for( size_t a = 0; pMesh->HasTextureCoords(static_cast<unsigned int>(a)) ; ++a ) {
+ oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts];
+ oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
+ }
+
+ for( size_t a = 0; pMesh->HasVertexColors( static_cast<unsigned int>(a)); ++a ) {
+ oMesh->mColors[a] = new aiColor4D[numSubVerts];
+ }
+
+ // and copy over the data, generating faces with linear indices along the way
+ oMesh->mFaces = new aiFace[numSubFaces];
+
+ for(unsigned int a = 0; a < numSubFaces; ++a ) {
+
+ const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
+ aiFace& dstFace = oMesh->mFaces[a];
+ dstFace.mNumIndices = srcFace.mNumIndices;
+ dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
+
+ // accumulate linearly all the vertices of the source face
+ for( size_t b = 0; b < dstFace.mNumIndices; ++b ) {
+ dstFace.mIndices[b] = vMap[srcFace.mIndices[b]];
+ }
+ }
+
+ for(unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex ) {
+ unsigned int nvi = vMap[srcIndex];
+ if(nvi==UINT_MAX) {
+ continue;
+ }
+
+ oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
+ if( pMesh->HasNormals() ) {
+ oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
+ }
+
+ if( pMesh->HasTangentsAndBitangents() ) {
+ oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
+ oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
+ }
+ for( size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c ) {
+ oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
+ }
+ for( size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c ) {
+ oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
+ }
+ }
+
+ if(~subFlags&AI_SUBMESH_FLAGS_SANS_BONES) {
+ std::vector<unsigned int> subBones(pMesh->mNumBones,0);
+
+ for(unsigned int a=0;a<pMesh->mNumBones;++a) {
+ const aiBone* bone = pMesh->mBones[a];
+
+ for(unsigned int b=0;b<bone->mNumWeights;b++) {
+ unsigned int v = vMap[bone->mWeights[b].mVertexId];
+
+ if(v!=UINT_MAX) {
+ subBones[a]++;
+ }
+ }
+ }
+
+ for(unsigned int a=0;a<pMesh->mNumBones;++a) {
+ if(subBones[a]>0) {
+ oMesh->mNumBones++;
+ }
+ }
+
+ if(oMesh->mNumBones) {
+ oMesh->mBones = new aiBone*[oMesh->mNumBones]();
+ unsigned int nbParanoia = oMesh->mNumBones;
+
+ oMesh->mNumBones = 0; //rewind
+
+ for(unsigned int a=0;a<pMesh->mNumBones;++a) {
+ if(subBones[a]==0) {
+ continue;
+ }
+ aiBone *newBone = new aiBone;
+ oMesh->mBones[oMesh->mNumBones++] = newBone;
+
+ const aiBone* bone = pMesh->mBones[a];
+
+ newBone->mName = bone->mName;
+ newBone->mOffsetMatrix = bone->mOffsetMatrix;
+ newBone->mWeights = new aiVertexWeight[subBones[a]];
+
+ for(unsigned int b=0;b<bone->mNumWeights;b++) {
+ const unsigned int v = vMap[bone->mWeights[b].mVertexId];
+
+ if(v!=UINT_MAX) {
+ aiVertexWeight w(v,bone->mWeights[b].mWeight);
+ newBone->mWeights[newBone->mNumWeights++] = w;
+ }
+ }
+ }
+
+ ai_assert(nbParanoia==oMesh->mNumBones);
+ (void)nbParanoia; // remove compiler warning on release build
+ }
+ }
+
+ return oMesh;
+}
+
+} // namespace Assimp
diff --git a/thirdparty/assimp/code/ProcessHelper.h b/thirdparty/assimp/code/ProcessHelper.h
new file mode 100644
index 0000000000..c59f3217bf
--- /dev/null
+++ b/thirdparty/assimp/code/ProcessHelper.h
@@ -0,0 +1,386 @@
+/*
+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 AI_PROCESS_HELPER_H_INCLUDED
+#define AI_PROCESS_HELPER_H_INCLUDED
+
+#include <assimp/postprocess.h>
+#include <assimp/anim.h>
+#include <assimp/mesh.h>
+#include <assimp/material.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/scene.h>
+
+#include <assimp/SpatialSort.h>
+#include "BaseProcess.h"
+#include <assimp/ParsingUtils.h>
+
+#include <list>
+
+// -------------------------------------------------------------------------------
+// Some extensions to std namespace. Mainly std::min and std::max for all
+// flat data types in the aiScene. They're used to quickly determine the
+// min/max bounds of data arrays.
+#ifdef __cplusplus
+namespace std {
+
+ // std::min for aiVector3D
+ template <typename TReal>
+ inline ::aiVector3t<TReal> min (const ::aiVector3t<TReal>& a, const ::aiVector3t<TReal>& b) {
+ return ::aiVector3t<TReal> (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
+ }
+
+ // std::max for aiVector3t<TReal>
+ template <typename TReal>
+ inline ::aiVector3t<TReal> max (const ::aiVector3t<TReal>& a, const ::aiVector3t<TReal>& b) {
+ return ::aiVector3t<TReal> (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
+ }
+
+ // std::min for aiVector2t<TReal>
+ template <typename TReal>
+ inline ::aiVector2t<TReal> min (const ::aiVector2t<TReal>& a, const ::aiVector2t<TReal>& b) {
+ return ::aiVector2t<TReal> (min(a.x,b.x),min(a.y,b.y));
+ }
+
+ // std::max for aiVector2t<TReal>
+ template <typename TReal>
+ inline ::aiVector2t<TReal> max (const ::aiVector2t<TReal>& a, const ::aiVector2t<TReal>& b) {
+ return ::aiVector2t<TReal> (max(a.x,b.x),max(a.y,b.y));
+ }
+
+ // std::min for aiColor4D
+ template <typename TReal>
+ inline ::aiColor4t<TReal> min (const ::aiColor4t<TReal>& a, const ::aiColor4t<TReal>& b) {
+ return ::aiColor4t<TReal> (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a));
+ }
+
+ // std::max for aiColor4D
+ template <typename TReal>
+ inline ::aiColor4t<TReal> max (const ::aiColor4t<TReal>& a, const ::aiColor4t<TReal>& b) {
+ return ::aiColor4t<TReal> (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a));
+ }
+
+
+ // std::min for aiQuaterniont<TReal>
+ template <typename TReal>
+ inline ::aiQuaterniont<TReal> min (const ::aiQuaterniont<TReal>& a, const ::aiQuaterniont<TReal>& b) {
+ return ::aiQuaterniont<TReal> (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
+ }
+
+ // std::max for aiQuaterniont<TReal>
+ template <typename TReal>
+ inline ::aiQuaterniont<TReal> max (const ::aiQuaterniont<TReal>& a, const ::aiQuaterniont<TReal>& b) {
+ return ::aiQuaterniont<TReal> (max(a.w,b.w),max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
+ }
+
+
+
+ // std::min for aiVectorKey
+ inline ::aiVectorKey min (const ::aiVectorKey& a, const ::aiVectorKey& b) {
+ return ::aiVectorKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
+ }
+
+ // std::max for aiVectorKey
+ inline ::aiVectorKey max (const ::aiVectorKey& a, const ::aiVectorKey& b) {
+ return ::aiVectorKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
+ }
+
+ // std::min for aiQuatKey
+ inline ::aiQuatKey min (const ::aiQuatKey& a, const ::aiQuatKey& b) {
+ return ::aiQuatKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
+ }
+
+ // std::max for aiQuatKey
+ inline ::aiQuatKey max (const ::aiQuatKey& a, const ::aiQuatKey& b) {
+ return ::aiQuatKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
+ }
+
+ // std::min for aiVertexWeight
+ inline ::aiVertexWeight min (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
+ return ::aiVertexWeight (min(a.mVertexId,b.mVertexId),min(a.mWeight,b.mWeight));
+ }
+
+ // std::max for aiVertexWeight
+ inline ::aiVertexWeight max (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
+ return ::aiVertexWeight (max(a.mVertexId,b.mVertexId),max(a.mWeight,b.mWeight));
+ }
+
+} // end namespace std
+#endif // !! C++
+
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+// Start points for ArrayBounds<T> for all supported Ts
+template <typename T>
+struct MinMaxChooser;
+
+template <> struct MinMaxChooser<float> {
+ void operator ()(float& min,float& max) {
+ max = -1e10f;
+ min = 1e10f;
+}};
+template <> struct MinMaxChooser<double> {
+ void operator ()(double& min,double& max) {
+ max = -1e10;
+ min = 1e10;
+}};
+template <> struct MinMaxChooser<unsigned int> {
+ void operator ()(unsigned int& min,unsigned int& max) {
+ max = 0;
+ min = (1u<<(sizeof(unsigned int)*8-1));
+}};
+
+template <typename T> struct MinMaxChooser< aiVector3t<T> > {
+ void operator ()(aiVector3t<T>& min,aiVector3t<T>& max) {
+ max = aiVector3t<T>(-1e10f,-1e10f,-1e10f);
+ min = aiVector3t<T>( 1e10f, 1e10f, 1e10f);
+}};
+template <typename T> struct MinMaxChooser< aiVector2t<T> > {
+ void operator ()(aiVector2t<T>& min,aiVector2t<T>& max) {
+ max = aiVector2t<T>(-1e10f,-1e10f);
+ min = aiVector2t<T>( 1e10f, 1e10f);
+ }};
+template <typename T> struct MinMaxChooser< aiColor4t<T> > {
+ void operator ()(aiColor4t<T>& min,aiColor4t<T>& max) {
+ max = aiColor4t<T>(-1e10f,-1e10f,-1e10f,-1e10f);
+ min = aiColor4t<T>( 1e10f, 1e10f, 1e10f, 1e10f);
+}};
+
+template <typename T> struct MinMaxChooser< aiQuaterniont<T> > {
+ void operator ()(aiQuaterniont<T>& min,aiQuaterniont<T>& max) {
+ max = aiQuaterniont<T>(-1e10f,-1e10f,-1e10f,-1e10f);
+ min = aiQuaterniont<T>( 1e10f, 1e10f, 1e10f, 1e10f);
+}};
+
+template <> struct MinMaxChooser<aiVectorKey> {
+ void operator ()(aiVectorKey& min,aiVectorKey& max) {
+ MinMaxChooser<double>()(min.mTime,max.mTime);
+ MinMaxChooser<aiVector3D>()(min.mValue,max.mValue);
+}};
+template <> struct MinMaxChooser<aiQuatKey> {
+ void operator ()(aiQuatKey& min,aiQuatKey& max) {
+ MinMaxChooser<double>()(min.mTime,max.mTime);
+ MinMaxChooser<aiQuaternion>()(min.mValue,max.mValue);
+}};
+
+template <> struct MinMaxChooser<aiVertexWeight> {
+ void operator ()(aiVertexWeight& min,aiVertexWeight& max) {
+ MinMaxChooser<unsigned int>()(min.mVertexId,max.mVertexId);
+ MinMaxChooser<float>()(min.mWeight,max.mWeight);
+}};
+
+// -------------------------------------------------------------------------------
+/** @brief Find the min/max values of an array of Ts
+ * @param in Input array
+ * @param size Number of elements to process
+ * @param[out] min minimum value
+ * @param[out] max maximum value
+ */
+template <typename T>
+inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max)
+{
+ MinMaxChooser<T> ()(min,max);
+ for (unsigned int i = 0; i < size;++i) {
+ min = std::min(in[i],min);
+ max = std::max(in[i],max);
+ }
+}
+
+
+// -------------------------------------------------------------------------------
+/** Little helper function to calculate the quadratic difference
+ * of two colours.
+ * @param pColor1 First color
+ * @param pColor2 second color
+ * @return Quadratic color difference */
+inline ai_real GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2)
+{
+ const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a);
+ return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
+}
+
+
+// -------------------------------------------------------------------------------
+/** @brief Extract single strings from a list of identifiers
+ * @param in Input string list.
+ * @param out Receives a list of clean output strings
+ * @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST */
+void ConvertListToStrings(const std::string& in, std::list<std::string>& out);
+
+
+// -------------------------------------------------------------------------------
+/** @brief Compute the AABB of a mesh after applying a given transform
+ * @param mesh Input mesh
+ * @param[out] min Receives minimum transformed vertex
+ * @param[out] max Receives maximum transformed vertex
+ * @param m Transformation matrix to be applied */
+void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, const aiMatrix4x4& m);
+
+
+// -------------------------------------------------------------------------------
+/** @brief Helper function to determine the 'real' center of a mesh
+ *
+ * That is the center of its axis-aligned bounding box.
+ * @param mesh Input mesh
+ * @param[out] min Minimum vertex of the mesh
+ * @param[out] max maximum vertex of the mesh
+ * @param[out] out Center point */
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max);
+
+// -------------------------------------------------------------------------------
+/** @brief Helper function to determine the 'real' center of a scene
+ *
+ * That is the center of its axis-aligned bounding box.
+ * @param scene Input scene
+ * @param[out] min Minimum vertex of the scene
+ * @param[out] max maximum vertex of the scene
+ * @param[out] out Center point */
+void FindSceneCenter (aiScene* scene, aiVector3D& out, aiVector3D& min, aiVector3D& max);
+
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh after applying a given transform
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,aiVector3D& max, const aiMatrix4x4& m);
+
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out);
+
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh after applying a given transform
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,const aiMatrix4x4& m);
+
+
+// -------------------------------------------------------------------------------
+// Compute a good epsilon value for position comparisons on a mesh
+ai_real ComputePositionEpsilon(const aiMesh* pMesh);
+
+
+// -------------------------------------------------------------------------------
+// Compute a good epsilon value for position comparisons on a array of meshes
+ai_real ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num);
+
+
+// -------------------------------------------------------------------------------
+// Compute an unique value for the vertex format of a mesh
+unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh);
+
+
+// defs for ComputeVertexBoneWeightTable()
+typedef std::pair <unsigned int,float> PerVertexWeight;
+typedef std::vector <PerVertexWeight> VertexWeightTable;
+
+// -------------------------------------------------------------------------------
+// Compute a per-vertex bone weight table
+VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh);
+
+
+// -------------------------------------------------------------------------------
+// Get a string for a given aiTextureType
+const char* TextureTypeToString(aiTextureType in);
+
+
+// -------------------------------------------------------------------------------
+// Get a string for a given aiTextureMapping
+const char* MappingTypeToString(aiTextureMapping in);
+
+
+// flags for MakeSubmesh()
+#define AI_SUBMESH_FLAGS_SANS_BONES 0x1
+
+// -------------------------------------------------------------------------------
+// Split a mesh given a list of faces to be contained in the sub mesh
+aiMesh* MakeSubmesh(const aiMesh *superMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags);
+
+// -------------------------------------------------------------------------------
+// Utility postprocess step to share the spatial sort tree between
+// all steps which use it to speedup its computations.
+class ComputeSpatialSortProcess : public BaseProcess
+{
+ bool IsActive( unsigned int pFlags) const
+ {
+ return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
+ aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
+ }
+
+ void Execute( aiScene* pScene)
+ {
+ typedef std::pair<SpatialSort, ai_real> _Type;
+ ASSIMP_LOG_DEBUG("Generate spatially-sorted vertex cache");
+
+ std::vector<_Type>* p = new std::vector<_Type>(pScene->mNumMeshes);
+ std::vector<_Type>::iterator it = p->begin();
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it) {
+ aiMesh* mesh = pScene->mMeshes[i];
+ _Type& blubb = *it;
+ blubb.first.Fill(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D));
+ blubb.second = ComputePositionEpsilon(mesh);
+ }
+
+ shared->AddProperty(AI_SPP_SPATIAL_SORT,p);
+ }
+};
+
+// -------------------------------------------------------------------------------
+// ... and the same again to cleanup the whole stuff
+class DestroySpatialSortProcess : public BaseProcess
+{
+ bool IsActive( unsigned int pFlags) const
+ {
+ return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
+ aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
+ }
+
+ void Execute( aiScene* /*pScene*/)
+ {
+ shared->RemoveProperty(AI_SPP_SPATIAL_SORT);
+ }
+};
+
+
+
+} // ! namespace Assimp
+#endif // !! AI_PROCESS_HELPER_H_INCLUDED
diff --git a/thirdparty/assimp/code/RawLoader.cpp b/thirdparty/assimp/code/RawLoader.cpp
new file mode 100644
index 0000000000..d0da247e47
--- /dev/null
+++ b/thirdparty/assimp/code/RawLoader.cpp
@@ -0,0 +1,331 @@
+/*
+---------------------------------------------------------------------------
+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 RawLoader.cpp
+ * @brief Implementation of the RAW importer class
+ */
+
+
+#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
+
+// internal headers
+#include "RawLoader.h"
+#include <assimp/ParsingUtils.h>
+#include <assimp/fast_atof.h>
+#include <memory>
+#include <assimp/IOSystem.hpp>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/scene.h>
+#include <assimp/importerdesc.h>
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+ "Raw Importer",
+ "",
+ "",
+ "",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "raw"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RAWImporter::RAWImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+RAWImporter::~RAWImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool RAWImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
+{
+ return SimpleExtensionCheck(pFile,"raw");
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc* RAWImporter::GetInfo () const
+{
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void RAWImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open RAW file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ // (terminate it with zero)
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ // list of groups loaded from the file
+ std::vector< GroupInformation > outGroups(1,GroupInformation("<default>"));
+ std::vector< GroupInformation >::iterator curGroup = outGroups.begin();
+
+ // now read all lines
+ char line[4096];
+ while (GetNextLine(buffer,line))
+ {
+ // if the line starts with a non-numeric identifier, it marks
+ // the beginning of a new group
+ const char* sz = line;SkipSpaces(&sz);
+ if (IsLineEnd(*sz))continue;
+ if (!IsNumeric(*sz))
+ {
+ const char* sz2 = sz;
+ while (!IsSpaceOrNewLine(*sz2))++sz2;
+ const unsigned int length = (unsigned int)(sz2-sz);
+
+ // find an existing group with this name
+ for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end();
+ it != end;++it)
+ {
+ if (length == (*it).name.length() && !::strcmp(sz,(*it).name.c_str()))
+ {
+ curGroup = it;sz2 = NULL;
+ break;
+ }
+ }
+ if (sz2)
+ {
+ outGroups.push_back(GroupInformation(std::string(sz,length)));
+ curGroup = outGroups.end()-1;
+ }
+ }
+ else
+ {
+ // there can be maximally 12 floats plus an extra texture file name
+ float data[12];
+ unsigned int num;
+ for (num = 0; num < 12;++num)
+ {
+ if(!SkipSpaces(&sz) || !IsNumeric(*sz))break;
+ sz = fast_atoreal_move<float>(sz,data[num]);
+ }
+ if (num != 12 && num != 9)
+ {
+ ASSIMP_LOG_ERROR("A line may have either 9 or 12 floats and an optional texture");
+ continue;
+ }
+
+ MeshInformation* output = NULL;
+
+ const char* sz2 = sz;
+ unsigned int length;
+ if (!IsLineEnd(*sz))
+ {
+ while (!IsSpaceOrNewLine(*sz2))++sz2;
+ length = (unsigned int)(sz2-sz);
+ }
+ else if (9 == num)
+ {
+ sz = "%default%";
+ length = 9;
+ }
+ else
+ {
+ sz = "";
+ length = 0;
+ }
+
+ // search in the list of meshes whether we have one with this texture
+ for (auto &mesh : (*curGroup).meshes)
+ {
+ if (length == mesh.name.length() && (length ? !::strcmp(sz, mesh.name.c_str()) : true))
+ {
+ output = &mesh;
+ break;
+ }
+ }
+ // if we don't have the mesh, create it
+ if (!output)
+ {
+ (*curGroup).meshes.push_back(MeshInformation(std::string(sz,length)));
+ output = &((*curGroup).meshes.back());
+ }
+ if (12 == num)
+ {
+ aiColor4D v(data[0],data[1],data[2],1.0f);
+ output->colors.push_back(v);
+ output->colors.push_back(v);
+ output->colors.push_back(v);
+
+ output->vertices.push_back(aiVector3D(data[3],data[4],data[5]));
+ output->vertices.push_back(aiVector3D(data[6],data[7],data[8]));
+ output->vertices.push_back(aiVector3D(data[9],data[10],data[11]));
+ }
+ else
+ {
+ output->vertices.push_back(aiVector3D(data[0],data[1],data[2]));
+ output->vertices.push_back(aiVector3D(data[3],data[4],data[5]));
+ output->vertices.push_back(aiVector3D(data[6],data[7],data[8]));
+ }
+ }
+ }
+
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<RawRoot>");
+
+ // count the number of valid groups
+ // (meshes can't be empty)
+ for (auto & outGroup : outGroups)
+ {
+ if (!outGroup.meshes.empty())
+ {
+ ++pScene->mRootNode->mNumChildren;
+ pScene->mNumMeshes += (unsigned int) outGroup.meshes.size();
+ }
+ }
+
+ if (!pScene->mNumMeshes)
+ {
+ throw DeadlyImportError("RAW: No meshes loaded. The file seems to be corrupt or empty.");
+ }
+
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ aiNode** cc;
+ if (1 == pScene->mRootNode->mNumChildren)
+ {
+ cc = &pScene->mRootNode;
+ pScene->mRootNode->mNumChildren = 0;
+ } else {
+ cc = new aiNode*[pScene->mRootNode->mNumChildren];
+ memset(cc, 0, sizeof(aiNode*) * pScene->mRootNode->mNumChildren);
+ pScene->mRootNode->mChildren = cc;
+ }
+
+ pScene->mNumMaterials = pScene->mNumMeshes;
+ aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+
+ unsigned int meshIdx = 0;
+ for (auto & outGroup : outGroups)
+ {
+ if (outGroup.meshes.empty())continue;
+
+ aiNode* node;
+ if (pScene->mRootNode->mNumChildren)
+ {
+ node = *cc = new aiNode();
+ node->mParent = pScene->mRootNode;
+ }
+ else node = *cc;
+ node->mName.Set(outGroup.name);
+
+ // add all meshes
+ node->mNumMeshes = (unsigned int) outGroup.meshes.size();
+ unsigned int* pi = node->mMeshes = new unsigned int[ node->mNumMeshes ];
+ for (std::vector< MeshInformation >::iterator it2 = outGroup.meshes.begin(),
+ end2 = outGroup.meshes.end(); it2 != end2; ++it2)
+ {
+ ai_assert(!(*it2).vertices.empty());
+
+ // allocate the mesh
+ *pi++ = meshIdx;
+ aiMesh* mesh = pScene->mMeshes[meshIdx] = new aiMesh();
+ mesh->mMaterialIndex = meshIdx++;
+
+ mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // allocate storage for the vertex components and copy them
+ mesh->mNumVertices = (unsigned int)(*it2).vertices.size();
+ mesh->mVertices = new aiVector3D[ mesh->mNumVertices ];
+ ::memcpy(mesh->mVertices,&(*it2).vertices[0],sizeof(aiVector3D)*mesh->mNumVertices);
+
+ if ((*it2).colors.size())
+ {
+ ai_assert((*it2).colors.size() == mesh->mNumVertices);
+
+ mesh->mColors[0] = new aiColor4D[ mesh->mNumVertices ];
+ ::memcpy(mesh->mColors[0],&(*it2).colors[0],sizeof(aiColor4D)*mesh->mNumVertices);
+ }
+
+ // generate triangles
+ ai_assert(0 == mesh->mNumVertices % 3);
+ aiFace* fc = mesh->mFaces = new aiFace[ mesh->mNumFaces = mesh->mNumVertices/3 ];
+ aiFace* const fcEnd = fc + mesh->mNumFaces;
+ unsigned int n = 0;
+ while (fc != fcEnd)
+ {
+ aiFace& f = *fc++;
+ f.mIndices = new unsigned int[f.mNumIndices = 3];
+ for (unsigned int m = 0; m < 3;++m)
+ f.mIndices[m] = n++;
+ }
+
+ // generate a material for the mesh
+ aiMaterial* mat = new aiMaterial();
+
+ aiColor4D clr(1.0f,1.0f,1.0f,1.0f);
+ if ("%default%" == (*it2).name) // a gray default material
+ {
+ clr.r = clr.g = clr.b = 0.6f;
+ }
+ else if ((*it2).name.length() > 0) // a texture
+ {
+ aiString s;
+ s.Set((*it2).name);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ mat->AddProperty<aiColor4D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+ *mats++ = mat;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_RAW_IMPORTER
diff --git a/thirdparty/assimp/code/RemoveComments.cpp b/thirdparty/assimp/code/RemoveComments.cpp
new file mode 100644
index 0000000000..91700a7699
--- /dev/null
+++ b/thirdparty/assimp/code/RemoveComments.cpp
@@ -0,0 +1,113 @@
+/*
+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
diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.cpp b/thirdparty/assimp/code/RemoveRedundantMaterials.cpp
new file mode 100644
index 0000000000..632bdca3fe
--- /dev/null
+++ b/thirdparty/assimp/code/RemoveRedundantMaterials.cpp
@@ -0,0 +1,221 @@
+/*
+---------------------------------------------------------------------------
+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 RemoveRedundantMaterials.cpp
+ * @brief Implementation of the "RemoveRedundantMaterials" post processing step
+*/
+
+// internal headers
+
+#include "RemoveRedundantMaterials.h"
+#include <assimp/ParsingUtils.h>
+#include "ProcessHelper.h"
+#include "MaterialSystem.h"
+#include <stdio.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RemoveRedundantMatsProcess::RemoveRedundantMatsProcess()
+: configFixedMaterials() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+RemoveRedundantMatsProcess::~RemoveRedundantMatsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import properties
+void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp)
+{
+ // Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST
+ configFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin");
+
+ unsigned int redundantRemoved = 0, unreferencedRemoved = 0;
+ if (pScene->mNumMaterials)
+ {
+ // Find out which materials are referenced by meshes
+ std::vector<bool> abReferenced(pScene->mNumMaterials,false);
+ for (unsigned int i = 0;i < pScene->mNumMeshes;++i)
+ abReferenced[pScene->mMeshes[i]->mMaterialIndex] = true;
+
+ // If a list of materials to be excluded was given, match the list with
+ // our imported materials and 'salt' all positive matches to ensure that
+ // we get unique hashes later.
+ if (configFixedMaterials.length()) {
+
+ std::list<std::string> strings;
+ ConvertListToStrings(configFixedMaterials,strings);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+ aiMaterial* mat = pScene->mMaterials[i];
+
+ aiString name;
+ mat->Get(AI_MATKEY_NAME,name);
+
+ if (name.length) {
+ std::list<std::string>::const_iterator it = std::find(strings.begin(), strings.end(), name.data);
+ if (it != strings.end()) {
+
+ // Our brilliant 'salt': A single material property with ~ as first
+ // character to mark it as internal and temporary.
+ const int dummy = 1;
+ ((aiMaterial*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0);
+
+ // Keep this material even if no mesh references it
+ abReferenced[i] = true;
+ ASSIMP_LOG_DEBUG_F( "Found positive match in exclusion list: \'", name.data, "\'");
+ }
+ }
+ }
+ }
+
+ // TODO: re-implement this algorithm to work in-place
+ unsigned int *aiMappingTable = new unsigned int[pScene->mNumMaterials];
+ for ( unsigned int i=0; i<pScene->mNumMaterials; i++ ) {
+ aiMappingTable[ i ] = 0;
+ }
+ unsigned int iNewNum = 0;
+
+ // Iterate through all materials and calculate a hash for them
+ // store all hashes in a list and so a quick search whether
+ // we do already have a specific hash. This allows us to
+ // determine which materials are identical.
+ uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];;
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ {
+ // No mesh is referencing this material, remove it.
+ if (!abReferenced[i]) {
+ ++unreferencedRemoved;
+ delete pScene->mMaterials[i];
+ pScene->mMaterials[i] = nullptr;
+ continue;
+ }
+
+ // Check all previously mapped materials for a matching hash.
+ // On a match we can delete this material and just make it ref to the same index.
+ uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]);
+ for (unsigned int a = 0; a < i;++a)
+ {
+ if (abReferenced[a] && me == aiHashes[a]) {
+ ++redundantRemoved;
+ me = 0;
+ aiMappingTable[i] = aiMappingTable[a];
+ delete pScene->mMaterials[i];
+ pScene->mMaterials[i] = nullptr;
+ break;
+ }
+ }
+ // This is a new material that is referenced, add to the map.
+ if (me) {
+ aiMappingTable[i] = iNewNum++;
+ }
+ }
+ // If the new material count differs from the original,
+ // we need to rebuild the material list and remap mesh material indexes.
+ if (iNewNum != pScene->mNumMaterials) {
+ ai_assert(iNewNum > 0);
+ aiMaterial** ppcMaterials = new aiMaterial*[iNewNum];
+ ::memset(ppcMaterials,0,sizeof(void*)*iNewNum);
+ for (unsigned int p = 0; p < pScene->mNumMaterials;++p)
+ {
+ // if the material is not referenced ... remove it
+ if (!abReferenced[p]) {
+ continue;
+ }
+
+ // generate new names for modified materials that had no names
+ const unsigned int idx = aiMappingTable[p];
+ if (ppcMaterials[idx]) {
+ aiString sz;
+ if( ppcMaterials[idx]->Get(AI_MATKEY_NAME, sz) != AI_SUCCESS ) {
+ sz.length = ::ai_snprintf(sz.data,MAXLEN,"JoinedMaterial_#%u",p);
+ ((aiMaterial*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME);
+ }
+ } else {
+ ppcMaterials[idx] = pScene->mMaterials[p];
+ }
+ }
+ // update all material indices
+ for (unsigned int p = 0; p < pScene->mNumMeshes;++p) {
+ aiMesh* mesh = pScene->mMeshes[p];
+ ai_assert( NULL!=mesh );
+ mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
+ }
+ // delete the old material list
+ delete[] pScene->mMaterials;
+ pScene->mMaterials = ppcMaterials;
+ pScene->mNumMaterials = iNewNum;
+ }
+ // delete temporary storage
+ delete[] aiHashes;
+ delete[] aiMappingTable;
+ }
+ if (redundantRemoved == 0 && unreferencedRemoved == 0)
+ {
+ ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished ");
+ }
+ else
+ {
+ ASSIMP_LOG_INFO_F("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ",
+ unreferencedRemoved, " unused materials.");
+ }
+}
diff --git a/thirdparty/assimp/code/RemoveRedundantMaterials.h b/thirdparty/assimp/code/RemoveRedundantMaterials.h
new file mode 100644
index 0000000000..dbd4d44cc0
--- /dev/null
+++ b/thirdparty/assimp/code/RemoveRedundantMaterials.h
@@ -0,0 +1,107 @@
+/*
+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 RemoveRedundantMaterials.h
+ * @brief Defines a post processing step to remove redundant materials
+ */
+#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC
+#define AI_REMOVEREDUNDANTMATERIALS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+
+class RemoveRedundantMatsTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** RemoveRedundantMatsProcess: Post-processing step to remove redundant
+ * materials from the imported scene.
+ */
+class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess
+{
+public:
+ /// The default class constructor.
+ RemoveRedundantMatsProcess();
+
+ /// The class destructor.
+ ~RemoveRedundantMatsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ // Check whether step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Set list of fixed (unmutable) materials
+ * @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
+ */
+ void SetFixedMaterialsString(const std::string& fixed = "") {
+ configFixedMaterials = fixed;
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Get list of fixed (unmutable) materials
+ * @return See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
+ */
+ const std::string& GetFixedMaterialsString() const {
+ return configFixedMaterials;
+ }
+
+private:
+
+ //! Configuration option: list of all fixed materials
+ std::string configFixedMaterials;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_REMOVEREDUNDANTMATERIALS_H_INC
diff --git a/thirdparty/assimp/code/RemoveVCProcess.cpp b/thirdparty/assimp/code/RemoveVCProcess.cpp
new file mode 100644
index 0000000000..99fd47a3aa
--- /dev/null
+++ b/thirdparty/assimp/code/RemoveVCProcess.cpp
@@ -0,0 +1,337 @@
+/*
+---------------------------------------------------------------------------
+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 post processing step to remove
+ * any parts of the mesh structure from the imported data.
+*/
+
+
+#include "RemoveVCProcess.h"
+#include <assimp/postprocess.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/scene.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RemoveVCProcess::RemoveVCProcess() :
+ configDeleteFlags()
+ , mScene()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+RemoveVCProcess::~RemoveVCProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool RemoveVCProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_RemoveComponent) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Small helper function to delete all elements in a T** aray using delete
+template <typename T>
+inline void ArrayDelete(T**& in, unsigned int& num)
+{
+ for (unsigned int i = 0; i < num; ++i)
+ delete in[i];
+
+ delete[] in;
+ in = NULL;
+ num = 0;
+}
+
+#if 0
+// ------------------------------------------------------------------------------------------------
+// Updates the node graph - removes all nodes which have the "remove" flag set and the
+// "don't remove" flag not set. Nodes with meshes are never deleted.
+bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
+{
+ bool b = false;
+
+ std::list<aiNode*> mine;
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ {
+ if(UpdateNodeGraph(node->mChildren[i],mine,false))
+ b = true;
+ }
+
+ // somewhat tricky ... mNumMeshes must be originally 0 and MSB2 may not be set,
+ // so we can do a simple comparison against MSB here
+ if (!root && AI_RC_UINT_MSB == node->mNumMeshes )
+ {
+ // this node needs to be removed
+ if(node->mNumChildren)
+ {
+ childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
+
+ // set all children to NULL to make sure they are not deleted when we delete ourself
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ node->mChildren[i] = NULL;
+ }
+ b = true;
+ delete node;
+ }
+ else
+ {
+ AI_RC_UNMASK(node->mNumMeshes);
+ childsOfParent.push_back(node);
+
+ if (b)
+ {
+ // reallocate the array of our children here
+ node->mNumChildren = (unsigned int)mine.size();
+ aiNode** const children = new aiNode*[mine.size()];
+ aiNode** ptr = children;
+
+ for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end();
+ it != end; ++it)
+ {
+ *ptr++ = *it;
+ }
+ delete[] node->mChildren;
+ node->mChildren = children;
+ return false;
+ }
+ }
+ return b;
+}
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void RemoveVCProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("RemoveVCProcess begin");
+ bool bHas = false; //,bMasked = false;
+
+ mScene = pScene;
+
+ // handle animations
+ if ( configDeleteFlags & aiComponent_ANIMATIONS)
+ {
+
+ bHas = true;
+ ArrayDelete(pScene->mAnimations,pScene->mNumAnimations);
+ }
+
+ // handle textures
+ if ( configDeleteFlags & aiComponent_TEXTURES)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mTextures,pScene->mNumTextures);
+ }
+
+ // handle materials
+ if ( configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials)
+ {
+ bHas = true;
+ for (unsigned int i = 1;i < pScene->mNumMaterials;++i)
+ delete pScene->mMaterials[i];
+
+ pScene->mNumMaterials = 1;
+ aiMaterial* helper = (aiMaterial*) pScene->mMaterials[0];
+ ai_assert(NULL != helper);
+ helper->Clear();
+
+ // gray
+ aiColor3D clr(0.6f,0.6f,0.6f);
+ helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // add a small ambient color value
+ clr = aiColor3D(0.05f,0.05f,0.05f);
+ helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString s;
+ s.Set("Dummy_MaterialsRemoved");
+ helper->AddProperty(&s,AI_MATKEY_NAME);
+ }
+
+ // handle light sources
+ if ( configDeleteFlags & aiComponent_LIGHTS)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mLights,pScene->mNumLights);
+ }
+
+ // handle camneras
+ if ( configDeleteFlags & aiComponent_CAMERAS)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mCameras,pScene->mNumCameras);
+ }
+
+ // handle meshes
+ if (configDeleteFlags & aiComponent_MESHES)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mMeshes,pScene->mNumMeshes);
+ }
+ else
+ {
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if( ProcessMesh( pScene->mMeshes[a]))
+ bHas = true;
+ }
+ }
+
+
+ // now check whether the result is still a full scene
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials)
+ {
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ ASSIMP_LOG_DEBUG("Setting AI_SCENE_FLAGS_INCOMPLETE flag");
+
+ // If we have no meshes anymore we should also clear another flag ...
+ if (!pScene->mNumMeshes)
+ pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+ }
+
+ if (bHas) {
+ ASSIMP_LOG_INFO("RemoveVCProcess finished. Data structure cleanup has been done.");
+ } else {
+ ASSIMP_LOG_DEBUG("RemoveVCProcess finished. Nothing to be done ...");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the step
+void RemoveVCProcess::SetupProperties(const Importer* pImp)
+{
+ configDeleteFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS,0x0);
+ if (!configDeleteFlags)
+ {
+ ASSIMP_LOG_WARN("RemoveVCProcess: AI_CONFIG_PP_RVC_FLAGS is zero.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
+{
+ bool ret = false;
+
+ // if all materials have been deleted let the material
+ // index of the mesh point to the created default material
+ if ( configDeleteFlags & aiComponent_MATERIALS)
+ pMesh->mMaterialIndex = 0;
+
+ // handle normals
+ if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals)
+ {
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = NULL;
+ ret = true;
+ }
+
+ // handle tangents and bitangents
+ if (configDeleteFlags & aiComponent_TANGENTS_AND_BITANGENTS && pMesh->mTangents)
+ {
+ delete[] pMesh->mTangents;
+ pMesh->mTangents = NULL;
+
+ delete[] pMesh->mBitangents;
+ pMesh->mBitangents = NULL;
+ ret = true;
+ }
+
+ // handle texture coordinates
+ bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS));
+ for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real)
+ {
+ if (!pMesh->mTextureCoords[i])break;
+ if (configDeleteFlags & aiComponent_TEXCOORDSn(real) || b)
+ {
+ delete [] pMesh->mTextureCoords[i];
+ pMesh->mTextureCoords[i] = NULL;
+ ret = true;
+
+ if (!b)
+ {
+ // collapse the rest of the array
+ for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a)
+ pMesh->mTextureCoords[a-1] = pMesh->mTextureCoords[a];
+
+ pMesh->mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS-1] = NULL;
+ continue;
+ }
+ }
+ ++i;
+ }
+
+ // handle vertex colors
+ b = (0 != (configDeleteFlags & aiComponent_COLORS));
+ for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_COLOR_SETS; ++real)
+ {
+ if (!pMesh->mColors[i])break;
+ if (configDeleteFlags & aiComponent_COLORSn(i) || b)
+ {
+ delete [] pMesh->mColors[i];
+ pMesh->mColors[i] = NULL;
+ ret = true;
+
+ if (!b)
+ {
+ // collapse the rest of the array
+ for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a)
+ pMesh->mColors[a-1] = pMesh->mColors[a];
+
+ pMesh->mColors[AI_MAX_NUMBER_OF_COLOR_SETS-1] = NULL;
+ continue;
+ }
+ }
+ ++i;
+ }
+
+ // handle bones
+ if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones)
+ {
+ ArrayDelete(pMesh->mBones,pMesh->mNumBones);
+ ret = true;
+ }
+ return ret;
+}
diff --git a/thirdparty/assimp/code/RemoveVCProcess.h b/thirdparty/assimp/code/RemoveVCProcess.h
new file mode 100644
index 0000000000..617d7b9b20
--- /dev/null
+++ b/thirdparty/assimp/code/RemoveVCProcess.h
@@ -0,0 +1,123 @@
+/*
+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 remove specific parts of the scene */
+#ifndef AI_REMOVEVCPROCESS_H_INCLUDED
+#define AI_REMOVEVCPROCESS_H_INCLUDED
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+
+class RemoveVCProcessTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** RemoveVCProcess: Class to exclude specific parts of the data structure
+ * from further processing by removing them,
+*/
+class ASSIMP_API RemoveVCProcess : public BaseProcess {
+public:
+ /// The default class constructor.
+ RemoveVCProcess();
+
+ /// The class destructor.
+ ~RemoveVCProcess();
+
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @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;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+ // -------------------------------------------------------------------
+ /** Manually setup the configuration flags for the step
+ *
+ * @param Bitwise combination of the #aiComponent enumerated values.
+ */
+ void SetDeleteFlags(unsigned int f)
+ {
+ configDeleteFlags = f;
+ }
+
+ // -------------------------------------------------------------------
+ /** Query the current configuration.
+ */
+ unsigned int GetDeleteFlags() const
+ {
+ return configDeleteFlags;
+ }
+
+private:
+
+ bool ProcessMesh (aiMesh* pcMesh);
+
+ /** Configuration flag
+ */
+ unsigned int configDeleteFlags;
+
+ /** The scene we're working with
+ */
+ aiScene* mScene;
+};
+
+// ---------------------------------------------------------------------------
+
+} // end of namespace Assimp
+
+#endif // !!AI_REMOVEVCPROCESS_H_INCLUDED
diff --git a/thirdparty/assimp/code/SGSpatialSort.cpp b/thirdparty/assimp/code/SGSpatialSort.cpp
new file mode 100644
index 0000000000..120070b0aa
--- /dev/null
+++ b/thirdparty/assimp/code/SGSpatialSort.cpp
@@ -0,0 +1,168 @@
+/*
+---------------------------------------------------------------------------
+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;
+ }
+ }
+}
+
+
diff --git a/thirdparty/assimp/code/ScaleProcess.cpp b/thirdparty/assimp/code/ScaleProcess.cpp
new file mode 100644
index 0000000000..6d458c4b11
--- /dev/null
+++ b/thirdparty/assimp/code/ScaleProcess.cpp
@@ -0,0 +1,103 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS
+
+#include "ScaleProcess.h"
+
+#include <assimp/scene.h>
+#include <assimp/postprocess.h>
+
+namespace Assimp {
+
+ScaleProcess::ScaleProcess()
+: BaseProcess()
+, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
+ // empty
+}
+
+ScaleProcess::~ScaleProcess() {
+ // empty
+}
+
+void ScaleProcess::setScale( ai_real scale ) {
+ mScale = scale;
+}
+
+ai_real ScaleProcess::getScale() const {
+ return mScale;
+}
+
+bool ScaleProcess::IsActive( unsigned int pFlags ) const {
+ return ( pFlags & aiProcess_GlobalScale ) != 0;
+}
+
+void ScaleProcess::SetupProperties( const Importer* pImp ) {
+ mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 );
+}
+
+void ScaleProcess::Execute( aiScene* pScene ) {
+ if ( nullptr == pScene ) {
+ return;
+ }
+
+ if ( nullptr == pScene->mRootNode ) {
+ return;
+ }
+
+ traverseNodes( pScene->mRootNode );
+}
+
+void ScaleProcess::traverseNodes( aiNode *node ) {
+ applyScaling( node );
+}
+
+void ScaleProcess::applyScaling( aiNode *currentNode ) {
+ if ( nullptr != currentNode ) {
+ currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale;
+ currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale;
+ currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale;
+ }
+}
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS
diff --git a/thirdparty/assimp/code/ScaleProcess.h b/thirdparty/assimp/code/ScaleProcess.h
new file mode 100644
index 0000000000..55146ae064
--- /dev/null
+++ b/thirdparty/assimp/code/ScaleProcess.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include "BaseProcess.h"
+
+struct aiNode;
+
+#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT)
+# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** ScaleProcess: Class to rescale the whole model.
+*/
+class ASSIMP_API ScaleProcess : public BaseProcess {
+public:
+ /// The default class constructor.
+ ScaleProcess();
+
+ /// The class destructor.
+ virtual ~ScaleProcess();
+
+ /// Will set the scale manually.
+ void setScale( ai_real scale );
+
+ /// Returns the current scaling value.
+ ai_real getScale() const;
+
+ /// Overwritten, @see BaseProcess
+ virtual bool IsActive( unsigned int pFlags ) const;
+
+ /// Overwritten, @see BaseProcess
+ virtual void SetupProperties( const Importer* pImp );
+
+ /// Overwritten, @see BaseProcess
+ virtual void Execute( aiScene* pScene );
+
+private:
+ void traverseNodes( aiNode *currentNode );
+ void applyScaling( aiNode *currentNode );
+
+private:
+ ai_real mScale;
+};
+
+} // Namespace Assimp
diff --git a/thirdparty/assimp/code/SceneCombiner.cpp b/thirdparty/assimp/code/SceneCombiner.cpp
new file mode 100644
index 0000000000..e445bd7434
--- /dev/null
+++ b/thirdparty/assimp/code/SceneCombiner.cpp
@@ -0,0 +1,1300 @@
+/*
+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);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+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 );
+}
+
+// ------------------------------------------------------------------------------------------------
+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( 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
+
diff --git a/thirdparty/assimp/code/ScenePreprocessor.cpp b/thirdparty/assimp/code/ScenePreprocessor.cpp
new file mode 100644
index 0000000000..432a3d7666
--- /dev/null
+++ b/thirdparty/assimp/code/ScenePreprocessor.cpp
@@ -0,0 +1,261 @@
+/*
+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. );
+ }
+}
diff --git a/thirdparty/assimp/code/ScenePreprocessor.h b/thirdparty/assimp/code/ScenePreprocessor.h
new file mode 100644
index 0000000000..3f4c8d7c3f
--- /dev/null
+++ b/thirdparty/assimp/code/ScenePreprocessor.h
@@ -0,0 +1,125 @@
+/*
+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
diff --git a/thirdparty/assimp/code/ScenePrivate.h b/thirdparty/assimp/code/ScenePrivate.h
new file mode 100644
index 0000000000..f336aafc9a
--- /dev/null
+++ b/thirdparty/assimp/code/ScenePrivate.h
@@ -0,0 +1,105 @@
+/*
+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
diff --git a/thirdparty/assimp/code/SkeletonMeshBuilder.cpp b/thirdparty/assimp/code/SkeletonMeshBuilder.cpp
new file mode 100644
index 0000000000..06cfe034e9
--- /dev/null
+++ b/thirdparty/assimp/code/SkeletonMeshBuilder.cpp
@@ -0,0 +1,270 @@
+/*
+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;
+}
diff --git a/thirdparty/assimp/code/SortByPTypeProcess.cpp b/thirdparty/assimp/code/SortByPTypeProcess.cpp
new file mode 100644
index 0000000000..2e0cc54004
--- /dev/null
+++ b/thirdparty/assimp/code/SortByPTypeProcess.cpp
@@ -0,0 +1,403 @@
+/*
+---------------------------------------------------------------------------
+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 DeterminePTypeHelperProcess and
+ * SortByPTypeProcess post-process steps.
+*/
+
+
+
+// internal headers
+#include "ProcessHelper.h"
+#include "SortByPTypeProcess.h"
+#include <assimp/Exceptional.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+SortByPTypeProcess::SortByPTypeProcess()
+{
+ configRemoveMeshes = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+SortByPTypeProcess::~SortByPTypeProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool SortByPTypeProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_SortByPType) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void SortByPTypeProcess::SetupProperties(const Importer* pImp)
+{
+ configRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update changed meshes in all nodes
+void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node)
+{
+ if (node->mNumMeshes)
+ {
+ unsigned int newSize = 0;
+ for (unsigned int m = 0; m< node->mNumMeshes; ++m)
+ {
+ unsigned int add = node->mMeshes[m]<<2;
+ for (unsigned int i = 0; i < 4;++i)
+ {
+ if (UINT_MAX != replaceMeshIndex[add+i])++newSize;
+ }
+ }
+ if (!newSize)
+ {
+ delete[] node->mMeshes;
+ node->mNumMeshes = 0;
+ node->mMeshes = NULL;
+ }
+ else
+ {
+ // Try to reuse the old array if possible
+ unsigned int* newMeshes = (newSize > node->mNumMeshes
+ ? new unsigned int[newSize] : node->mMeshes);
+
+ for (unsigned int m = 0; m< node->mNumMeshes; ++m)
+ {
+ unsigned int add = node->mMeshes[m]<<2;
+ for (unsigned int i = 0; i < 4;++i)
+ {
+ if (UINT_MAX != replaceMeshIndex[add+i])
+ *newMeshes++ = replaceMeshIndex[add+i];
+ }
+ }
+ if (newSize > node->mNumMeshes)
+ delete[] node->mMeshes;
+
+ node->mMeshes = newMeshes-(node->mNumMeshes = newSize);
+ }
+ }
+
+ // call all subnodes recursively
+ for (unsigned int m = 0; m < node->mNumChildren; ++m)
+ UpdateNodes(replaceMeshIndex,node->mChildren[m]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SortByPTypeProcess::Execute( aiScene* pScene) {
+ if ( 0 == pScene->mNumMeshes) {
+ ASSIMP_LOG_DEBUG("SortByPTypeProcess skipped, there are no meshes");
+ return;
+ }
+
+ ASSIMP_LOG_DEBUG("SortByPTypeProcess begin");
+
+ unsigned int aiNumMeshesPerPType[4] = {0,0,0,0};
+
+ std::vector<aiMesh*> outMeshes;
+ outMeshes.reserve(pScene->mNumMeshes<<1u);
+
+ bool bAnyChanges = false;
+
+ std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX);
+ std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin();
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ aiMesh* const mesh = pScene->mMeshes[i];
+ ai_assert(0 != mesh->mPrimitiveTypes);
+
+ // if there's just one primitive type in the mesh there's nothing to do for us
+ unsigned int num = 0;
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) {
+ ++aiNumMeshesPerPType[0];
+ ++num;
+ }
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) {
+ ++aiNumMeshesPerPType[1];
+ ++num;
+ }
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) {
+ ++aiNumMeshesPerPType[2];
+ ++num;
+ }
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) {
+ ++aiNumMeshesPerPType[3];
+ ++num;
+ }
+
+ if (1 == num) {
+ if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) {
+ *meshIdx = static_cast<unsigned int>( outMeshes.size() );
+ outMeshes.push_back(mesh);
+ } else {
+ delete mesh;
+ pScene->mMeshes[ i ] = nullptr;
+ bAnyChanges = true;
+ }
+
+ meshIdx += 4;
+ continue;
+ }
+ bAnyChanges = true;
+
+ // reuse our current mesh arrays for the submesh
+ // with the largest number of primitives
+ unsigned int aiNumPerPType[4] = {0,0,0,0};
+ aiFace* pFirstFace = mesh->mFaces;
+ aiFace* const pLastFace = pFirstFace + mesh->mNumFaces;
+
+ unsigned int numPolyVerts = 0;
+ for (;pFirstFace != pLastFace; ++pFirstFace) {
+ if (pFirstFace->mNumIndices <= 3)
+ ++aiNumPerPType[pFirstFace->mNumIndices-1];
+ else
+ {
+ ++aiNumPerPType[3];
+ numPolyVerts += pFirstFace-> mNumIndices;
+ }
+ }
+
+ VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh);
+ for (unsigned int real = 0; real < 4; ++real,++meshIdx)
+ {
+ if ( !aiNumPerPType[real] || configRemoveMeshes & (1u << real))
+ {
+ continue;
+ }
+
+ *meshIdx = (unsigned int) outMeshes.size();
+ outMeshes.push_back(new aiMesh());
+ aiMesh* out = outMeshes.back();
+
+ // the name carries the adjacency information between the meshes
+ out->mName = mesh->mName;
+
+ // copy data members
+ out->mPrimitiveTypes = 1u << real;
+ out->mMaterialIndex = mesh->mMaterialIndex;
+
+ // allocate output storage
+ out->mNumFaces = aiNumPerPType[real];
+ aiFace* outFaces = out->mFaces = new aiFace[out->mNumFaces];
+
+ out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1));
+
+ aiVector3D *vert(nullptr), *nor(nullptr), *tan(nullptr), *bit(nullptr);
+ aiVector3D *uv [AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ aiColor4D *cols [AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ if (mesh->mVertices) {
+ vert = out->mVertices = new aiVector3D[out->mNumVertices];
+ }
+
+ if (mesh->mNormals) {
+ nor = out->mNormals = new aiVector3D[out->mNumVertices];
+ }
+
+ if (mesh->mTangents) {
+ tan = out->mTangents = new aiVector3D[out->mNumVertices];
+ bit = out->mBitangents = new aiVector3D[out->mNumVertices];
+ }
+
+ for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_TEXTURECOORDS;++j) {
+ uv[j] = nullptr;
+ if (mesh->mTextureCoords[j]) {
+ uv[j] = out->mTextureCoords[j] = new aiVector3D[out->mNumVertices];
+ }
+
+ out->mNumUVComponents[j] = mesh->mNumUVComponents[j];
+ }
+
+ for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_COLOR_SETS;++j) {
+ cols[j] = nullptr;
+ if (mesh->mColors[j]) {
+ cols[j] = out->mColors[j] = new aiColor4D[out->mNumVertices];
+ }
+ }
+
+ typedef std::vector< aiVertexWeight > TempBoneInfo;
+ std::vector< TempBoneInfo > tempBones(mesh->mNumBones);
+
+ // try to guess how much storage we'll need
+ for (unsigned int q = 0; q < mesh->mNumBones;++q)
+ {
+ tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num-1));
+ }
+
+ unsigned int outIdx = 0;
+ for (unsigned int m = 0; m < mesh->mNumFaces; ++m)
+ {
+ aiFace& in = mesh->mFaces[m];
+ if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real+1))
+ {
+ continue;
+ }
+
+ outFaces->mNumIndices = in.mNumIndices;
+ outFaces->mIndices = in.mIndices;
+
+ for (unsigned int q = 0; q < in.mNumIndices; ++q)
+ {
+ unsigned int idx = in.mIndices[q];
+
+ // process all bones of this index
+ if (avw)
+ {
+ VertexWeightTable& tbl = avw[idx];
+ for (VertexWeightTable::const_iterator it = tbl.begin(), end = tbl.end();
+ it != end; ++it)
+ {
+ tempBones[ (*it).first ].push_back( aiVertexWeight(outIdx, (*it).second) );
+ }
+ }
+
+ if (vert)
+ {
+ *vert++ = mesh->mVertices[idx];
+ //mesh->mVertices[idx].x = get_qnan();
+ }
+ if (nor )*nor++ = mesh->mNormals[idx];
+ if (tan )
+ {
+ *tan++ = mesh->mTangents[idx];
+ *bit++ = mesh->mBitangents[idx];
+ }
+
+ for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp)
+ {
+ if (!uv[pp])break;
+ *uv[pp]++ = mesh->mTextureCoords[pp][idx];
+ }
+
+ for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp)
+ {
+ if (!cols[pp])break;
+ *cols[pp]++ = mesh->mColors[pp][idx];
+ }
+
+ in.mIndices[q] = outIdx++;
+ }
+
+ in.mIndices = nullptr;
+ ++outFaces;
+ }
+ ai_assert(outFaces == out->mFaces + out->mNumFaces);
+
+ // now generate output bones
+ for (unsigned int q = 0; q < mesh->mNumBones;++q)
+ if (!tempBones[q].empty())++out->mNumBones;
+
+ if (out->mNumBones)
+ {
+ out->mBones = new aiBone*[out->mNumBones];
+ for (unsigned int q = 0, real = 0; q < mesh->mNumBones;++q)
+ {
+ TempBoneInfo& in = tempBones[q];
+ if (in.empty())continue;
+
+ aiBone* srcBone = mesh->mBones[q];
+ aiBone* bone = out->mBones[real] = new aiBone();
+
+ bone->mName = srcBone->mName;
+ bone->mOffsetMatrix = srcBone->mOffsetMatrix;
+
+ bone->mNumWeights = (unsigned int)in.size();
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+
+ ::memcpy(bone->mWeights,&in[0],bone->mNumWeights*sizeof(aiVertexWeight));
+
+ ++real;
+ }
+ }
+ }
+
+ // delete the per-vertex bone weights table
+ delete[] avw;
+
+ // delete the input mesh
+ delete mesh;
+
+ // avoid invalid pointer
+ pScene->mMeshes[i] = NULL;
+ }
+
+ if (outMeshes.empty())
+ {
+ // This should not occur
+ throw DeadlyImportError("No meshes remaining");
+ }
+
+ // If we added at least one mesh process all nodes in the node
+ // graph and update their respective mesh indices.
+ if (bAnyChanges)
+ {
+ UpdateNodes(replaceMeshIndex,pScene->mRootNode);
+ }
+
+ if (outMeshes.size() != pScene->mNumMeshes)
+ {
+ delete[] pScene->mMeshes;
+ pScene->mNumMeshes = (unsigned int)outMeshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ }
+ ::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*));
+
+ if (!DefaultLogger::isNullLogger())
+ {
+ char buffer[1024];
+ ::ai_snprintf(buffer,1024,"Points: %u%s, Lines: %u%s, Triangles: %u%s, Polygons: %u%s (Meshes, X = removed)",
+ aiNumMeshesPerPType[0], ((configRemoveMeshes & aiPrimitiveType_POINT) ? "X" : ""),
+ aiNumMeshesPerPType[1], ((configRemoveMeshes & aiPrimitiveType_LINE) ? "X" : ""),
+ aiNumMeshesPerPType[2], ((configRemoveMeshes & aiPrimitiveType_TRIANGLE) ? "X" : ""),
+ aiNumMeshesPerPType[3], ((configRemoveMeshes & aiPrimitiveType_POLYGON) ? "X" : ""));
+ ASSIMP_LOG_INFO(buffer);
+ ASSIMP_LOG_DEBUG("SortByPTypeProcess finished");
+ }
+}
+
diff --git a/thirdparty/assimp/code/SortByPTypeProcess.h b/thirdparty/assimp/code/SortByPTypeProcess.h
new file mode 100644
index 0000000000..c9d9924d8f
--- /dev/null
+++ b/thirdparty/assimp/code/SortByPTypeProcess.h
@@ -0,0 +1,85 @@
+/*
+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 sort meshes by the types
+ of primitives they contain */
+#ifndef AI_SORTBYPTYPEPROCESS_H_INC
+#define AI_SORTBYPTYPEPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include <assimp/mesh.h>
+
+class SortByPTypeProcessTest;
+namespace Assimp {
+
+
+// ---------------------------------------------------------------------------
+/** SortByPTypeProcess: Sorts meshes by the types of primitives they contain.
+ * A mesh with 5 lines, 3 points and 145 triangles would be split in 3
+ * submeshes.
+*/
+class ASSIMP_API SortByPTypeProcess : public BaseProcess
+{
+public:
+
+ SortByPTypeProcess();
+ ~SortByPTypeProcess();
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ int configRemoveMeshes;
+};
+
+
+} // end of namespace Assimp
+
+#endif // !!AI_SORTBYPTYPEPROCESS_H_INC
diff --git a/thirdparty/assimp/code/SpatialSort.cpp b/thirdparty/assimp/code/SpatialSort.cpp
new file mode 100644
index 0000000000..a4f3a4e4b8
--- /dev/null
+++ b/thirdparty/assimp/code/SpatialSort.cpp
@@ -0,0 +1,342 @@
+/*
+---------------------------------------------------------------------------
+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;
+}
diff --git a/thirdparty/assimp/code/SplitByBoneCountProcess.cpp b/thirdparty/assimp/code/SplitByBoneCountProcess.cpp
new file mode 100644
index 0000000000..2ef66a9afc
--- /dev/null
+++ b/thirdparty/assimp/code/SplitByBoneCountProcess.cpp
@@ -0,0 +1,407 @@
+/*
+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]);
+ }
+}
diff --git a/thirdparty/assimp/code/SplitByBoneCountProcess.h b/thirdparty/assimp/code/SplitByBoneCountProcess.h
new file mode 100644
index 0000000000..6c904a9df4
--- /dev/null
+++ b/thirdparty/assimp/code/SplitByBoneCountProcess.h
@@ -0,0 +1,111 @@
+/*
+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
diff --git a/thirdparty/assimp/code/SplitLargeMeshes.cpp b/thirdparty/assimp/code/SplitLargeMeshes.cpp
new file mode 100644
index 0000000000..1797b28d5a
--- /dev/null
+++ b/thirdparty/assimp/code/SplitLargeMeshes.cpp
@@ -0,0 +1,623 @@
+/*
+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 SplitLargeMeshes postprocessing step
+ */
+
+// internal headers of the post-processing framework
+#include "SplitLargeMeshes.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Triangle::SplitLargeMeshesProcess_Triangle() {
+ LIMIT = AI_SLM_DEFAULT_MAX_TRIANGLES;
+}
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Triangle::~SplitLargeMeshesProcess_Triangle() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool SplitLargeMeshesProcess_Triangle::IsActive( unsigned int pFlags) const {
+ return (pFlags & aiProcess_SplitLargeMeshes) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) {
+ if (0xffffffff == this->LIMIT || nullptr == pScene ) {
+ return;
+ }
+
+ ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle begin");
+ std::vector<std::pair<aiMesh*, unsigned int> > avList;
+
+ for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+ this->SplitMesh(a, pScene->mMeshes[a],avList);
+ }
+
+ if (avList.size() != pScene->mNumMeshes) {
+ // it seems something has been split. rebuild the mesh list
+ delete[] pScene->mMeshes;
+ pScene->mNumMeshes = (unsigned int)avList.size();
+ pScene->mMeshes = new aiMesh*[avList.size()];
+
+ for (unsigned int i = 0; i < avList.size();++i) {
+ pScene->mMeshes[i] = avList[i].first;
+ }
+
+ // now we need to update all nodes
+ this->UpdateNode(pScene->mRootNode,avList);
+ ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
+ } else {
+ ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties
+void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) {
+ // get the current value of the split property
+ this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update a node after some meshes have been split
+void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode,
+ const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
+ // for every index in out list build a new entry
+ std::vector<unsigned int> aiEntries;
+ aiEntries.reserve(pcNode->mNumMeshes + 1);
+ for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) {
+ for (unsigned int a = 0; a < avList.size();++a) {
+ if (avList[a].second == pcNode->mMeshes[i]) {
+ aiEntries.push_back(a);
+ }
+ }
+ }
+
+ // now build the new list
+ delete[] pcNode->mMeshes;
+ pcNode->mNumMeshes = (unsigned int)aiEntries.size();
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+
+ for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) {
+ pcNode->mMeshes[b] = aiEntries[b];
+ }
+
+ // recusively update all other nodes
+ for (unsigned int i = 0; i < pcNode->mNumChildren;++i) {
+ UpdateNode ( pcNode->mChildren[i], avList );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Triangle::SplitMesh(
+ unsigned int a,
+ aiMesh* pMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
+ if (pMesh->mNumFaces > SplitLargeMeshesProcess_Triangle::LIMIT) {
+ ASSIMP_LOG_INFO("Mesh exceeds the triangle limit. It will be split ...");
+
+ // we need to split this mesh into sub meshes
+ // determine the size of a submesh
+ const unsigned int iSubMeshes = (pMesh->mNumFaces / LIMIT) + 1;
+
+ const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes;
+ const unsigned int iOutVertexNum = iOutFaceNum * 3;
+
+ // now generate all submeshes
+ for (unsigned int i = 0; i < iSubMeshes;++i) {
+ aiMesh* pcMesh = new aiMesh;
+ pcMesh->mNumFaces = iOutFaceNum;
+ pcMesh->mMaterialIndex = pMesh->mMaterialIndex;
+
+ // the name carries the adjacency information between the meshes
+ pcMesh->mName = pMesh->mName;
+
+ if (i == iSubMeshes-1) {
+ pcMesh->mNumFaces = iOutFaceNum + (
+ pMesh->mNumFaces - iOutFaceNum * iSubMeshes);
+ }
+ // copy the list of faces
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ const unsigned int iBase = iOutFaceNum * i;
+
+ // get the total number of indices
+ unsigned int iCnt = 0;
+ for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p) {
+ iCnt += pMesh->mFaces[p].mNumIndices;
+ }
+ pcMesh->mNumVertices = iCnt;
+
+ // allocate storage
+ if (pMesh->mVertices != nullptr) {
+ pcMesh->mVertices = new aiVector3D[iCnt];
+ }
+
+ if (pMesh->HasNormals()) {
+ pcMesh->mNormals = new aiVector3D[iCnt];
+ }
+
+ if (pMesh->HasTangentsAndBitangents()) {
+ pcMesh->mTangents = new aiVector3D[iCnt];
+ pcMesh->mBitangents = new aiVector3D[iCnt];
+ }
+
+ // texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c];
+ if (pMesh->HasTextureCoords( c)) {
+ pcMesh->mTextureCoords[c] = new aiVector3D[iCnt];
+ }
+ }
+
+ // vertex colors
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) {
+ if (pMesh->HasVertexColors( c)) {
+ pcMesh->mColors[c] = new aiColor4D[iCnt];
+ }
+ }
+
+ if (pMesh->HasBones()) {
+ // assume the number of bones won't change in most cases
+ pcMesh->mBones = new aiBone*[pMesh->mNumBones];
+
+ // iterate through all bones of the mesh and find those which
+ // need to be copied to the split mesh
+ std::vector<aiVertexWeight> avTempWeights;
+ for (unsigned int p = 0; p < pcMesh->mNumBones;++p) {
+ aiBone* const bone = pcMesh->mBones[p];
+ avTempWeights.clear();
+ avTempWeights.reserve(bone->mNumWeights / iSubMeshes);
+
+ for (unsigned int q = 0; q < bone->mNumWeights;++q) {
+ aiVertexWeight& weight = bone->mWeights[q];
+ if(weight.mVertexId >= iBase && weight.mVertexId < iBase + iOutVertexNum) {
+ avTempWeights.push_back(weight);
+ weight = avTempWeights.back();
+ weight.mVertexId -= iBase;
+ }
+ }
+
+ if (!avTempWeights.empty()) {
+ // we'll need this bone. Copy it ...
+ aiBone* pc = new aiBone();
+ pcMesh->mBones[pcMesh->mNumBones++] = pc;
+ pc->mName = aiString(bone->mName);
+ pc->mNumWeights = (unsigned int)avTempWeights.size();
+ pc->mOffsetMatrix = bone->mOffsetMatrix;
+
+ // no need to reallocate the array for the last submesh.
+ // Here we can reuse the (large) source array, although
+ // we'll waste some memory
+ if (iSubMeshes-1 == i) {
+ pc->mWeights = bone->mWeights;
+ bone->mWeights = nullptr;
+ } else {
+ pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+ }
+
+ // copy the weights
+ ::memcpy(pc->mWeights,&avTempWeights[0],sizeof(aiVertexWeight)*pc->mNumWeights);
+ }
+ }
+ }
+
+ // (we will also need to copy the array of indices)
+ unsigned int iCurrent = 0;
+ for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) {
+ pcMesh->mFaces[p].mNumIndices = 3;
+ // allocate a new array
+ const unsigned int iTemp = p + iBase;
+ const unsigned int iNumIndices = pMesh->mFaces[iTemp].mNumIndices;
+
+ // setup face type and number of indices
+ pcMesh->mFaces[p].mNumIndices = iNumIndices;
+ unsigned int* pi = pMesh->mFaces[iTemp].mIndices;
+ unsigned int* piOut = pcMesh->mFaces[p].mIndices = new unsigned int[iNumIndices];
+
+ // need to update the output primitive types
+ switch (iNumIndices) {
+ case 1:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ }
+
+ // and copy the contents of the old array, offset by current base
+ for (unsigned int v = 0; v < iNumIndices;++v) {
+ unsigned int iIndex = pi[v];
+ unsigned int iIndexOut = iCurrent++;
+ piOut[v] = iIndexOut;
+
+ // copy positions
+ if (pMesh->mVertices != nullptr) {
+ pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex];
+ }
+
+ // copy normals
+ if (pMesh->HasNormals()) {
+ pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex];
+ }
+
+ // copy tangents/bitangents
+ if (pMesh->HasTangentsAndBitangents()) {
+ pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex];
+ pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex];
+ }
+
+ // texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (pMesh->HasTextureCoords( c ) ) {
+ pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex];
+ }
+ }
+ // vertex colors
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) {
+ if (pMesh->HasVertexColors( c)) {
+ pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex];
+ }
+ }
+ }
+ }
+
+ // add the newly created mesh to the list
+ avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a));
+ }
+
+ // now delete the old mesh data
+ delete pMesh;
+ } else {
+ avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Vertex::SplitLargeMeshesProcess_Vertex() {
+ LIMIT = AI_SLM_DEFAULT_MAX_VERTICES;
+}
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Vertex::~SplitLargeMeshesProcess_Vertex() {
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool SplitLargeMeshesProcess_Vertex::IsActive( unsigned int pFlags) const {
+ return (pFlags & aiProcess_SplitLargeMeshes) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) {
+ if (0xffffffff == this->LIMIT || nullptr == pScene ) {
+ return;
+ }
+
+ ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex begin");
+
+ std::vector<std::pair<aiMesh*, unsigned int> > avList;
+
+ //Check for point cloud first,
+ //Do not process point cloud, splitMesh works only with faces data
+ for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) {
+ return;
+ }
+ }
+
+ for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
+ this->SplitMesh(a, pScene->mMeshes[a], avList);
+ }
+
+ if (avList.size() != pScene->mNumMeshes) {
+ // it seems something has been split. rebuild the mesh list
+ delete[] pScene->mMeshes;
+ pScene->mNumMeshes = (unsigned int)avList.size();
+ pScene->mMeshes = new aiMesh*[avList.size()];
+
+ for (unsigned int i = 0; i < avList.size();++i) {
+ pScene->mMeshes[i] = avList[i].first;
+ }
+
+ // now we need to update all nodes
+ SplitLargeMeshesProcess_Triangle::UpdateNode(pScene->mRootNode,avList);
+ ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Vertex finished. Meshes have been split");
+ } else {
+ ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex finished. There was nothing to do");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties
+void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp) {
+ this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Vertex::SplitMesh(
+ unsigned int a,
+ aiMesh* pMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
+ if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT) {
+ typedef std::vector< std::pair<unsigned int,float> > VertexWeightTable;
+
+ // build a per-vertex weight list if necessary
+ VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(pMesh);
+
+ // we need to split this mesh into sub meshes
+ // determine the estimated size of a submesh
+ // (this could be too large. Max waste is a single digit percentage)
+ const unsigned int iSubMeshes = (pMesh->mNumVertices / SplitLargeMeshesProcess_Vertex::LIMIT) + 1;
+
+ // create a std::vector<unsigned int> to indicate which vertices
+ // have already been copied
+ std::vector<unsigned int> avWasCopied;
+ avWasCopied.resize(pMesh->mNumVertices,0xFFFFFFFF);
+
+ // try to find a good estimate for the number of output faces
+ // per mesh. Add 12.5% as buffer
+ unsigned int iEstimatedSize = pMesh->mNumFaces / iSubMeshes;
+ iEstimatedSize += iEstimatedSize >> 3;
+
+ // now generate all submeshes
+ unsigned int iBase( 0 );
+ while (true) {
+ const unsigned int iOutVertexNum = SplitLargeMeshesProcess_Vertex::LIMIT;
+ aiMesh* pcMesh = new aiMesh;
+ pcMesh->mNumVertices = 0;
+ pcMesh->mMaterialIndex = pMesh->mMaterialIndex;
+
+ // the name carries the adjacency information between the meshes
+ pcMesh->mName = pMesh->mName;
+
+ typedef std::vector<aiVertexWeight> BoneWeightList;
+ if (pMesh->HasBones()) {
+ pcMesh->mBones = new aiBone*[pMesh->mNumBones];
+ ::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones);
+ }
+
+ // clear the temporary helper array
+ if (iBase) {
+ // we can't use memset here we unsigned int needn' be 32 bits
+ for (auto &elem : avWasCopied) {
+ elem = 0xffffffff;
+ }
+ }
+
+ // output vectors
+ std::vector<aiFace> vFaces;
+
+ // reserve enough storage for most cases
+ if (pMesh->HasPositions()) {
+ pcMesh->mVertices = new aiVector3D[iOutVertexNum];
+ }
+ if (pMesh->HasNormals()) {
+ pcMesh->mNormals = new aiVector3D[iOutVertexNum];
+ }
+ if (pMesh->HasTangentsAndBitangents()) {
+ pcMesh->mTangents = new aiVector3D[iOutVertexNum];
+ pcMesh->mBitangents = new aiVector3D[iOutVertexNum];
+ }
+ for (unsigned int c = 0; pMesh->HasVertexColors(c);++c) {
+ pcMesh->mColors[c] = new aiColor4D[iOutVertexNum];
+ }
+ for (unsigned int c = 0; pMesh->HasTextureCoords(c);++c) {
+ pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c];
+ pcMesh->mTextureCoords[c] = new aiVector3D[iOutVertexNum];
+ }
+ vFaces.reserve(iEstimatedSize);
+
+ // (we will also need to copy the array of indices)
+ while (iBase < pMesh->mNumFaces) {
+ // allocate a new array
+ const unsigned int iNumIndices = pMesh->mFaces[iBase].mNumIndices;
+
+ // doesn't catch degenerates but is quite fast
+ unsigned int iNeed = 0;
+ for (unsigned int v = 0; v < iNumIndices;++v) {
+ unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v];
+
+ // check whether we do already have this vertex
+ if (0xFFFFFFFF == avWasCopied[iIndex]) {
+ iNeed++;
+ }
+ }
+ if (pcMesh->mNumVertices + iNeed > iOutVertexNum) {
+ // don't use this face
+ break;
+ }
+
+ vFaces.push_back(aiFace());
+ aiFace& rFace = vFaces.back();
+
+ // setup face type and number of indices
+ rFace.mNumIndices = iNumIndices;
+ rFace.mIndices = new unsigned int[iNumIndices];
+
+ // need to update the output primitive types
+ switch (rFace.mNumIndices) {
+ case 1:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ }
+
+ // and copy the contents of the old array, offset by current base
+ for (unsigned int v = 0; v < iNumIndices;++v) {
+ unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v];
+
+ // check whether we do already have this vertex
+ if (0xFFFFFFFF != avWasCopied[iIndex]) {
+ rFace.mIndices[v] = avWasCopied[iIndex];
+ continue;
+ }
+
+ // copy positions
+ pcMesh->mVertices[pcMesh->mNumVertices] = (pMesh->mVertices[iIndex]);
+
+ // copy normals
+ if (pMesh->HasNormals()) {
+ pcMesh->mNormals[pcMesh->mNumVertices] = (pMesh->mNormals[iIndex]);
+ }
+
+ // copy tangents/bitangents
+ if (pMesh->HasTangentsAndBitangents()) {
+ pcMesh->mTangents[pcMesh->mNumVertices] = (pMesh->mTangents[iIndex]);
+ pcMesh->mBitangents[pcMesh->mNumVertices] = (pMesh->mBitangents[iIndex]);
+ }
+
+ // texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (pMesh->HasTextureCoords( c)) {
+ pcMesh->mTextureCoords[c][pcMesh->mNumVertices] = pMesh->mTextureCoords[c][iIndex];
+ }
+ }
+ // vertex colors
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) {
+ if (pMesh->HasVertexColors( c)) {
+ pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex];
+ }
+ }
+ // check whether we have bone weights assigned to this vertex
+ rFace.mIndices[v] = pcMesh->mNumVertices;
+ if (avPerVertexWeights) {
+ VertexWeightTable& table = avPerVertexWeights[ pcMesh->mNumVertices ];
+ if( !table.empty() ) {
+ for (VertexWeightTable::const_iterator iter = table.begin();
+ iter != table.end();++iter) {
+ // allocate the bone weight array if necessary
+ BoneWeightList* pcWeightList = (BoneWeightList*)pcMesh->mBones[(*iter).first];
+ if (nullptr == pcWeightList) {
+ pcMesh->mBones[(*iter).first] = (aiBone*)(pcWeightList = new BoneWeightList());
+ }
+ pcWeightList->push_back(aiVertexWeight(pcMesh->mNumVertices,(*iter).second));
+ }
+ }
+ }
+
+ avWasCopied[iIndex] = pcMesh->mNumVertices;
+ pcMesh->mNumVertices++;
+ }
+ ++iBase;
+ if(pcMesh->mNumVertices == iOutVertexNum) {
+ // break here. The face is only added if it was complete
+ break;
+ }
+ }
+
+ // check which bones we'll need to create for this submesh
+ if (pMesh->HasBones()) {
+ aiBone** ppCurrent = pcMesh->mBones;
+ for (unsigned int k = 0; k < pMesh->mNumBones;++k) {
+ // check whether the bone is existing
+ BoneWeightList* pcWeightList;
+ if ((pcWeightList = (BoneWeightList*)pcMesh->mBones[k])) {
+ aiBone* pcOldBone = pMesh->mBones[k];
+ aiBone* pcOut( nullptr );
+ *ppCurrent++ = pcOut = new aiBone();
+ pcOut->mName = aiString(pcOldBone->mName);
+ pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix;
+ pcOut->mNumWeights = (unsigned int)pcWeightList->size();
+ pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights];
+
+ // copy the vertex weights
+ ::memcpy(pcOut->mWeights,&pcWeightList->operator[](0),
+ pcOut->mNumWeights * sizeof(aiVertexWeight));
+
+ // delete the temporary bone weight list
+ delete pcWeightList;
+ pcMesh->mNumBones++;
+ }
+ }
+ }
+
+ // copy the face list to the mesh
+ pcMesh->mFaces = new aiFace[vFaces.size()];
+ pcMesh->mNumFaces = (unsigned int)vFaces.size();
+
+ for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) {
+ pcMesh->mFaces[p] = vFaces[p];
+ }
+
+ // add the newly created mesh to the list
+ avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a));
+
+ if (iBase == pMesh->mNumFaces) {
+ // have all faces ... finish the outer loop, too
+ break;
+ }
+ }
+
+ // delete the per-vertex weight list again
+ delete[] avPerVertexWeights;
+
+ // now delete the old mesh data
+ delete pMesh;
+ return;
+ }
+ avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a));
+}
diff --git a/thirdparty/assimp/code/SplitLargeMeshes.h b/thirdparty/assimp/code/SplitLargeMeshes.h
new file mode 100644
index 0000000000..77f089ce7e
--- /dev/null
+++ b/thirdparty/assimp/code/SplitLargeMeshes.h
@@ -0,0 +1,210 @@
+/*
+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 split large meshes into submeshes
+ */
+#ifndef AI_SPLITLARGEMESHES_H_INC
+#define AI_SPLITLARGEMESHES_H_INC
+
+#include <vector>
+#include "BaseProcess.h"
+
+#include <assimp/mesh.h>
+#include <assimp/scene.h>
+
+class SplitLargeMeshesTest;
+
+namespace Assimp
+{
+
+class SplitLargeMeshesProcess_Triangle;
+class SplitLargeMeshesProcess_Vertex;
+
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
+
+// **********************************************************
+// Java: ConfigProperty.java,
+// ConfigProperty.DEFAULT_VERTEX_SPLIT_LIMIT
+// ConfigProperty.DEFAULT_TRIANGLE_SPLIT_LIMIT
+// **********************************************************
+
+// default limit for vertices
+#if (!defined AI_SLM_DEFAULT_MAX_VERTICES)
+# define AI_SLM_DEFAULT_MAX_VERTICES 1000000
+#endif
+
+// default limit for triangles
+#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES)
+# define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000
+#endif
+
+// ---------------------------------------------------------------------------
+/** Post-processing filter to split large meshes into sub-meshes
+ *
+ * Applied BEFORE the JoinVertices-Step occurs.
+ * Returns NON-UNIQUE vertices, splits by triangle number.
+*/
+class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess
+{
+ friend class SplitLargeMeshesProcess_Vertex;
+
+public:
+
+ SplitLargeMeshesProcess_Triangle();
+ ~SplitLargeMeshesProcess_Triangle();
+
+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);
+
+
+ //! Set the split limit - needed for unit testing
+ inline void SetLimit(unsigned int l)
+ {LIMIT = l;}
+
+ //! Get the split limit
+ inline unsigned int GetLimit() const
+ {return LIMIT;}
+
+public:
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+ // -------------------------------------------------------------------
+ //! Apply the algorithm to a given mesh
+ void SplitMesh (unsigned int a, aiMesh* pcMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+
+ // -------------------------------------------------------------------
+ //! Update a node in the asset after a few of its meshes
+ //! have been split
+ static void UpdateNode(aiNode* pcNode,
+ const std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+
+public:
+ //! Triangle limit
+ unsigned int LIMIT;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Post-processing filter to split large meshes into sub-meshes
+ *
+ * Applied AFTER the JoinVertices-Step occurs.
+ * Returns UNIQUE vertices, splits by vertex number.
+*/
+class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess
+{
+public:
+
+ SplitLargeMeshesProcess_Vertex();
+ ~SplitLargeMeshesProcess_Vertex();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * 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);
+
+
+ //! Set the split limit - needed for unit testing
+ inline void SetLimit(unsigned int l)
+ {LIMIT = l;}
+
+ //! Get the split limit
+ inline unsigned int GetLimit() const
+ {return LIMIT;}
+
+public:
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+ // -------------------------------------------------------------------
+ //! Apply the algorithm to a given mesh
+ void SplitMesh (unsigned int a, aiMesh* pcMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+
+ // NOTE: Reuse SplitLargeMeshesProcess_Triangle::UpdateNode()
+
+public:
+ //! Triangle limit
+ unsigned int LIMIT;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_SPLITLARGEMESHES_H_INC
diff --git a/thirdparty/assimp/code/StandardShapes.cpp b/thirdparty/assimp/code/StandardShapes.cpp
new file mode 100644
index 0000000000..2e5100130f
--- /dev/null
+++ b/thirdparty/assimp/code/StandardShapes.cpp
@@ -0,0 +1,507 @@
+/*
+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
diff --git a/thirdparty/assimp/code/StdOStreamLogStream.h b/thirdparty/assimp/code/StdOStreamLogStream.h
new file mode 100644
index 0000000000..893e261a2b
--- /dev/null
+++ b/thirdparty/assimp/code/StdOStreamLogStream.h
@@ -0,0 +1,101 @@
+/*
+---------------------------------------------------------------------------
+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
diff --git a/thirdparty/assimp/code/Subdivision.cpp b/thirdparty/assimp/code/Subdivision.cpp
new file mode 100644
index 0000000000..19db223a55
--- /dev/null
+++ b/thirdparty/assimp/code/Subdivision.cpp
@@ -0,0 +1,588 @@
+/*
+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 "ProcessHelper.h"
+#include <assimp/Vertex.h>
+#include <assimp/ai_assert.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];
+ }
+ }
+}
diff --git a/thirdparty/assimp/code/TargetAnimation.cpp b/thirdparty/assimp/code/TargetAnimation.cpp
new file mode 100644
index 0000000000..b8062499ff
--- /dev/null
+++ b/thirdparty/assimp/code/TargetAnimation.cpp
@@ -0,0 +1,248 @@
+/*
+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;
+ }
+}
diff --git a/thirdparty/assimp/code/TargetAnimation.h b/thirdparty/assimp/code/TargetAnimation.h
new file mode 100644
index 0000000000..91634ab5aa
--- /dev/null
+++ b/thirdparty/assimp/code/TargetAnimation.h
@@ -0,0 +1,183 @@
+/*
+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
diff --git a/thirdparty/assimp/code/TextureTransform.cpp b/thirdparty/assimp/code/TextureTransform.cpp
new file mode 100644
index 0000000000..8ae2ba7218
--- /dev/null
+++ b/thirdparty/assimp/code/TextureTransform.cpp
@@ -0,0 +1,566 @@
+/*
+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 A helper class that processes texture transformations */
+
+
+
+#include <assimp/Importer.hpp>
+#include <assimp/postprocess.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/scene.h>
+
+#include "TextureTransform.h"
+#include <assimp/StringUtils.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TextureTransformStep::TextureTransformStep() :
+ configFlags()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+TextureTransformStep::~TextureTransformStep()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool TextureTransformStep::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_TransformUVCoords) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties
+void TextureTransformStep::SetupProperties(const Importer* pImp)
+{
+ configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL);
+}
+
+// ------------------------------------------------------------------------------------------------
+void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
+{
+ /* This function tries to simplify the input UV transformation.
+ * That's very important as it allows us to reduce the number
+ * of output UV channels. The order in which the transformations
+ * are applied is - as always - scaling, rotation, translation.
+ */
+
+ char szTemp[512];
+ int rounded = 0;
+
+
+ /* Optimize the rotation angle. That's slightly difficult as
+ * we have an inprecise floating-point number (when comparing
+ * UV transformations we'll take that into account by using
+ * an epsilon of 5 degrees). If there is a rotation value, we can't
+ * perform any further optimizations.
+ */
+ if (info.mRotation)
+ {
+ float out = info.mRotation;
+ if ((rounded = static_cast<int>((info.mRotation / static_cast<float>(AI_MATH_TWO_PI)))))
+ {
+ out -= rounded * static_cast<float>(AI_MATH_PI);
+ ASSIMP_LOG_INFO_F("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out);
+ }
+
+ // Next step - convert negative rotation angles to positives
+ if (out < 0.f)
+ out = (float)AI_MATH_TWO_PI * 2 + out;
+
+ info.mRotation = out;
+ return;
+ }
+
+
+ /* Optimize UV translation in the U direction. To determine whether
+ * or not we can optimize we need to look at the requested mapping
+ * type (e.g. if mirroring is active there IS a difference between
+ * offset 2 and 3)
+ */
+ if ((rounded = (int)info.mTranslation.x)) {
+ float out = 0.0f;
+ szTemp[0] = 0;
+ if (aiTextureMapMode_Wrap == info.mapU) {
+ // Wrap - simple take the fraction of the field
+ out = info.mTranslation.x-(float)rounded;
+ ai_snprintf(szTemp, 512, "[w] UV U offset %f can be simplified to %f", info.mTranslation.x, out);
+ }
+ else if (aiTextureMapMode_Mirror == info.mapU && 1 != rounded) {
+ // Mirror
+ if (rounded % 2)
+ rounded--;
+ out = info.mTranslation.x-(float)rounded;
+
+ ai_snprintf(szTemp,512,"[m/d] UV U offset %f can be simplified to %f",info.mTranslation.x,out);
+ }
+ else if (aiTextureMapMode_Clamp == info.mapU || aiTextureMapMode_Decal == info.mapU) {
+ // Clamp - translations beyond 1,1 are senseless
+ ai_snprintf(szTemp,512,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x);
+
+ out = 1.f;
+ }
+ if (szTemp[0]) {
+ ASSIMP_LOG_INFO(szTemp);
+ info.mTranslation.x = out;
+ }
+ }
+
+ /* Optimize UV translation in the V direction. To determine whether
+ * or not we can optimize we need to look at the requested mapping
+ * type (e.g. if mirroring is active there IS a difference between
+ * offset 2 and 3)
+ */
+ if ((rounded = (int)info.mTranslation.y)) {
+ float out = 0.0f;
+ szTemp[0] = 0;
+ if (aiTextureMapMode_Wrap == info.mapV) {
+ // Wrap - simple take the fraction of the field
+ out = info.mTranslation.y-(float)rounded;
+ ::ai_snprintf(szTemp,512,"[w] UV V offset %f can be simplified to %f",info.mTranslation.y,out);
+ }
+ else if (aiTextureMapMode_Mirror == info.mapV && 1 != rounded) {
+ // Mirror
+ if (rounded % 2)
+ rounded--;
+ out = info.mTranslation.x-(float)rounded;
+
+ ::ai_snprintf(szTemp,512,"[m/d] UV V offset %f can be simplified to %f",info.mTranslation.y,out);
+ }
+ else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) {
+ // Clamp - translations beyond 1,1 are senseless
+ ::ai_snprintf(szTemp,512,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y);
+
+ out = 1.f;
+ }
+ if (szTemp[0]) {
+ ASSIMP_LOG_INFO(szTemp);
+ info.mTranslation.y = out;
+ }
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void UpdateUVIndex(const std::list<TTUpdateInfo>& l, unsigned int n)
+{
+ // Don't set if == 0 && wasn't set before
+ for (std::list<TTUpdateInfo>::const_iterator it = l.begin();it != l.end(); ++it) {
+ const TTUpdateInfo& info = *it;
+
+ if (info.directShortcut)
+ *info.directShortcut = n;
+ else if (!n)
+ {
+ info.mat->AddProperty<int>((int*)&n,1,AI_MATKEY_UVWSRC(info.semantic,info.index));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline const char* MappingModeToChar(aiTextureMapMode map)
+{
+ if (aiTextureMapMode_Wrap == map)
+ return "-w";
+
+ if (aiTextureMapMode_Mirror == map)
+ return "-m";
+
+ return "-c";
+}
+
+// ------------------------------------------------------------------------------------------------
+void TextureTransformStep::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("TransformUVCoordsProcess begin");
+
+
+ /* We build a per-mesh list of texture transformations we'll need
+ * to apply. To achieve this, we iterate through all materials,
+ * find all textures and get their transformations and UV indices.
+ * Then we search for all meshes using this material.
+ */
+ typedef std::list<STransformVecInfo> MeshTrafoList;
+ std::vector<MeshTrafoList> meshLists(pScene->mNumMeshes);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+
+ aiMaterial* mat = pScene->mMaterials[i];
+ for (unsigned int a = 0; a < mat->mNumProperties;++a) {
+
+ aiMaterialProperty* prop = mat->mProperties[a];
+ if (!::strcmp( prop->mKey.data, "$tex.file")) {
+ STransformVecInfo info;
+
+ // Setup a shortcut structure to allow for a fast updating
+ // of the UV index later
+ TTUpdateInfo update;
+ update.mat = (aiMaterial*) mat;
+ update.semantic = prop->mSemantic;
+ update.index = prop->mIndex;
+
+ // Get textured properties and transform
+ for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) {
+ aiMaterialProperty* prop2 = mat->mProperties[a2];
+ if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) {
+ continue;
+ }
+
+ if ( !::strcmp( prop2->mKey.data, "$tex.uvwsrc")) {
+ info.uvIndex = *((int*)prop2->mData);
+
+ // Store a direct pointer for later use
+ update.directShortcut = (unsigned int*) prop2->mData;
+ }
+
+ else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu")) {
+ info.mapU = *((aiTextureMapMode*)prop2->mData);
+ }
+ else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodev")) {
+ info.mapV = *((aiTextureMapMode*)prop2->mData);
+ }
+ else if ( !::strcmp( prop2->mKey.data, "$tex.uvtrafo")) {
+ // ValidateDS should check this
+ ai_assert(prop2->mDataLength >= 20);
+ ::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5);
+
+ // Directly remove this property from the list
+ mat->mNumProperties--;
+ for (unsigned int a3 = a2; a3 < mat->mNumProperties;++a3) {
+ mat->mProperties[a3] = mat->mProperties[a3+1];
+ }
+
+ delete prop2;
+
+ // Warn: could be an underflow, but this does not invoke undefined behaviour
+ --a2;
+ }
+ }
+
+ // Find out which transformations are to be evaluated
+ if (!(configFlags & AI_UVTRAFO_ROTATION)) {
+ info.mRotation = 0.f;
+ }
+ if (!(configFlags & AI_UVTRAFO_SCALING)) {
+ info.mScaling = aiVector2D(1.f,1.f);
+ }
+ if (!(configFlags & AI_UVTRAFO_TRANSLATION)) {
+ info.mTranslation = aiVector2D(0.f,0.f);
+ }
+
+ // Do some preprocessing
+ PreProcessUVTransform(info);
+ info.uvIndex = std::min(info.uvIndex,AI_MAX_NUMBER_OF_TEXTURECOORDS -1u);
+
+ // Find out whether this material is used by more than
+ // one mesh. This will make our task much, much more difficult!
+ unsigned int cnt = 0;
+ for (unsigned int n = 0; n < pScene->mNumMeshes;++n) {
+ if (pScene->mMeshes[n]->mMaterialIndex == i)
+ ++cnt;
+ }
+
+ if (!cnt)
+ continue;
+ else if (1 != cnt) {
+ // This material is referenced by more than one mesh!
+ // So we need to make sure the UV index for the texture
+ // is identical for each of it ...
+ info.lockedPos = AI_TT_UV_IDX_LOCK_TBD;
+ }
+
+ // Get all corresponding meshes
+ for (unsigned int n = 0; n < pScene->mNumMeshes;++n) {
+ aiMesh* mesh = pScene->mMeshes[n];
+ if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0])
+ continue;
+
+ unsigned int uv = info.uvIndex;
+ if (!mesh->mTextureCoords[uv]) {
+ // If the requested UV index is not available, take the first one instead.
+ uv = 0;
+ }
+
+ if (mesh->mNumUVComponents[info.uvIndex] >= 3){
+ ASSIMP_LOG_WARN("UV transformations on 3D mapping channels are not supported");
+ continue;
+ }
+
+ MeshTrafoList::iterator it;
+
+ // Check whether we have this transform setup already
+ for (it = meshLists[n].begin();it != meshLists[n].end(); ++it) {
+
+ if ((*it) == info && (*it).uvIndex == uv) {
+ (*it).updateList.push_back(update);
+ break;
+ }
+ }
+
+ if (it == meshLists[n].end()) {
+ meshLists[n].push_back(info);
+ meshLists[n].back().uvIndex = uv;
+ meshLists[n].back().updateList.push_back(update);
+ }
+ }
+ }
+ }
+ }
+
+ char buffer[1024]; // should be sufficiently large
+ unsigned int outChannels = 0, inChannels = 0, transformedChannels = 0;
+
+ // Now process all meshes. Important: we don't remove unreferenced UV channels.
+ // This is a job for the RemoveUnreferencedData-Step.
+ for (unsigned int q = 0; q < pScene->mNumMeshes;++q) {
+
+ aiMesh* mesh = pScene->mMeshes[q];
+ MeshTrafoList& trafo = meshLists[q];
+
+ inChannels += mesh->GetNumUVChannels();
+
+ if (!mesh->mTextureCoords[0] || trafo.empty() || (trafo.size() == 1 && trafo.begin()->IsUntransformed())) {
+ outChannels += mesh->GetNumUVChannels();
+ continue;
+ }
+
+ // Move untransformed UV channels to the first position in the list ....
+ // except if we need a new locked index which should be as small as possible
+ bool veto = false, need = false;
+ unsigned int cnt = 0;
+ unsigned int untransformed = 0;
+
+ MeshTrafoList::iterator it,it2;
+ for (it = trafo.begin();it != trafo.end(); ++it,++cnt) {
+
+ if (!(*it).IsUntransformed()) {
+ need = true;
+ }
+
+ if ((*it).lockedPos == AI_TT_UV_IDX_LOCK_TBD) {
+ // Lock this index and make sure it won't be changed
+ (*it).lockedPos = cnt;
+ veto = true;
+ continue;
+ }
+
+ if (!veto && it != trafo.begin() && (*it).IsUntransformed()) {
+ for (it2 = trafo.begin();it2 != it; ++it2) {
+ if (!(*it2).IsUntransformed())
+ break;
+ }
+ trafo.insert(it2,*it);
+ trafo.erase(it);
+ break;
+ }
+ }
+ if (!need)
+ continue;
+
+ // Find all that are not at their 'locked' position and move them to it.
+ // Conflicts are possible but quite unlikely.
+ cnt = 0;
+ for (it = trafo.begin();it != trafo.end(); ++it,++cnt) {
+ if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) {
+ it2 = trafo.begin();unsigned int t = 0;
+ while (t != (*it).lockedPos)
+ ++it2;
+
+ if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) {
+ ASSIMP_LOG_ERROR("Channel mismatch, can't compute all transformations properly [design bug]");
+ continue;
+ }
+
+ std::swap(*it2,*it);
+ if ((*it).lockedPos == untransformed)
+ untransformed = cnt;
+ }
+ }
+
+ // ... and add dummies for all unreferenced channels
+ // at the end of the list
+ bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
+ ref[n] = (!mesh->mTextureCoords[n] ? true : false);
+
+ for (it = trafo.begin();it != trafo.end(); ++it)
+ ref[(*it).uvIndex] = true;
+
+ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
+ if (ref[n])
+ continue;
+ trafo.push_back(STransformVecInfo());
+ trafo.back().uvIndex = n;
+ }
+
+ // Then check whether this list breaks the channel limit.
+ // The unimportant ones are at the end of the list, so
+ // it shouldn't be too worse if we remove them.
+ unsigned int size = (unsigned int)trafo.size();
+ if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_ERROR_F(static_cast<unsigned int>(trafo.size()), " UV channels required but just ",
+ AI_MAX_NUMBER_OF_TEXTURECOORDS, " available");
+ }
+ size = AI_MAX_NUMBER_OF_TEXTURECOORDS;
+ }
+
+
+ aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
+ old[n] = mesh->mTextureCoords[n];
+
+ // Now continue and generate the output channels. Channels
+ // that we're not going to need later can be overridden.
+ it = trafo.begin();
+ for (unsigned int n = 0; n < trafo.size();++n,++it) {
+
+ if (n >= size) {
+ // Try to use an untransformed channel for all channels we threw over board
+ UpdateUVIndex((*it).updateList,untransformed);
+ continue;
+ }
+
+ outChannels++;
+
+ // Write to the log
+ if (!DefaultLogger::isNullLogger()) {
+ ::ai_snprintf(buffer,1024,"Mesh %u, channel %u: t(%.3f,%.3f), s(%.3f,%.3f), r(%.3f), %s%s",
+ q,n,
+ (*it).mTranslation.x,
+ (*it).mTranslation.y,
+ (*it).mScaling.x,
+ (*it).mScaling.y,
+ AI_RAD_TO_DEG( (*it).mRotation),
+ MappingModeToChar ((*it).mapU),
+ MappingModeToChar ((*it).mapV));
+
+ ASSIMP_LOG_INFO(buffer);
+ }
+
+ // Check whether we need a new buffer here
+ if (mesh->mTextureCoords[n]) {
+
+ it2 = it;++it2;
+ for (unsigned int m = n+1; m < size;++m, ++it2) {
+
+ if ((*it2).uvIndex == n){
+ it2 = trafo.begin();
+ break;
+ }
+ }
+ if (it2 == trafo.begin()){
+ mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
+ }
+ }
+ else mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
+
+ aiVector3D* src = old[(*it).uvIndex];
+ aiVector3D* dest, *end;
+ dest = mesh->mTextureCoords[n];
+
+ ai_assert(NULL != src);
+
+ // Copy the data to the destination array
+ if (dest != src)
+ ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices);
+
+ end = dest + mesh->mNumVertices;
+
+ // Build a transformation matrix and transform all UV coords with it
+ if (!(*it).IsUntransformed()) {
+ const aiVector2D& trl = (*it).mTranslation;
+ const aiVector2D& scl = (*it).mScaling;
+
+ // fixme: simplify ..
+ ++transformedChannels;
+ aiMatrix3x3 matrix;
+
+ aiMatrix3x3 m2,m3,m4,m5;
+
+ m4.a1 = scl.x;
+ m4.b2 = scl.y;
+
+ m2.a3 = m2.b3 = 0.5f;
+ m3.a3 = m3.b3 = -0.5f;
+
+ if ((*it).mRotation > AI_TT_ROTATION_EPSILON )
+ aiMatrix3x3::RotationZ((*it).mRotation,matrix);
+
+ m5.a3 += trl.x; m5.b3 += trl.y;
+ matrix = m2 * m4 * matrix * m3 * m5;
+
+ for (src = dest; src != end; ++src) { /* manual homogenious divide */
+ src->z = 1.f;
+ *src = matrix * *src;
+ src->x /= src->z;
+ src->y /= src->z;
+ src->z = 0.f;
+ }
+ }
+
+ // Update all UV indices
+ UpdateUVIndex((*it).updateList,n);
+ }
+ }
+
+ // Print some detailed statistics into the log
+ if (!DefaultLogger::isNullLogger()) {
+
+ if (transformedChannels) {
+ ASSIMP_LOG_INFO_F("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")");
+ } else {
+ ASSIMP_LOG_DEBUG("TransformUVCoordsProcess finished");
+ }
+ }
+}
+
+
diff --git a/thirdparty/assimp/code/TextureTransform.h b/thirdparty/assimp/code/TextureTransform.h
new file mode 100644
index 0000000000..c556ff5d8c
--- /dev/null
+++ b/thirdparty/assimp/code/TextureTransform.h
@@ -0,0 +1,232 @@
+/*
+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 Definition of a helper step that processes texture transformations */
+#ifndef AI_TEXTURE_TRANSFORM_H_INCLUDED
+#define AI_TEXTURE_TRANSFORM_H_INCLUDED
+
+#include <assimp/BaseImporter.h>
+#include "BaseProcess.h"
+
+#include <assimp/material.h>
+#include <list>
+
+struct aiNode;
+struct aiMaterial;
+
+namespace Assimp {
+
+#define AI_TT_UV_IDX_LOCK_TBD 0xffffffff
+#define AI_TT_UV_IDX_LOCK_NONE 0xeeeeeeee
+
+
+#define AI_TT_ROTATION_EPSILON ((float)AI_DEG_TO_RAD(0.5))
+
+// ---------------------------------------------------------------------------
+/** Small helper structure representing a shortcut into the material list
+ * to be able to update some values quickly.
+*/
+struct TTUpdateInfo {
+ TTUpdateInfo() AI_NO_EXCEPT
+ : directShortcut(nullptr)
+ , mat(nullptr)
+ , semantic(0)
+ , index(0) {
+ // empty
+ }
+
+ //! Direct shortcut, if available
+ unsigned int* directShortcut;
+
+ //! Material
+ aiMaterial *mat;
+
+ //! Texture type and index
+ unsigned int semantic, index;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Helper class representing texture coordinate transformations
+*/
+struct STransformVecInfo : public aiUVTransform {
+ STransformVecInfo() AI_NO_EXCEPT
+ : uvIndex(0)
+ , mapU(aiTextureMapMode_Wrap)
+ , mapV(aiTextureMapMode_Wrap)
+ , lockedPos(AI_TT_UV_IDX_LOCK_NONE) {
+ // empty
+ }
+
+ //! Source texture coordinate index
+ unsigned int uvIndex;
+
+ //! Texture mapping mode in the u, v direction
+ aiTextureMapMode mapU,mapV;
+
+ //! Locked destination UV index
+ //! AI_TT_UV_IDX_LOCK_TBD - to be determined
+ //! AI_TT_UV_IDX_LOCK_NONE - none (default)
+ unsigned int lockedPos;
+
+ //! Update info - shortcuts into all materials
+ //! that are referencing this transform setup
+ std::list<TTUpdateInfo> updateList;
+
+
+ // -------------------------------------------------------------------
+ /** Compare two transform setups
+ */
+ inline bool operator== (const STransformVecInfo& other) const
+ {
+ // We use a small epsilon here
+ const static float epsilon = 0.05f;
+
+ if (std::fabs( mTranslation.x - other.mTranslation.x ) > epsilon ||
+ std::fabs( mTranslation.y - other.mTranslation.y ) > epsilon)
+ {
+ return false;
+ }
+
+ if (std::fabs( mScaling.x - other.mScaling.x ) > epsilon ||
+ std::fabs( mScaling.y - other.mScaling.y ) > epsilon)
+ {
+ return false;
+ }
+
+ if (std::fabs( mRotation - other.mRotation) > epsilon)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ inline bool operator!= (const STransformVecInfo& other) const
+ {
+ return !(*this == other);
+ }
+
+
+ // -------------------------------------------------------------------
+ /** Returns whether this is an untransformed texture coordinate set
+ */
+ inline bool IsUntransformed() const
+ {
+ return (1.0f == mScaling.x && 1.f == mScaling.y &&
+ !mTranslation.x && !mTranslation.y &&
+ mRotation < AI_TT_ROTATION_EPSILON);
+ }
+
+ // -------------------------------------------------------------------
+ /** Build a 3x3 matrix from the transformations
+ */
+ inline void GetMatrix(aiMatrix3x3& mOut)
+ {
+ mOut = aiMatrix3x3();
+
+ if (1.0f != mScaling.x || 1.0f != mScaling.y)
+ {
+ aiMatrix3x3 mScale;
+ mScale.a1 = mScaling.x;
+ mScale.b2 = mScaling.y;
+ mOut = mScale;
+ }
+ if (mRotation)
+ {
+ aiMatrix3x3 mRot;
+ mRot.a1 = mRot.b2 = std::cos(mRotation);
+ mRot.a2 = mRot.b1 = std::sin(mRotation);
+ mRot.a2 = -mRot.a2;
+ mOut *= mRot;
+ }
+ if (mTranslation.x || mTranslation.y)
+ {
+ aiMatrix3x3 mTrans;
+ mTrans.a3 = mTranslation.x;
+ mTrans.b3 = mTranslation.y;
+ mOut *= mTrans;
+ }
+ }
+};
+
+
+// ---------------------------------------------------------------------------
+/** Helper step to compute final UV coordinate sets if there are scalings
+ * or rotations in the original data read from the file.
+*/
+class TextureTransformStep : public BaseProcess
+{
+public:
+
+ TextureTransformStep();
+ ~TextureTransformStep();
+
+public:
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Preprocess a specific UV transformation setup
+ *
+ * @param info Transformation setup to be preprocessed.
+ */
+ void PreProcessUVTransform(STransformVecInfo& info);
+
+private:
+
+ unsigned int configFlags;
+};
+
+}
+
+#endif //! AI_TEXTURE_TRANSFORM_H_INCLUDED
diff --git a/thirdparty/assimp/code/TriangulateProcess.cpp b/thirdparty/assimp/code/TriangulateProcess.cpp
new file mode 100644
index 0000000000..0f68f47ddb
--- /dev/null
+++ b/thirdparty/assimp/code/TriangulateProcess.cpp
@@ -0,0 +1,530 @@
+/*
+---------------------------------------------------------------------------
+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 TriangulateProcess.cpp
+ * @brief Implementation of the post processing step to split up
+ * all faces with more than three indices into triangles.
+ *
+ *
+ * The triangulation algorithm will handle concave or convex polygons.
+ * Self-intersecting or non-planar polygons are not rejected, but
+ * they're probably not triangulated correctly.
+ *
+ * DEBUG SWITCHES - do not enable any of them in release builds:
+ *
+ * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+ * - generates vertex colors to represent the face winding order.
+ * the first vertex of a polygon becomes red, the last blue.
+ * AI_BUILD_TRIANGULATE_DEBUG_POLYS
+ * - dump all polygons and their triangulation sequences to
+ * a file
+ */
+#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
+#include "TriangulateProcess.h"
+#include "ProcessHelper.h"
+#include "PolyTools.h"
+#include <memory>
+
+//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+//#define AI_BUILD_TRIANGULATE_DEBUG_POLYS
+
+#define POLY_GRID_Y 40
+#define POLY_GRID_X 70
+#define POLY_GRID_XPAD 20
+#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TriangulateProcess::TriangulateProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+TriangulateProcess::~TriangulateProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool TriangulateProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_Triangulate) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void TriangulateProcess::Execute( aiScene* pScene)
+{
+ ASSIMP_LOG_DEBUG("TriangulateProcess begin");
+
+ bool bHas = false;
+ for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if (pScene->mMeshes[ a ]) {
+ if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) {
+ bHas = true;
+ }
+ }
+ }
+ if ( bHas ) {
+ ASSIMP_LOG_INFO( "TriangulateProcess finished. All polygons have been triangulated." );
+ } else {
+ ASSIMP_LOG_DEBUG( "TriangulateProcess finished. There was nothing to be done." );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Triangulates the given mesh.
+bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
+{
+ // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
+ if (!pMesh->mPrimitiveTypes) {
+ bool bNeed = false;
+
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ const aiFace& face = pMesh->mFaces[a];
+
+ if( face.mNumIndices != 3) {
+ bNeed = true;
+ }
+ }
+ if (!bNeed)
+ return false;
+ }
+ else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
+ return false;
+ }
+
+ // Find out how many output faces we'll get
+ unsigned int numOut = 0, max_out = 0;
+ bool get_normals = true;
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ aiFace& face = pMesh->mFaces[a];
+ if (face.mNumIndices <= 4) {
+ get_normals = false;
+ }
+ if( face.mNumIndices <= 3) {
+ numOut++;
+
+ }
+ else {
+ numOut += face.mNumIndices-2;
+ max_out = std::max(max_out,face.mNumIndices);
+ }
+ }
+
+ // Just another check whether aiMesh::mPrimitiveTypes is correct
+ ai_assert(numOut != pMesh->mNumFaces);
+
+ aiVector3D* nor_out = NULL;
+
+ // if we don't have normals yet, but expect them to be a cheap side
+ // product of triangulation anyway, allocate storage for them.
+ if (!pMesh->mNormals && get_normals) {
+ // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals
+ // nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ }
+
+ // the output mesh will contain triangles, but no polys anymore
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
+
+ aiFace* out = new aiFace[numOut](), *curOut = out;
+ std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */
+ std::vector<aiVector2D> temp_verts(max_out+2);
+
+ // Apply vertex colors to represent the face winding?
+#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+ if (!pMesh->mColors[0])
+ pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
+ else
+ new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices];
+
+ aiColor4D* clr = pMesh->mColors[0];
+#endif
+
+#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
+ FILE* fout = fopen(POLY_OUTPUT_FILE,"a");
+#endif
+
+ const aiVector3D* verts = pMesh->mVertices;
+
+ // use std::unique_ptr to avoid slow std::vector<bool> specialiations
+ std::unique_ptr<bool[]> done(new bool[max_out]);
+ for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ aiFace& face = pMesh->mFaces[a];
+
+ unsigned int* idx = face.mIndices;
+ int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num;
+
+ // Apply vertex colors to represent the face winding?
+#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+ for (unsigned int i = 0; i < face.mNumIndices; ++i) {
+ aiColor4D& c = clr[idx[i]];
+ c.r = (i+1) / (float)max;
+ c.b = 1.f - c.r;
+ }
+#endif
+
+ aiFace* const last_face = curOut;
+
+ // if it's a simple point,line or triangle: just copy it
+ if( face.mNumIndices <= 3)
+ {
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = face.mNumIndices;
+ nface.mIndices = face.mIndices;
+
+ face.mIndices = NULL;
+ continue;
+ }
+ // optimized code for quadrilaterals
+ else if ( face.mNumIndices == 4) {
+
+ // quads can have at maximum one concave vertex. Determine
+ // this vertex (if it exists) and start tri-fanning from
+ // it.
+ unsigned int start_vertex = 0;
+ for (unsigned int i = 0; i < 4; ++i) {
+ const aiVector3D& v0 = verts[face.mIndices[(i+3) % 4]];
+ const aiVector3D& v1 = verts[face.mIndices[(i+2) % 4]];
+ const aiVector3D& v2 = verts[face.mIndices[(i+1) % 4]];
+
+ const aiVector3D& v = verts[face.mIndices[i]];
+
+ aiVector3D left = (v0-v);
+ aiVector3D diag = (v1-v);
+ aiVector3D right = (v2-v);
+
+ left.Normalize();
+ diag.Normalize();
+ right.Normalize();
+
+ const float angle = std::acos(left*diag) + std::acos(right*diag);
+ if (angle > AI_MATH_PI_F) {
+ // this is the concave point
+ start_vertex = i;
+ break;
+ }
+ }
+
+ const unsigned int temp[] = {face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3]};
+
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = 3;
+ nface.mIndices = face.mIndices;
+
+ nface.mIndices[0] = temp[start_vertex];
+ nface.mIndices[1] = temp[(start_vertex + 1) % 4];
+ nface.mIndices[2] = temp[(start_vertex + 2) % 4];
+
+ aiFace& sface = *curOut++;
+ sface.mNumIndices = 3;
+ sface.mIndices = new unsigned int[3];
+
+ sface.mIndices[0] = temp[start_vertex];
+ sface.mIndices[1] = temp[(start_vertex + 2) % 4];
+ sface.mIndices[2] = temp[(start_vertex + 3) % 4];
+
+ // prevent double deletion of the indices field
+ face.mIndices = NULL;
+ continue;
+ }
+ else
+ {
+ // A polygon with more than 3 vertices can be either concave or convex.
+ // Usually everything we're getting is convex and we could easily
+ // triangulate by tri-fanning. However, LightWave is probably the only
+ // modeling suite to make extensive use of highly concave, monster polygons ...
+ // so we need to apply the full 'ear cutting' algorithm to get it right.
+
+ // RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
+ // We project it onto a plane to get a 2d triangle.
+
+ // Collect all vertices of of the polygon.
+ for (tmp = 0; tmp < max; ++tmp) {
+ temp_verts3d[tmp] = verts[idx[tmp]];
+ }
+
+ // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh
+ aiVector3D n;
+ NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z);
+ if (nor_out) {
+ for (tmp = 0; tmp < max; ++tmp)
+ nor_out[idx[tmp]] = n;
+ }
+
+ // Select largest normal coordinate to ignore for projection
+ const float ax = (n.x>0 ? n.x : -n.x);
+ const float ay = (n.y>0 ? n.y : -n.y);
+ const float az = (n.z>0 ? n.z : -n.z);
+
+ unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */
+ float inv = n.z;
+ if (ax > ay) {
+ if (ax > az) { /* no x coord. projection to yz */
+ ac = 1; bc = 2;
+ inv = n.x;
+ }
+ }
+ else if (ay > az) { /* no y coord. projection to zy */
+ ac = 2; bc = 0;
+ inv = n.y;
+ }
+
+ // Swap projection axes to take the negated projection vector into account
+ if (inv < 0.f) {
+ std::swap(ac,bc);
+ }
+
+ for (tmp =0; tmp < max; ++tmp) {
+ temp_verts[tmp].x = verts[idx[tmp]][ac];
+ temp_verts[tmp].y = verts[idx[tmp]][bc];
+ done[tmp] = false;
+ }
+
+#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
+ // plot the plane onto which we mapped the polygon to a 2D ASCII pic
+ aiVector2D bmin,bmax;
+ ArrayBounds(&temp_verts[0],max,bmin,bmax);
+
+ char grid[POLY_GRID_Y][POLY_GRID_X+POLY_GRID_XPAD];
+ std::fill_n((char*)grid,POLY_GRID_Y*(POLY_GRID_X+POLY_GRID_XPAD),' ');
+
+ for (int i =0; i < max; ++i) {
+ const aiVector2D& v = (temp_verts[i] - bmin) / (bmax-bmin);
+ const size_t x = static_cast<size_t>(v.x*(POLY_GRID_X-1)), y = static_cast<size_t>(v.y*(POLY_GRID_Y-1));
+ char* loc = grid[y]+x;
+ if (grid[y][x] != ' ') {
+ for(;*loc != ' '; ++loc);
+ *loc++ = '_';
+ }
+ *(loc+::ai_snprintf(loc, POLY_GRID_XPAD,"%i",i)) = ' ';
+ }
+
+
+ for(size_t y = 0; y < POLY_GRID_Y; ++y) {
+ grid[y][POLY_GRID_X+POLY_GRID_XPAD-1] = '\0';
+ fprintf(fout,"%s\n",grid[y]);
+ }
+
+ fprintf(fout,"\ntriangulation sequence: ");
+#endif
+
+ //
+ // FIXME: currently this is the slow O(kn) variant with a worst case
+ // complexity of O(n^2) (I think). Can be done in O(n).
+ while (num > 3) {
+
+ // Find the next ear of the polygon
+ int num_found = 0;
+ for (ear = next;;prev = ear,ear = next) {
+
+ // break after we looped two times without a positive match
+ for (next=ear+1;done[(next>=max?next=0:next)];++next);
+ if (next < ear) {
+ if (++num_found == 2) {
+ break;
+ }
+ }
+ const aiVector2D* pnt1 = &temp_verts[ear],
+ *pnt0 = &temp_verts[prev],
+ *pnt2 = &temp_verts[next];
+
+ // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1.
+ if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) {
+ continue;
+ }
+
+ // and no other point may be contained in this triangle
+ for ( tmp = 0; tmp < max; ++tmp) {
+
+ // We need to compare the actual values because it's possible that multiple indexes in
+ // the polygon are referring to the same position. concave_polygon.obj is a sample
+ //
+ // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in
+ // PointInTriangle() I'm guessing that it's actually possible to construct
+ // input data that would cause us to end up with no ears. The problem is,
+ // which epsilon? If we chose a too large value, we'd get wrong results
+ const aiVector2D& vtmp = temp_verts[tmp];
+ if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp)) {
+ break;
+ }
+ }
+ if (tmp != max) {
+ continue;
+ }
+
+ // this vertex is an ear
+ break;
+ }
+ if (num_found == 2) {
+
+ // Due to the 'two ear theorem', every simple polygon with more than three points must
+ // have 2 'ears'. Here's definitely something wrong ... but we don't give up yet.
+ //
+
+ // Instead we're continuing with the standard tri-fanning algorithm which we'd
+ // use if we had only convex polygons. That's life.
+ ASSIMP_LOG_ERROR("Failed to triangulate polygon (no ear found). Probably not a simple polygon?");
+
+#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
+ fprintf(fout,"critical error here, no ear found! ");
+#endif
+ num = 0;
+ break;
+
+ curOut -= (max-num); /* undo all previous work */
+ for (tmp = 0; tmp < max-2; ++tmp) {
+ aiFace& nface = *curOut++;
+
+ nface.mNumIndices = 3;
+ if (!nface.mIndices)
+ nface.mIndices = new unsigned int[3];
+
+ nface.mIndices[0] = 0;
+ nface.mIndices[1] = tmp+1;
+ nface.mIndices[2] = tmp+2;
+
+ }
+ num = 0;
+ break;
+ }
+
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = 3;
+
+ if (!nface.mIndices) {
+ nface.mIndices = new unsigned int[3];
+ }
+
+ // setup indices for the new triangle ...
+ nface.mIndices[0] = prev;
+ nface.mIndices[1] = ear;
+ nface.mIndices[2] = next;
+
+ // exclude the ear from most further processing
+ done[ear] = true;
+ --num;
+ }
+ if (num > 0) {
+ // We have three indices forming the last 'ear' remaining. Collect them.
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = 3;
+ if (!nface.mIndices) {
+ nface.mIndices = new unsigned int[3];
+ }
+
+ for (tmp = 0; done[tmp]; ++tmp);
+ nface.mIndices[0] = tmp;
+
+ for (++tmp; done[tmp]; ++tmp);
+ nface.mIndices[1] = tmp;
+
+ for (++tmp; done[tmp]; ++tmp);
+ nface.mIndices[2] = tmp;
+
+ }
+ }
+
+#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
+
+ for(aiFace* f = last_face; f != curOut; ++f) {
+ unsigned int* i = f->mIndices;
+ fprintf(fout," (%i %i %i)",i[0],i[1],i[2]);
+ }
+
+ fprintf(fout,"\n*********************************************************************\n");
+ fflush(fout);
+
+#endif
+
+ for(aiFace* f = last_face; f != curOut; ) {
+ unsigned int* i = f->mIndices;
+
+ // drop dumb 0-area triangles - deactivated for now:
+ //FindDegenerates post processing step can do the same thing
+ //if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
+ // ASSIMP_LOG_DEBUG("Dropping triangle with area 0");
+ // --curOut;
+
+ // delete[] f->mIndices;
+ // f->mIndices = nullptr;
+
+ // for(aiFace* ff = f; ff != curOut; ++ff) {
+ // ff->mNumIndices = (ff+1)->mNumIndices;
+ // ff->mIndices = (ff+1)->mIndices;
+ // (ff+1)->mIndices = nullptr;
+ // }
+ // continue;
+ //}
+
+ i[0] = idx[i[0]];
+ i[1] = idx[i[1]];
+ i[2] = idx[i[2]];
+ ++f;
+ }
+
+ delete[] face.mIndices;
+ face.mIndices = NULL;
+ }
+
+#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS
+ fclose(fout);
+#endif
+
+ // kill the old faces
+ delete [] pMesh->mFaces;
+
+ // ... and store the new ones
+ pMesh->mFaces = out;
+ pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */
+ return true;
+}
+
+#endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
diff --git a/thirdparty/assimp/code/TriangulateProcess.h b/thirdparty/assimp/code/TriangulateProcess.h
new file mode 100644
index 0000000000..47bd2115ad
--- /dev/null
+++ b/thirdparty/assimp/code/TriangulateProcess.h
@@ -0,0 +1,95 @@
+/*
+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 triangulate all faces
+ with more than three vertices.
+ */
+#ifndef AI_TRIANGULATEPROCESS_H_INC
+#define AI_TRIANGULATEPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+class TriangulateProcessTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The TriangulateProcess splits up all faces with more than three indices
+ * into triangles. You usually want this to happen because the graphics cards
+ * need their data as triangles.
+ */
+class ASSIMP_API TriangulateProcess : public BaseProcess
+{
+public:
+
+ TriangulateProcess();
+ ~TriangulateProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+public:
+ // -------------------------------------------------------------------
+ /** Triangulates the given mesh.
+ * @param pMesh The mesh to triangulate.
+ */
+ bool TriangulateMesh( aiMesh* pMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_TRIANGULATEPROCESS_H_INC
diff --git a/thirdparty/assimp/code/ValidateDataStructure.cpp b/thirdparty/assimp/code/ValidateDataStructure.cpp
new file mode 100644
index 0000000000..657b0361b7
--- /dev/null
+++ b/thirdparty/assimp/code/ValidateDataStructure.cpp
@@ -0,0 +1,986 @@
+/*
+---------------------------------------------------------------------------
+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 ValidateDataStructure.cpp
+ * @brief Implementation of the post processing step to validate
+ * the data structure returned by Assimp.
+ */
+
+
+
+// internal headers
+#include "ValidateDataStructure.h"
+#include <assimp/BaseImporter.h>
+#include <assimp/fast_atof.h>
+#include "ProcessHelper.h"
+#include <memory>
+
+// CRT headers
+#include <stdarg.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ValidateDSProcess::ValidateDSProcess() :
+ mScene()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ValidateDSProcess::~ValidateDSProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool ValidateDSProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_ValidateDataStructure) != 0;
+}
+// ------------------------------------------------------------------------------------------------
+AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...)
+{
+ ai_assert(NULL != msg);
+
+ va_list args;
+ va_start(args,msg);
+
+ char szBuffer[3000];
+ const int iLen = vsprintf(szBuffer,msg,args);
+ ai_assert(iLen > 0);
+
+ va_end(args);
+
+ throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen));
+}
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::ReportWarning(const char* msg,...)
+{
+ ai_assert(NULL != msg);
+
+ va_list args;
+ va_start(args,msg);
+
+ char szBuffer[3000];
+ const int iLen = vsprintf(szBuffer,msg,args);
+ ai_assert(iLen > 0);
+
+ va_end(args);
+ ASSIMP_LOG_WARN("Validation warning: " + std::string(szBuffer,iLen));
+}
+
+// ------------------------------------------------------------------------------------------------
+inline int HasNameMatch(const aiString& in, aiNode* node)
+{
+ int result = (node->mName == in ? 1 : 0 );
+ for (unsigned int i = 0; i < node->mNumChildren;++i) {
+ result += HasNameMatch(in,node->mChildren[i]);
+ }
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size,
+ const char* firstName, const char* secondName)
+{
+ // validate all entries
+ if (size)
+ {
+ if (!parray)
+ {
+ ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
+ firstName, secondName, size);
+ }
+ for (unsigned int i = 0; i < size;++i)
+ {
+ if (!parray[i])
+ {
+ ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
+ firstName,i,secondName,size);
+ }
+ Validate(parray[i]);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
+ const char* firstName, const char* secondName)
+{
+ // validate all entries
+ if (size)
+ {
+ if (!parray) {
+ ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
+ firstName, secondName, size);
+ }
+ for (unsigned int i = 0; i < size;++i)
+ {
+ if (!parray[i])
+ {
+ ReportError("aiScene::%s[%u] is NULL (aiScene::%s is %u)",
+ firstName,i,secondName,size);
+ }
+ Validate(parray[i]);
+
+ // check whether there are duplicate names
+ for (unsigned int a = i+1; a < size;++a)
+ {
+ if (parray[i]->mName == parray[a]->mName)
+ {
+ ReportError("aiScene::%s[%u] has the same name as "
+ "aiScene::%s[%u]",firstName, i,secondName, a);
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline
+void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName, const char* secondName) {
+ // validate all entries
+ DoValidationEx(array,size,firstName,secondName);
+
+ for (unsigned int i = 0; i < size;++i) {
+ int res = HasNameMatch(array[i]->mName,mScene->mRootNode);
+ if (0 == res) {
+ const std::string name = static_cast<char*>(array[i]->mName.data);
+ ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)",
+ firstName,i, name.c_str());
+ } else if (1 != res) {
+ const std::string name = static_cast<char*>(array[i]->mName.data);
+ ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name",
+ firstName,i, name.c_str());
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void ValidateDSProcess::Execute( aiScene* pScene)
+{
+ this->mScene = pScene;
+ ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin");
+
+ // validate the node graph of the scene
+ Validate(pScene->mRootNode);
+
+ // validate all meshes
+ if (pScene->mNumMeshes) {
+ DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes");
+ }
+ else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
+ ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there");
+ }
+ else if (pScene->mMeshes) {
+ ReportError("aiScene::mMeshes is non-null although there are no meshes");
+ }
+
+ // validate all animations
+ if (pScene->mNumAnimations) {
+ DoValidation(pScene->mAnimations,pScene->mNumAnimations,
+ "mAnimations","mNumAnimations");
+ }
+ else if (pScene->mAnimations) {
+ ReportError("aiScene::mAnimations is non-null although there are no animations");
+ }
+
+ // validate all cameras
+ if (pScene->mNumCameras) {
+ DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras,
+ "mCameras","mNumCameras");
+ }
+ else if (pScene->mCameras) {
+ ReportError("aiScene::mCameras is non-null although there are no cameras");
+ }
+
+ // validate all lights
+ if (pScene->mNumLights) {
+ DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights,
+ "mLights","mNumLights");
+ }
+ else if (pScene->mLights) {
+ ReportError("aiScene::mLights is non-null although there are no lights");
+ }
+
+ // validate all textures
+ if (pScene->mNumTextures) {
+ DoValidation(pScene->mTextures,pScene->mNumTextures,
+ "mTextures","mNumTextures");
+ }
+ else if (pScene->mTextures) {
+ ReportError("aiScene::mTextures is non-null although there are no textures");
+ }
+
+ // validate all materials
+ if (pScene->mNumMaterials) {
+ DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials");
+ }
+#if 0
+ // NOTE: ScenePreprocessor generates a default material if none is there
+ else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
+ ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
+ }
+#endif
+ else if (pScene->mMaterials) {
+ ReportError("aiScene::mMaterials is non-null although there are no materials");
+ }
+
+// if (!has)ReportError("The aiScene data structure is empty");
+ ASSIMP_LOG_DEBUG("ValidateDataStructureProcess end");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiLight* pLight)
+{
+ if (pLight->mType == aiLightSource_UNDEFINED)
+ ReportWarning("aiLight::mType is aiLightSource_UNDEFINED");
+
+ if (!pLight->mAttenuationConstant &&
+ !pLight->mAttenuationLinear &&
+ !pLight->mAttenuationQuadratic) {
+ ReportWarning("aiLight::mAttenuationXXX - all are zero");
+ }
+
+ if (pLight->mAngleInnerCone > pLight->mAngleOuterCone)
+ ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone");
+
+ if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack()
+ && pLight->mColorSpecular.IsBlack())
+ {
+ ReportWarning("aiLight::mColorXXX - all are black and won't have any influence");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiCamera* pCamera)
+{
+ if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
+ ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
+
+ // FIX: there are many 3ds files with invalid FOVs. No reason to
+ // reject them at all ... a warning is appropriate.
+ if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
+ ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiMesh* pMesh)
+{
+ // validate the material index of the mesh
+ if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials)
+ {
+ ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)",
+ pMesh->mMaterialIndex,mScene->mNumMaterials-1);
+ }
+
+ Validate(&pMesh->mName);
+
+ for (unsigned int i = 0; i < pMesh->mNumFaces; ++i)
+ {
+ aiFace& face = pMesh->mFaces[i];
+
+ if (pMesh->mPrimitiveTypes)
+ {
+ switch (face.mNumIndices)
+ {
+ case 0:
+ ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
+ break;
+ case 1:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
+ {
+ ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes "
+ "does not report the POINT flag",i);
+ }
+ break;
+ case 2:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE))
+ {
+ ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimitiveTypes "
+ "does not report the LINE flag",i);
+ }
+ break;
+ case 3:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE))
+ {
+ ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimitiveTypes "
+ "does not report the TRIANGLE flag",i);
+ }
+ break;
+ default:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON))
+ {
+ this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimitiveTypes "
+ "does not report the POLYGON flag",i);
+ }
+ break;
+ };
+ }
+
+ if (!face.mIndices)
+ ReportError("aiMesh::mFaces[%i].mIndices is NULL",i);
+ }
+
+ // positions must always be there ...
+ if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) {
+ ReportError("The mesh %s contains no vertices", pMesh->mName.C_Str());
+ }
+
+ if (pMesh->mNumVertices > AI_MAX_VERTICES) {
+ ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES);
+ }
+ if (pMesh->mNumFaces > AI_MAX_FACES) {
+ ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES);
+ }
+
+ // if tangents are there there must also be bitangent vectors ...
+ if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) {
+ ReportError("If there are tangents, bitangent vectors must be present as well");
+ }
+
+ // faces, too
+ if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) {
+ ReportError("Mesh %s contains no faces", pMesh->mName.C_Str());
+ }
+
+ // now check whether the face indexing layout is correct:
+ // unique vertices, pseudo-indexed.
+ std::vector<bool> abRefList;
+ abRefList.resize(pMesh->mNumVertices,false);
+ for (unsigned int i = 0; i < pMesh->mNumFaces;++i)
+ {
+ aiFace& face = pMesh->mFaces[i];
+ if (face.mNumIndices > AI_MAX_FACE_INDICES) {
+ ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES);
+ }
+
+ for (unsigned int a = 0; a < face.mNumIndices;++a)
+ {
+ if (face.mIndices[a] >= pMesh->mNumVertices) {
+ ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
+ }
+ // the MSB flag is temporarily used by the extra verbose
+ // mode to tell us that the JoinVerticesProcess might have
+ // been executed already.
+ /*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) &&
+ abRefList[face.mIndices[a]])
+ {
+ ReportError("aiMesh::mVertices[%i] is referenced twice - second "
+ "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);
+ }*/
+ abRefList[face.mIndices[a]] = true;
+ }
+ }
+
+ // check whether there are vertices that aren't referenced by a face
+ bool b = false;
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
+ if (!abRefList[i])b = true;
+ }
+ abRefList.clear();
+ if (b) {
+ ReportWarning("There are unreferenced vertices");
+ }
+
+ // texture channel 2 may not be set if channel 1 is zero ...
+ {
+ unsigned int i = 0;
+ for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ {
+ if (!pMesh->HasTextureCoords(i))break;
+ }
+ for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ if (pMesh->HasTextureCoords(i))
+ {
+ ReportError("Texture coordinate channel %i exists "
+ "although the previous channel was NULL.",i);
+ }
+ }
+ // the same for the vertex colors
+ {
+ unsigned int i = 0;
+ for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
+ {
+ if (!pMesh->HasVertexColors(i))break;
+ }
+ for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
+ if (pMesh->HasVertexColors(i))
+ {
+ ReportError("Vertex color channel %i is exists "
+ "although the previous channel was NULL.",i);
+ }
+ }
+
+
+ // now validate all bones
+ if (pMesh->mNumBones)
+ {
+ if (!pMesh->mBones)
+ {
+ ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)",
+ pMesh->mNumBones);
+ }
+ std::unique_ptr<float[]> afSum(nullptr);
+ if (pMesh->mNumVertices)
+ {
+ afSum.reset(new float[pMesh->mNumVertices]);
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
+ afSum[i] = 0.0f;
+ }
+
+ // check whether there are duplicate bone names
+ for (unsigned int i = 0; i < pMesh->mNumBones;++i)
+ {
+ const aiBone* bone = pMesh->mBones[i];
+ if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) {
+ ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS);
+ }
+
+ if (!pMesh->mBones[i])
+ {
+ ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
+ i,pMesh->mNumBones);
+ }
+ Validate(pMesh,pMesh->mBones[i],afSum.get());
+
+ for (unsigned int a = i+1; a < pMesh->mNumBones;++a)
+ {
+ if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName)
+ {
+ const char *name = "unknown";
+ if (nullptr != pMesh->mBones[ i ]->mName.C_Str()) {
+ name = pMesh->mBones[ i ]->mName.C_Str();
+ }
+ ReportError("aiMesh::mBones[%i], name = \"%s\" has the same name as "
+ "aiMesh::mBones[%i]", i, name, a );
+ }
+ }
+ }
+ // check whether all bone weights for a vertex sum to 1.0 ...
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
+ {
+ if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) {
+ ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]);
+ }
+ }
+ }
+ else if (pMesh->mBones)
+ {
+ ReportError("aiMesh::mBones is non-null although there are no bones");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiMesh* pMesh,
+ const aiBone* pBone,float* afSum)
+{
+ this->Validate(&pBone->mName);
+
+ if (!pBone->mNumWeights) {
+ ReportError("aiBone::mNumWeights is zero");
+ }
+
+ // check whether all vertices affected by this bone are valid
+ for (unsigned int i = 0; i < pBone->mNumWeights;++i)
+ {
+ if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) {
+ ReportError("aiBone::mWeights[%i].mVertexId is out of range",i);
+ }
+ else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) {
+ ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i);
+ }
+ afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
+{
+ Validate(&pAnimation->mName);
+
+ // validate all materials
+ if (pAnimation->mNumChannels)
+ {
+ if (!pAnimation->mChannels) {
+ ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)",
+ pAnimation->mNumChannels);
+ }
+ for (unsigned int i = 0; i < pAnimation->mNumChannels;++i)
+ {
+ if (!pAnimation->mChannels[i])
+ {
+ ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)",
+ i, pAnimation->mNumChannels);
+ }
+ Validate(pAnimation, pAnimation->mChannels[i]);
+ }
+ }
+ else {
+ ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
+ }
+
+ // Animation duration is allowed to be zero in cases where the anim contains only a single key frame.
+ // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
+ aiTextureType type)
+{
+ const char* szType = TextureTypeToString(type);
+
+ // ****************************************************************************
+ // Search all keys of the material ...
+ // textures must be specified with ascending indices
+ // (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...)
+ // ****************************************************************************
+
+ int iNumIndices = 0;
+ int iIndex = -1;
+ for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) {
+ aiMaterialProperty* prop = pMaterial->mProperties[ i ];
+ ai_assert(nullptr != prop);
+ if ( !::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == static_cast<unsigned int>(type)) {
+ iIndex = std::max(iIndex, (int) prop->mIndex);
+ ++iNumIndices;
+
+ if (aiPTI_String != prop->mType) {
+ ReportError("Material property %s is expected to be a string", prop->mKey.data);
+ }
+ }
+ }
+ if (iIndex +1 != iNumIndices) {
+ ReportError("%s #%i is set, but there are only %i %s textures",
+ szType,iIndex,iNumIndices,szType);
+ }
+ if (!iNumIndices)return;
+ std::vector<aiTextureMapping> mappings(iNumIndices);
+
+ // Now check whether all UV indices are valid ...
+ bool bNoSpecified = true;
+ for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
+ {
+ aiMaterialProperty* prop = pMaterial->mProperties[i];
+ if (prop->mSemantic != type)continue;
+
+ if ((int)prop->mIndex >= iNumIndices)
+ {
+ ReportError("Found texture property with index %i, although there "
+ "are only %i textures of type %s",
+ prop->mIndex, iNumIndices, szType);
+ }
+
+ if (!::strcmp(prop->mKey.data,"$tex.mapping")) {
+ if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping))
+ {
+ ReportError("Material property %s%i is expected to be an integer (size is %i)",
+ prop->mKey.data,prop->mIndex,prop->mDataLength);
+ }
+ mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
+ }
+ else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) {
+ if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform))
+ {
+ ReportError("Material property %s%i is expected to be 5 floats large (size is %i)",
+ prop->mKey.data,prop->mIndex, prop->mDataLength);
+ }
+ mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
+ }
+ else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) {
+ if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength)
+ {
+ ReportError("Material property %s%i is expected to be an integer (size is %i)",
+ prop->mKey.data,prop->mIndex,prop->mDataLength);
+ }
+ bNoSpecified = false;
+
+ // Ignore UV indices for texture channels that are not there ...
+
+ // Get the value
+ iIndex = *((unsigned int*)prop->mData);
+
+ // Check whether there is a mesh using this material
+ // which has not enough UV channels ...
+ for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
+ {
+ aiMesh* mesh = this->mScene->mMeshes[a];
+ if(mesh->mMaterialIndex == (unsigned int)i)
+ {
+ int iChannels = 0;
+ while (mesh->HasTextureCoords(iChannels))++iChannels;
+ if (iIndex >= iChannels)
+ {
+ ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels",
+ iIndex,prop->mKey.data,a,iChannels);
+ }
+ }
+ }
+ }
+ }
+ if (bNoSpecified)
+ {
+ // Assume that all textures are using the first UV channel
+ for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
+ {
+ aiMesh* mesh = mScene->mMeshes[a];
+ if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV)
+ {
+ if (!mesh->mTextureCoords[0])
+ {
+ // This is a special case ... it could be that the
+ // original mesh format intended the use of a special
+ // mapping here.
+ ReportWarning("UV-mapped texture, but there are no UV coords");
+ }
+ }
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
+{
+ // check whether there are material keys that are obviously not legal
+ for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
+ {
+ const aiMaterialProperty* prop = pMaterial->mProperties[i];
+ if (!prop) {
+ ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)",
+ i,pMaterial->mNumProperties);
+ }
+ if (!prop->mDataLength || !prop->mData) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength or "
+ "aiMaterial::mProperties[%i].mData is 0",i,i);
+ }
+ // check all predefined types
+ if (aiPTI_String == prop->mType) {
+ // FIX: strings are now stored in a less expensive way, but we can't use the
+ // validation routine for 'normal' aiStrings
+ if (prop->mDataLength < 5 || prop->mDataLength < 4 + (*reinterpret_cast<uint32_t*>(prop->mData)) + 1) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength is "
+ "too small to contain a string (%i, needed: %i)",
+ i,prop->mDataLength,static_cast<int>(sizeof(aiString)));
+ }
+ if(prop->mData[prop->mDataLength-1]) {
+ ReportError("Missing null-terminator in string material property");
+ }
+ // Validate((const aiString*)prop->mData);
+ }
+ else if (aiPTI_Float == prop->mType) {
+ if (prop->mDataLength < sizeof(float)) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength is "
+ "too small to contain a float (%i, needed: %i)",
+ i,prop->mDataLength, static_cast<int>(sizeof(float)));
+ }
+ }
+ else if (aiPTI_Integer == prop->mType) {
+ if (prop->mDataLength < sizeof(int)) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength is "
+ "too small to contain an integer (%i, needed: %i)",
+ i,prop->mDataLength, static_cast<int>(sizeof(int)));
+ }
+ }
+ // TODO: check whether there is a key with an unknown name ...
+ }
+
+ // make some more specific tests
+ ai_real fTemp;
+ int iShading;
+ if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) {
+ switch ((aiShadingMode)iShading)
+ {
+ case aiShadingMode_Blinn:
+ case aiShadingMode_CookTorrance:
+ case aiShadingMode_Phong:
+
+ if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) {
+ ReportWarning("A specular shading model is specified but there is no "
+ "AI_MATKEY_SHININESS key");
+ }
+ if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) {
+ ReportWarning("A specular shading model is specified but the value of the "
+ "AI_MATKEY_SHININESS_STRENGTH key is 0.0");
+ }
+ break;
+ default: ;
+ };
+ }
+
+ if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) {
+ ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)");
+ }
+
+ // Check whether there are invalid texture keys
+ // TODO: that's a relict of the past, where texture type and index were baked
+ // into the material string ... we could do that in one single pass.
+ SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE);
+ SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR);
+ SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT);
+ SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE);
+ SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY);
+ SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS);
+ SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT);
+ SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS);
+ SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT);
+ SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP);
+ SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiTexture* pTexture)
+{
+ // the data section may NEVER be NULL
+ if (!pTexture->pcData) {
+ ReportError("aiTexture::pcData is NULL");
+ }
+ if (pTexture->mHeight)
+ {
+ if (!pTexture->mWidth){
+ ReportError("aiTexture::mWidth is zero (aiTexture::mHeight is %i, uncompressed texture)",
+ pTexture->mHeight);
+ }
+ }
+ else
+ {
+ if (!pTexture->mWidth) {
+ ReportError("aiTexture::mWidth is zero (compressed texture)");
+ }
+ if ('\0' != pTexture->achFormatHint[3]) {
+ ReportWarning("aiTexture::achFormatHint must be zero-terminated");
+ }
+ else if ('.' == pTexture->achFormatHint[0]) {
+ ReportWarning("aiTexture::achFormatHint should contain a file extension "
+ "without a leading dot (format hint: %s).",pTexture->achFormatHint);
+ }
+ }
+
+ const char* sz = pTexture->achFormatHint;
+ if ((sz[0] >= 'A' && sz[0] <= 'Z') ||
+ (sz[1] >= 'A' && sz[1] <= 'Z') ||
+ (sz[2] >= 'A' && sz[2] <= 'Z') ||
+ (sz[3] >= 'A' && sz[3] <= 'Z')) {
+ ReportError("aiTexture::achFormatHint contains non-lowercase letters");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
+ const aiNodeAnim* pNodeAnim)
+{
+ Validate(&pNodeAnim->mNodeName);
+
+ if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys) {
+ ReportError("Empty node animation channel");
+ }
+ // otherwise check whether one of the keys exceeds the total duration of the animation
+ if (pNodeAnim->mNumPositionKeys)
+ {
+ if (!pNodeAnim->mPositionKeys)
+ {
+ ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
+ pNodeAnim->mNumPositionKeys);
+ }
+ double dLast = -10e10;
+ for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i)
+ {
+ // ScenePreprocessor will compute the duration if still the default value
+ // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration,
+ // seems to be due the compilers register usage/width.
+ if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001)
+ {
+ ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger "
+ "than aiAnimation::mDuration (which is %.5f)",i,
+ (float)pNodeAnim->mPositionKeys[i].mTime,
+ (float)pAnimation->mDuration);
+ }
+ if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast)
+ {
+ ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller "
+ "than aiAnimation::mPositionKeys[%i] (which is %.5f)",i,
+ (float)pNodeAnim->mPositionKeys[i].mTime,
+ i-1, (float)dLast);
+ }
+ dLast = pNodeAnim->mPositionKeys[i].mTime;
+ }
+ }
+ // rotation keys
+ if (pNodeAnim->mNumRotationKeys)
+ {
+ if (!pNodeAnim->mRotationKeys)
+ {
+ ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
+ pNodeAnim->mNumRotationKeys);
+ }
+ double dLast = -10e10;
+ for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i)
+ {
+ if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001)
+ {
+ ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger "
+ "than aiAnimation::mDuration (which is %.5f)",i,
+ (float)pNodeAnim->mRotationKeys[i].mTime,
+ (float)pAnimation->mDuration);
+ }
+ if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast)
+ {
+ ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller "
+ "than aiAnimation::mRotationKeys[%i] (which is %.5f)",i,
+ (float)pNodeAnim->mRotationKeys[i].mTime,
+ i-1, (float)dLast);
+ }
+ dLast = pNodeAnim->mRotationKeys[i].mTime;
+ }
+ }
+ // scaling keys
+ if (pNodeAnim->mNumScalingKeys)
+ {
+ if (!pNodeAnim->mScalingKeys) {
+ ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)",
+ pNodeAnim->mNumScalingKeys);
+ }
+ double dLast = -10e10;
+ for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i)
+ {
+ if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001)
+ {
+ ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger "
+ "than aiAnimation::mDuration (which is %.5f)",i,
+ (float)pNodeAnim->mScalingKeys[i].mTime,
+ (float)pAnimation->mDuration);
+ }
+ if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast)
+ {
+ ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller "
+ "than aiAnimation::mScalingKeys[%i] (which is %.5f)",i,
+ (float)pNodeAnim->mScalingKeys[i].mTime,
+ i-1, (float)dLast);
+ }
+ dLast = pNodeAnim->mScalingKeys[i].mTime;
+ }
+ }
+
+ if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys &&
+ !pNodeAnim->mNumPositionKeys)
+ {
+ ReportError("A node animation channel must have at least one subtrack");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiNode* pNode)
+{
+ if (!pNode) {
+ ReportError("A node of the scenegraph is NULL");
+ }
+ // Validate node name string first so that it's safe to use in below expressions
+ this->Validate(&pNode->mName);
+ const char* nodeName = (&pNode->mName)->C_Str();
+ if (pNode != mScene->mRootNode && !pNode->mParent){
+ ReportError("Non-root node %s lacks a valid parent (aiNode::mParent is NULL) ", nodeName);
+ }
+
+ // validate all meshes
+ if (pNode->mNumMeshes)
+ {
+ if (!pNode->mMeshes)
+ {
+ ReportError("aiNode::mMeshes is NULL for node %s (aiNode::mNumMeshes is %i)",
+ nodeName, pNode->mNumMeshes);
+ }
+ std::vector<bool> abHadMesh;
+ abHadMesh.resize(mScene->mNumMeshes,false);
+ for (unsigned int i = 0; i < pNode->mNumMeshes;++i)
+ {
+ if (pNode->mMeshes[i] >= mScene->mNumMeshes)
+ {
+ ReportError("aiNode::mMeshes[%i] is out of range for node %s (maximum is %i)",
+ pNode->mMeshes[i], nodeName, mScene->mNumMeshes-1);
+ }
+ if (abHadMesh[pNode->mMeshes[i]])
+ {
+ ReportError("aiNode::mMeshes[%i] is already referenced by this node %s (value: %i)",
+ i, nodeName, pNode->mMeshes[i]);
+ }
+ abHadMesh[pNode->mMeshes[i]] = true;
+ }
+ }
+ if (pNode->mNumChildren)
+ {
+ if (!pNode->mChildren) {
+ ReportError("aiNode::mChildren is NULL for node %s (aiNode::mNumChildren is %i)",
+ nodeName, pNode->mNumChildren);
+ }
+ for (unsigned int i = 0; i < pNode->mNumChildren;++i) {
+ Validate(pNode->mChildren[i]);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiString* pString)
+{
+ if (pString->length > MAXLEN)
+ {
+ ReportError("aiString::length is too large (%lu, maximum is %lu)",
+ pString->length,MAXLEN);
+ }
+ const char* sz = pString->data;
+ while (true)
+ {
+ if ('\0' == *sz)
+ {
+ if (pString->length != (unsigned int)(sz-pString->data)) {
+ ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
+ }
+ break;
+ }
+ else if (sz >= &pString->data[MAXLEN]) {
+ ReportError("aiString::data is invalid. There is no terminal character");
+ }
+ ++sz;
+ }
+}
diff --git a/thirdparty/assimp/code/ValidateDataStructure.h b/thirdparty/assimp/code/ValidateDataStructure.h
new file mode 100644
index 0000000000..bd21e88545
--- /dev/null
+++ b/thirdparty/assimp/code/ValidateDataStructure.h
@@ -0,0 +1,188 @@
+/*
+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 (dummy) post processing step to validate the loader's
+ * output data structure (for debugging)
+ */
+#ifndef AI_VALIDATEPROCESS_H_INC
+#define AI_VALIDATEPROCESS_H_INC
+
+#include <assimp/types.h>
+#include <assimp/material.h>
+#include "BaseProcess.h"
+
+struct aiBone;
+struct aiMesh;
+struct aiAnimation;
+struct aiNodeAnim;
+struct aiTexture;
+struct aiMaterial;
+struct aiNode;
+struct aiString;
+struct aiCamera;
+struct aiLight;
+
+namespace Assimp {
+
+// --------------------------------------------------------------------------------------
+/** Validates the whole ASSIMP scene data structure for correctness.
+ * ImportErrorException is thrown of the scene is corrupt.*/
+// --------------------------------------------------------------------------------------
+class ValidateDSProcess : public BaseProcess
+{
+public:
+
+ ValidateDSProcess();
+ ~ValidateDSProcess();
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Report a validation error. This will throw an exception,
+ * control won't return.
+ * @param msg Format string for sprintf().*/
+ AI_WONT_RETURN void ReportError(const char* msg,...) AI_WONT_RETURN_SUFFIX;
+
+
+ // -------------------------------------------------------------------
+ /** Report a validation warning. This won't throw an exception,
+ * control will return to the caller.
+ * @param msg Format string for sprintf().*/
+ void ReportWarning(const char* msg,...);
+
+
+ // -------------------------------------------------------------------
+ /** Validates a mesh
+ * @param pMesh Input mesh*/
+ void Validate( const aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** Validates a bone
+ * @param pMesh Input mesh
+ * @param pBone Input bone*/
+ void Validate( const aiMesh* pMesh,const aiBone* pBone,float* afSum);
+
+ // -------------------------------------------------------------------
+ /** Validates an animation
+ * @param pAnimation Input animation*/
+ void Validate( const aiAnimation* pAnimation);
+
+ // -------------------------------------------------------------------
+ /** Validates a material
+ * @param pMaterial Input material*/
+ void Validate( const aiMaterial* pMaterial);
+
+ // -------------------------------------------------------------------
+ /** Search the material data structure for invalid or corrupt
+ * texture keys.
+ * @param pMaterial Input material
+ * @param type Type of the texture*/
+ void SearchForInvalidTextures(const aiMaterial* pMaterial,
+ aiTextureType type);
+
+ // -------------------------------------------------------------------
+ /** Validates a texture
+ * @param pTexture Input texture*/
+ void Validate( const aiTexture* pTexture);
+
+ // -------------------------------------------------------------------
+ /** Validates a light source
+ * @param pLight Input light
+ */
+ void Validate( const aiLight* pLight);
+
+ // -------------------------------------------------------------------
+ /** Validates a camera
+ * @param pCamera Input camera*/
+ void Validate( const aiCamera* pCamera);
+
+ // -------------------------------------------------------------------
+ /** Validates a bone animation channel
+ * @param pAnimation Animation channel.
+ * @param pBoneAnim Input bone animation */
+ void Validate( const aiAnimation* pAnimation,
+ const aiNodeAnim* pBoneAnim);
+
+ // -------------------------------------------------------------------
+ /** Validates a node and all of its subnodes
+ * @param Node Input node*/
+ void Validate( const aiNode* pNode);
+
+ // -------------------------------------------------------------------
+ /** Validates a string
+ * @param pString Input string*/
+ void Validate( const aiString* pString);
+
+private:
+
+ // template to validate one of the aiScene::mXXX arrays
+ template <typename T>
+ inline void DoValidation(T** array, unsigned int size,
+ const char* firstName, const char* secondName);
+
+ // extended version: checks whether T::mName occurs twice
+ template <typename T>
+ inline void DoValidationEx(T** array, unsigned int size,
+ const char* firstName, const char* secondName);
+
+ // extension to the first template which does also search
+ // the nodegraph for an item with the same name
+ template <typename T>
+ inline void DoValidationWithNameCheck(T** array, unsigned int size,
+ const char* firstName, const char* secondName);
+
+ aiScene* mScene;
+};
+
+
+
+
+} // end of namespace Assimp
+
+#endif // AI_VALIDATEPROCESS_H_INC
diff --git a/thirdparty/assimp/code/Version.cpp b/thirdparty/assimp/code/Version.cpp
new file mode 100644
index 0000000000..0381037ff1
--- /dev/null
+++ b/thirdparty/assimp/code/Version.cpp
@@ -0,0 +1,185 @@
+/*
+---------------------------------------------------------------------------
+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"
+
+static const unsigned int MajorVersion = 4;
+static const unsigned int MinorVersion = 1;
+
+// --------------------------------------------------------------------------------
+// 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) 2008-2017, assimp team\n"
+"License under the terms and conditions of the 3-clause BSD license\n"
+"http://assimp.sourceforge.net\n"
+;
+
+// ------------------------------------------------------------------------------------------------
+// Get legal string
+ASSIMP_API const char* aiGetLegalString () {
+ return LEGAL_INFORMATION;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get Assimp minor version
+ASSIMP_API unsigned int aiGetVersionMinor () {
+ return MinorVersion;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get Assimp major version
+ASSIMP_API unsigned int aiGetVersionMajor () {
+ return MajorVersion;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+}
+
+// include current build revision, which is even updated from time to time -- :-)
+#include "revision.h"
+
+// ------------------------------------------------------------------------------------------------
+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 );
+}
+
diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.cpp b/thirdparty/assimp/code/VertexTriangleAdjacency.cpp
new file mode 100644
index 0000000000..7cfd1a3505
--- /dev/null
+++ b/thirdparty/assimp/code/VertexTriangleAdjacency.cpp
@@ -0,0 +1,134 @@
+/*
+---------------------------------------------------------------------------
+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;
+}
diff --git a/thirdparty/assimp/code/VertexTriangleAdjacency.h b/thirdparty/assimp/code/VertexTriangleAdjacency.h
new file mode 100644
index 0000000000..f3be47612d
--- /dev/null
+++ b/thirdparty/assimp/code/VertexTriangleAdjacency.h
@@ -0,0 +1,117 @@
+/*
+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
diff --git a/thirdparty/assimp/code/Win32DebugLogStream.h b/thirdparty/assimp/code/Win32DebugLogStream.h
new file mode 100644
index 0000000000..a6063a261e
--- /dev/null
+++ b/thirdparty/assimp/code/Win32DebugLogStream.h
@@ -0,0 +1,95 @@
+/*
+---------------------------------------------------------------------------
+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
diff --git a/thirdparty/assimp/code/revision.h b/thirdparty/assimp/code/revision.h
new file mode 100644
index 0000000000..88872aef22
--- /dev/null
+++ b/thirdparty/assimp/code/revision.h
@@ -0,0 +1,7 @@
+#ifndef ASSIMP_REVISION_H_INC
+#define ASSIMP_REVISION_H_INC
+
+#define GitVersion 0x00000000
+#define GitBranch "master"
+
+#endif // ASSIMP_REVISION_H_INC
diff --git a/thirdparty/assimp/code/scene.cpp b/thirdparty/assimp/code/scene.cpp
new file mode 100644
index 0000000000..2acb348d81
--- /dev/null
+++ b/thirdparty/assimp/code/scene.cpp
@@ -0,0 +1,140 @@
+/*
+---------------------------------------------------------------------------
+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(NULL)
+, mNumChildren(0)
+, mChildren(NULL)
+, mNumMeshes(0)
+, mMeshes(NULL)
+, mMetaData(NULL) {
+ // empty
+}
+
+aiNode::aiNode(const std::string& name)
+: mName(name)
+, mParent(NULL)
+, mNumChildren(0)
+, mChildren(NULL)
+, mNumMeshes(0)
+, mMeshes(NULL)
+, mMetaData(NULL) {
+ // empty
+}
+
+/** Destructor */
+aiNode::~aiNode() {
+ // delete all children recursively
+ // to make sure we won't crash if the data is invalid ...
+ if (mChildren && mNumChildren)
+ {
+ 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;
+ }
+}
diff --git a/thirdparty/assimp/code/simd.cpp b/thirdparty/assimp/code/simd.cpp
new file mode 100644
index 0000000000..04615f408e
--- /dev/null
+++ b/thirdparty/assimp/code/simd.cpp
@@ -0,0 +1,79 @@
+/*
+---------------------------------------------------------------------------
+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
diff --git a/thirdparty/assimp/code/simd.h b/thirdparty/assimp/code/simd.h
new file mode 100644
index 0000000000..3eecdd4581
--- /dev/null
+++ b/thirdparty/assimp/code/simd.h
@@ -0,0 +1,53 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+#pragma once
+
+#include <assimp/defs.h>
+
+namespace Assimp {
+
+/// @brief Checks if the platform supports SSE2 optimization
+/// @return true, if SSE2 is supported. false if SSE2 is not supported.
+bool ASSIMP_API CPUSupportsSSE2();
+
+} // Namespace Assimp
diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8.h
new file mode 100644
index 0000000000..82b13f59f9
--- /dev/null
+++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8.h
@@ -0,0 +1,34 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "utf8/checked.h"
+#include "utf8/unchecked.h"
+
+#endif // header guard
diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8/checked.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8/checked.h
new file mode 100644
index 0000000000..1331155138
--- /dev/null
+++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8/checked.h
@@ -0,0 +1,327 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "core.h"
+#include <stdexcept>
+
+namespace utf8
+{
+ // Base for the exceptions that may be thrown from the library
+ class exception : public ::std::exception {
+ };
+
+ // Exceptions that may be thrown from the library functions.
+ class invalid_code_point : public exception {
+ uint32_t cp;
+ public:
+ invalid_code_point(uint32_t cp) : cp(cp) {}
+ virtual const char* what() const throw() { return "Invalid code point"; }
+ uint32_t code_point() const {return cp;}
+ };
+
+ class invalid_utf8 : public exception {
+ uint8_t u8;
+ public:
+ invalid_utf8 (uint8_t u) : u8(u) {}
+ virtual const char* what() const throw() { return "Invalid UTF-8"; }
+ uint8_t utf8_octet() const {return u8;}
+ };
+
+ class invalid_utf16 : public exception {
+ uint16_t u16;
+ public:
+ invalid_utf16 (uint16_t u) : u16(u) {}
+ virtual const char* what() const throw() { return "Invalid UTF-16"; }
+ uint16_t utf16_word() const {return u16;}
+ };
+
+ class not_enough_room : public exception {
+ public:
+ virtual const char* what() const throw() { return "Not enough space"; }
+ };
+
+ /// The library API - functions intended to be called by the users
+
+ template <typename octet_iterator>
+ octet_iterator append(uint32_t cp, octet_iterator result)
+ {
+ if (!utf8::internal::is_code_point_valid(cp))
+ throw invalid_code_point(cp);
+
+ if (cp < 0x80) // one octet
+ *(result++) = static_cast<uint8_t>(cp);
+ else if (cp < 0x800) { // two octets
+ *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else if (cp < 0x10000) { // three octets
+ *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else { // four octets
+ *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
+ *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ return result;
+ }
+
+ template <typename octet_iterator, typename output_iterator>
+ output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
+ {
+ while (start != end) {
+ octet_iterator sequence_start = start;
+ internal::utf_error err_code = utf8::internal::validate_next(start, end);
+ switch (err_code) {
+ case internal::UTF8_OK :
+ for (octet_iterator it = sequence_start; it != start; ++it)
+ *out++ = *it;
+ break;
+ case internal::NOT_ENOUGH_ROOM:
+ throw not_enough_room();
+ case internal::INVALID_LEAD:
+ out = utf8::append (replacement, out);
+ ++start;
+ break;
+ case internal::INCOMPLETE_SEQUENCE:
+ case internal::OVERLONG_SEQUENCE:
+ case internal::INVALID_CODE_POINT:
+ out = utf8::append (replacement, out);
+ ++start;
+ // just one replacement mark for the sequence
+ while (start != end && utf8::internal::is_trail(*start))
+ ++start;
+ break;
+ }
+ }
+ return out;
+ }
+
+ template <typename octet_iterator, typename output_iterator>
+ inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
+ {
+ static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
+ return utf8::replace_invalid(start, end, out, replacement_marker);
+ }
+
+ template <typename octet_iterator>
+ uint32_t next(octet_iterator& it, octet_iterator end)
+ {
+ uint32_t cp = 0;
+ internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
+ switch (err_code) {
+ case internal::UTF8_OK :
+ break;
+ case internal::NOT_ENOUGH_ROOM :
+ throw not_enough_room();
+ case internal::INVALID_LEAD :
+ case internal::INCOMPLETE_SEQUENCE :
+ case internal::OVERLONG_SEQUENCE :
+ throw invalid_utf8(*it);
+ case internal::INVALID_CODE_POINT :
+ throw invalid_code_point(cp);
+ }
+ return cp;
+ }
+
+ template <typename octet_iterator>
+ uint32_t peek_next(octet_iterator it, octet_iterator end)
+ {
+ return utf8::next(it, end);
+ }
+
+ template <typename octet_iterator>
+ uint32_t prior(octet_iterator& it, octet_iterator start)
+ {
+ // can't do much if it == start
+ if (it == start)
+ throw not_enough_room();
+
+ octet_iterator end = it;
+ // Go back until we hit either a lead octet or start
+ while (utf8::internal::is_trail(*(--it)))
+ if (it == start)
+ throw invalid_utf8(*it); // error - no lead byte in the sequence
+ return utf8::peek_next(it, end);
+ }
+
+ /// Deprecated in versions that include "prior"
+ template <typename octet_iterator>
+ uint32_t previous(octet_iterator& it, octet_iterator pass_start)
+ {
+ octet_iterator end = it;
+ while (utf8::internal::is_trail(*(--it)))
+ if (it == pass_start)
+ throw invalid_utf8(*it); // error - no lead byte in the sequence
+ octet_iterator temp = it;
+ return utf8::next(temp, end);
+ }
+
+ template <typename octet_iterator, typename distance_type>
+ void advance (octet_iterator& it, distance_type n, octet_iterator end)
+ {
+ for (distance_type i = 0; i < n; ++i)
+ utf8::next(it, end);
+ }
+
+ template <typename octet_iterator>
+ typename std::iterator_traits<octet_iterator>::difference_type
+ distance (octet_iterator first, octet_iterator last)
+ {
+ typename std::iterator_traits<octet_iterator>::difference_type dist;
+ for (dist = 0; first < last; ++dist)
+ utf8::next(first, last);
+ return dist;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
+ {
+ while (start != end) {
+ uint32_t cp = utf8::internal::mask16(*start++);
+ // Take care of surrogate pairs first
+ if (utf8::internal::is_lead_surrogate(cp)) {
+ if (start != end) {
+ uint32_t trail_surrogate = utf8::internal::mask16(*start++);
+ if (utf8::internal::is_trail_surrogate(trail_surrogate))
+ cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
+ else
+ throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
+ }
+ else
+ throw invalid_utf16(static_cast<uint16_t>(cp));
+
+ }
+ // Lone trail surrogate
+ else if (utf8::internal::is_trail_surrogate(cp))
+ throw invalid_utf16(static_cast<uint16_t>(cp));
+
+ result = utf8::append(cp, result);
+ }
+ return result;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
+ {
+ while (start != end) {
+ uint32_t cp = utf8::next(start, end);
+ if (cp > 0xffff) { //make a surrogate pair
+ *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
+ *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
+ }
+ else
+ *result++ = static_cast<uint16_t>(cp);
+ }
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
+ {
+ while (start != end)
+ result = utf8::append(*(start++), result);
+
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
+ {
+ while (start != end)
+ (*result++) = utf8::next(start, end);
+
+ return result;
+ }
+
+ // The iterator class
+ template <typename octet_iterator>
+ class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
+ octet_iterator it;
+ octet_iterator range_start;
+ octet_iterator range_end;
+ public:
+ iterator () {}
+ explicit iterator (const octet_iterator& octet_it,
+ const octet_iterator& range_start,
+ const octet_iterator& range_end) :
+ it(octet_it), range_start(range_start), range_end(range_end)
+ {
+ if (it < range_start || it > range_end)
+ throw std::out_of_range("Invalid utf-8 iterator position");
+ }
+ // the default "big three" are OK
+ octet_iterator base () const { return it; }
+ uint32_t operator * () const
+ {
+ octet_iterator temp = it;
+ return utf8::next(temp, range_end);
+ }
+ bool operator == (const iterator& rhs) const
+ {
+ if (range_start != rhs.range_start || range_end != rhs.range_end)
+ throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
+ return (it == rhs.it);
+ }
+ bool operator != (const iterator& rhs) const
+ {
+ return !(operator == (rhs));
+ }
+ iterator& operator ++ ()
+ {
+ utf8::next(it, range_end);
+ return *this;
+ }
+ iterator operator ++ (int)
+ {
+ iterator temp = *this;
+ utf8::next(it, range_end);
+ return temp;
+ }
+ iterator& operator -- ()
+ {
+ utf8::prior(it, range_start);
+ return *this;
+ }
+ iterator operator -- (int)
+ {
+ iterator temp = *this;
+ utf8::prior(it, range_start);
+ return temp;
+ }
+ }; // class iterator
+
+} // namespace utf8
+
+#endif //header guard
+
+
diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8/core.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8/core.h
new file mode 100644
index 0000000000..693d388c07
--- /dev/null
+++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8/core.h
@@ -0,0 +1,329 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include <iterator>
+
+namespace utf8
+{
+ // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
+ // You may need to change them to match your system.
+ // These typedefs have the same names as ones from cstdint, or boost/cstdint
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+
+// Helper code - not intended to be directly called by the library users. May be changed at any time
+namespace internal
+{
+ // Unicode constants
+ // Leading (high) surrogates: 0xd800 - 0xdbff
+ // Trailing (low) surrogates: 0xdc00 - 0xdfff
+ const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
+ const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
+ const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
+ const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
+ const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
+ const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
+
+ // Maximum valid value for a Unicode code point
+ const uint32_t CODE_POINT_MAX = 0x0010ffffu;
+
+ template<typename octet_type>
+ inline uint8_t mask8(octet_type oc)
+ {
+ return static_cast<uint8_t>(0xff & oc);
+ }
+ template<typename u16_type>
+ inline uint16_t mask16(u16_type oc)
+ {
+ return static_cast<uint16_t>(0xffff & oc);
+ }
+ template<typename octet_type>
+ inline bool is_trail(octet_type oc)
+ {
+ return ((utf8::internal::mask8(oc) >> 6) == 0x2);
+ }
+
+ template <typename u16>
+ inline bool is_lead_surrogate(u16 cp)
+ {
+ return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
+ }
+
+ template <typename u16>
+ inline bool is_trail_surrogate(u16 cp)
+ {
+ return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
+ }
+
+ template <typename u16>
+ inline bool is_surrogate(u16 cp)
+ {
+ return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
+ }
+
+ template <typename u32>
+ inline bool is_code_point_valid(u32 cp)
+ {
+ return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
+ }
+
+ template <typename octet_iterator>
+ inline typename std::iterator_traits<octet_iterator>::difference_type
+ sequence_length(octet_iterator lead_it)
+ {
+ uint8_t lead = utf8::internal::mask8(*lead_it);
+ if (lead < 0x80)
+ return 1;
+ else if ((lead >> 5) == 0x6)
+ return 2;
+ else if ((lead >> 4) == 0xe)
+ return 3;
+ else if ((lead >> 3) == 0x1e)
+ return 4;
+ else
+ return 0;
+ }
+
+ template <typename octet_difference_type>
+ inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
+ {
+ if (cp < 0x80) {
+ if (length != 1)
+ return true;
+ }
+ else if (cp < 0x800) {
+ if (length != 2)
+ return true;
+ }
+ else if (cp < 0x10000) {
+ if (length != 3)
+ return true;
+ }
+
+ return false;
+ }
+
+ enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
+
+ /// Helper for get_sequence_x
+ template <typename octet_iterator>
+ utf_error increase_safely(octet_iterator& it, octet_iterator end)
+ {
+ if (++it == end)
+ return NOT_ENOUGH_ROOM;
+
+ if (!utf8::internal::is_trail(*it))
+ return INCOMPLETE_SEQUENCE;
+
+ return UTF8_OK;
+ }
+
+ #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
+
+ /// get_sequence_x functions decode utf-8 sequences of the length x
+ template <typename octet_iterator>
+ utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+ {
+ if (it == end)
+ return NOT_ENOUGH_ROOM;
+
+ code_point = utf8::internal::mask8(*it);
+
+ return UTF8_OK;
+ }
+
+ template <typename octet_iterator>
+ utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+ {
+ if (it == end)
+ return NOT_ENOUGH_ROOM;
+
+ code_point = utf8::internal::mask8(*it);
+
+ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+ code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
+
+ return UTF8_OK;
+ }
+
+ template <typename octet_iterator>
+ utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+ {
+ if (it == end)
+ return NOT_ENOUGH_ROOM;
+
+ code_point = utf8::internal::mask8(*it);
+
+ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+ code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
+
+ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+ code_point += (*it) & 0x3f;
+
+ return UTF8_OK;
+ }
+
+ template <typename octet_iterator>
+ utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+ {
+ if (it == end)
+ return NOT_ENOUGH_ROOM;
+
+ code_point = utf8::internal::mask8(*it);
+
+ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+ code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
+
+ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+ code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
+
+ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
+
+ code_point += (*it) & 0x3f;
+
+ return UTF8_OK;
+ }
+
+ #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
+
+ template <typename octet_iterator>
+ utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
+ {
+ // Save the original value of it so we can go back in case of failure
+ // Of course, it does not make much sense with i.e. stream iterators
+ octet_iterator original_it = it;
+
+ uint32_t cp = 0;
+ // Determine the sequence length based on the lead octet
+ typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
+ const octet_difference_type length = utf8::internal::sequence_length(it);
+
+ // Get trail octets and calculate the code point
+ utf_error err = UTF8_OK;
+ switch (length) {
+ case 0:
+ return INVALID_LEAD;
+ case 1:
+ err = utf8::internal::get_sequence_1(it, end, cp);
+ break;
+ case 2:
+ err = utf8::internal::get_sequence_2(it, end, cp);
+ break;
+ case 3:
+ err = utf8::internal::get_sequence_3(it, end, cp);
+ break;
+ case 4:
+ err = utf8::internal::get_sequence_4(it, end, cp);
+ break;
+ }
+
+ if (err == UTF8_OK) {
+ // Decoding succeeded. Now, security checks...
+ if (utf8::internal::is_code_point_valid(cp)) {
+ if (!utf8::internal::is_overlong_sequence(cp, length)){
+ // Passed! Return here.
+ code_point = cp;
+ ++it;
+ return UTF8_OK;
+ }
+ else
+ err = OVERLONG_SEQUENCE;
+ }
+ else
+ err = INVALID_CODE_POINT;
+ }
+
+ // Failure branch - restore the original value of the iterator
+ it = original_it;
+ return err;
+ }
+
+ template <typename octet_iterator>
+ inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
+ uint32_t ignored;
+ return utf8::internal::validate_next(it, end, ignored);
+ }
+
+} // namespace internal
+
+ /// The library API - functions intended to be called by the users
+
+ // Byte order mark
+ const uint8_t bom[] = {0xef, 0xbb, 0xbf};
+
+ template <typename octet_iterator>
+ octet_iterator find_invalid(octet_iterator start, octet_iterator end)
+ {
+ octet_iterator result = start;
+ while (result != end) {
+ utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
+ if (err_code != internal::UTF8_OK)
+ return result;
+ }
+ return result;
+ }
+
+ template <typename octet_iterator>
+ inline bool is_valid(octet_iterator start, octet_iterator end)
+ {
+ return (utf8::find_invalid(start, end) == end);
+ }
+
+ template <typename octet_iterator>
+ inline bool starts_with_bom (octet_iterator it, octet_iterator end)
+ {
+ return (
+ ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
+ ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
+ ((it != end) && (utf8::internal::mask8(*it)) == bom[2])
+ );
+ }
+
+ //Deprecated in release 2.3
+ template <typename octet_iterator>
+ inline bool is_bom (octet_iterator it)
+ {
+ return (
+ (utf8::internal::mask8(*it++)) == bom[0] &&
+ (utf8::internal::mask8(*it++)) == bom[1] &&
+ (utf8::internal::mask8(*it)) == bom[2]
+ );
+ }
+} // namespace utf8
+
+#endif // header guard
+
+
diff --git a/thirdparty/assimp/contrib/utf8cpp/source/utf8/unchecked.h b/thirdparty/assimp/contrib/utf8cpp/source/utf8/unchecked.h
new file mode 100644
index 0000000000..cb2427166b
--- /dev/null
+++ b/thirdparty/assimp/contrib/utf8cpp/source/utf8/unchecked.h
@@ -0,0 +1,228 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "core.h"
+
+namespace utf8
+{
+ namespace unchecked
+ {
+ template <typename octet_iterator>
+ octet_iterator append(uint32_t cp, octet_iterator result)
+ {
+ if (cp < 0x80) // one octet
+ *(result++) = static_cast<uint8_t>(cp);
+ else if (cp < 0x800) { // two octets
+ *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else if (cp < 0x10000) { // three octets
+ *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else { // four octets
+ *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
+ *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80);
+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ return result;
+ }
+
+ template <typename octet_iterator>
+ uint32_t next(octet_iterator& it)
+ {
+ uint32_t cp = utf8::internal::mask8(*it);
+ typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);
+ switch (length) {
+ case 1:
+ break;
+ case 2:
+ it++;
+ cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
+ break;
+ case 3:
+ ++it;
+ cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
+ ++it;
+ cp += (*it) & 0x3f;
+ break;
+ case 4:
+ ++it;
+ cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
+ ++it;
+ cp += (utf8::internal::mask8(*it) << 6) & 0xfff;
+ ++it;
+ cp += (*it) & 0x3f;
+ break;
+ }
+ ++it;
+ return cp;
+ }
+
+ template <typename octet_iterator>
+ uint32_t peek_next(octet_iterator it)
+ {
+ return utf8::unchecked::next(it);
+ }
+
+ template <typename octet_iterator>
+ uint32_t prior(octet_iterator& it)
+ {
+ while (utf8::internal::is_trail(*(--it))) ;
+ octet_iterator temp = it;
+ return utf8::unchecked::next(temp);
+ }
+
+ // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous)
+ template <typename octet_iterator>
+ inline uint32_t previous(octet_iterator& it)
+ {
+ return utf8::unchecked::prior(it);
+ }
+
+ template <typename octet_iterator, typename distance_type>
+ void advance (octet_iterator& it, distance_type n)
+ {
+ for (distance_type i = 0; i < n; ++i)
+ utf8::unchecked::next(it);
+ }
+
+ template <typename octet_iterator>
+ typename std::iterator_traits<octet_iterator>::difference_type
+ distance (octet_iterator first, octet_iterator last)
+ {
+ typename std::iterator_traits<octet_iterator>::difference_type dist;
+ for (dist = 0; first < last; ++dist)
+ utf8::unchecked::next(first);
+ return dist;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
+ {
+ while (start != end) {
+ uint32_t cp = utf8::internal::mask16(*start++);
+ // Take care of surrogate pairs first
+ if (utf8::internal::is_lead_surrogate(cp)) {
+ uint32_t trail_surrogate = utf8::internal::mask16(*start++);
+ cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
+ }
+ result = utf8::unchecked::append(cp, result);
+ }
+ return result;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
+ {
+ while (start < end) {
+ uint32_t cp = utf8::unchecked::next(start);
+ if (cp > 0xffff) { //make a surrogate pair
+ *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
+ *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
+ }
+ else
+ *result++ = static_cast<uint16_t>(cp);
+ }
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
+ {
+ while (start != end)
+ result = utf8::unchecked::append(*(start++), result);
+
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
+ {
+ while (start < end)
+ (*result++) = utf8::unchecked::next(start);
+
+ return result;
+ }
+
+ // The iterator class
+ template <typename octet_iterator>
+ class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
+ octet_iterator it;
+ public:
+ iterator () {}
+ explicit iterator (const octet_iterator& octet_it): it(octet_it) {}
+ // the default "big three" are OK
+ octet_iterator base () const { return it; }
+ uint32_t operator * () const
+ {
+ octet_iterator temp = it;
+ return utf8::unchecked::next(temp);
+ }
+ bool operator == (const iterator& rhs) const
+ {
+ return (it == rhs.it);
+ }
+ bool operator != (const iterator& rhs) const
+ {
+ return !(operator == (rhs));
+ }
+ iterator& operator ++ ()
+ {
+ ::std::advance(it, utf8::internal::sequence_length(it));
+ return *this;
+ }
+ iterator operator ++ (int)
+ {
+ iterator temp = *this;
+ ::std::advance(it, utf8::internal::sequence_length(it));
+ return temp;
+ }
+ iterator& operator -- ()
+ {
+ utf8::unchecked::prior(it);
+ return *this;
+ }
+ iterator operator -- (int)
+ {
+ iterator temp = *this;
+ utf8::unchecked::prior(it);
+ return temp;
+ }
+ }; // class iterator
+
+ } // namespace utf8::unchecked
+} // namespace utf8
+
+
+#endif // header guard
+
diff --git a/thirdparty/assimp/include/assimp/.editorconfig b/thirdparty/assimp/include/assimp/.editorconfig
new file mode 100644
index 0000000000..9ea66423ad
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/.editorconfig
@@ -0,0 +1,8 @@
+# See <http://EditorConfig.org> for details
+
+[*.{h,hpp,inl}]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_size = 4
+indent_style = space
diff --git a/thirdparty/assimp/include/assimp/BaseImporter.h b/thirdparty/assimp/include/assimp/BaseImporter.h
new file mode 100644
index 0000000000..48dfc8ed8b
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/BaseImporter.h
@@ -0,0 +1,361 @@
+/*
+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 Definition of the base class for all importer worker classes. */
+#ifndef INCLUDED_AI_BASEIMPORTER_H
+#define INCLUDED_AI_BASEIMPORTER_H
+
+#include "Exceptional.h"
+
+#include <vector>
+#include <set>
+#include <assimp/types.h>
+#include <assimp/ProgressHandler.hpp>
+
+struct aiScene;
+struct aiImporterDesc;
+
+namespace Assimp {
+
+class Importer;
+class IOSystem;
+class BaseProcess;
+class SharedPostProcessInfo;
+class IOStream;
+
+// utility to do char4 to uint32 in a portable manner
+#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \
+ (string[1] << 16) + (string[2] << 8) + string[3]))
+
+
+// ---------------------------------------------------------------------------
+/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface
+ * for all importer worker classes.
+ *
+ * The interface defines two functions: CanRead() is used to check if the
+ * importer can handle the format of the given file. If an implementation of
+ * this function returns true, the importer then calls ReadFile() which
+ * imports the given file. ReadFile is not overridable, it just calls
+ * InternReadFile() and catches any ImportErrorException that might occur.
+ */
+class ASSIMP_API BaseImporter {
+ friend class Importer;
+
+public:
+
+ /** Constructor to be privately used by #Importer */
+ BaseImporter() AI_NO_EXCEPT;
+
+ /** Destructor, private as well */
+ virtual ~BaseImporter();
+
+ // -------------------------------------------------------------------
+ /** Returns whether the class can handle the format of the given file.
+ *
+ * The implementation should be as quick as possible. A check for
+ * the file extension is enough. If no suitable loader is found with
+ * this strategy, CanRead() is called again, the 'checkSig' parameter
+ * set to true this time. Now the implementation is expected to
+ * perform a full check of the file structure, possibly searching the
+ * first bytes of the file for magic identifiers or keywords.
+ *
+ * @param pFile Path and file name of the file to be examined.
+ * @param pIOHandler The IO handler to use for accessing any file.
+ * @param checkSig Set to true if this method is called a second time.
+ * This time, the implementation may take more time to examine the
+ * contents of the file to be loaded for magic bytes, keywords, etc
+ * to be able to load files with unknown/not existent file extensions.
+ * @return true if the class can read this file, false if not.
+ */
+ virtual bool CanRead(
+ const std::string& pFile,
+ IOSystem* pIOHandler,
+ bool checkSig
+ ) const = 0;
+
+ // -------------------------------------------------------------------
+ /** Imports the given file and returns the imported data.
+ * If the import succeeds, ownership of the data is transferred to
+ * the caller. If the import fails, NULL is returned. The function
+ * takes care that any partially constructed data is destroyed
+ * beforehand.
+ *
+ * @param pImp #Importer object hosting this loader.
+ * @param pFile Path of the file to be imported.
+ * @param pIOHandler IO-Handler used to open this and possible other files.
+ * @return The imported data or NULL if failed. If it failed a
+ * human-readable error description can be retrieved by calling
+ * GetErrorText()
+ *
+ * @note This function is not intended to be overridden. Implement
+ * InternReadFile() to do the import. If an exception is thrown somewhere
+ * in InternReadFile(), this function will catch it and transform it into
+ * a suitable response to the caller.
+ */
+ aiScene* ReadFile(
+ const Importer* pImp,
+ const std::string& pFile,
+ IOSystem* pIOHandler
+ );
+
+ // -------------------------------------------------------------------
+ /** Returns the error description of the last error that occurred.
+ * @return A description of the last error that occurred. An empty
+ * string if there was no error.
+ */
+ const std::string& GetErrorText() const {
+ return m_ErrorText;
+ }
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ * @param pImp Importer instance
+ */
+ virtual void SetupProperties(
+ const Importer* pImp
+ );
+
+ // -------------------------------------------------------------------
+ /** Called by #Importer::GetImporterInfo to get a description of
+ * some loader features. Importers must provide this information. */
+ virtual const aiImporterDesc* GetInfo() const = 0;
+
+ // -------------------------------------------------------------------
+ /** Called by #Importer::GetExtensionList for each loaded importer.
+ * Take the extension list contained in the structure returned by
+ * #GetInfo and insert all file extensions into the given set.
+ * @param extension set to collect file extensions in*/
+ void GetExtensionList(std::set<std::string>& extensions);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure. The
+ * function is expected to throw an ImportErrorException if there is
+ * an error. If it terminates normally, the data in aiScene is
+ * expected to be correct. Override this function to implement the
+ * actual importing.
+ * <br>
+ * The output scene must meet the following requirements:<br>
+ * <ul>
+ * <li>At least a root node must be there, even if its only purpose
+ * is to reference one mesh.</li>
+ * <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives
+ * in the mesh are determined automatically in this case.</li>
+ * <li>the vertex data is stored in a pseudo-indexed "verbose" format.
+ * In fact this means that every vertex that is referenced by
+ * a face is unique. Or the other way round: a vertex index may
+ * not occur twice in a single aiMesh.</li>
+ * <li>aiAnimation::mDuration may be -1. Assimp determines the length
+ * of the animation automatically in this case as the length of
+ * the longest animation channel.</li>
+ * <li>aiMesh::mBitangents may be NULL if tangents and normals are
+ * given. In this case bitangents are computed as the cross product
+ * between normal and tangent.</li>
+ * <li>There needn't be a material. If none is there a default material
+ * is generated. However, it is recommended practice for loaders
+ * to generate a default material for yourself that matches the
+ * default material setting for the file format better than Assimp's
+ * generic default material. Note that default materials *should*
+ * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded
+ * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy)
+ * texture. </li>
+ * </ul>
+ * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul>
+ * <li> at least one mesh must be there</li>
+ * <li> there may be no meshes with 0 vertices or faces</li>
+ * </ul>
+ * This won't be checked (except by the validation step): Assimp will
+ * crash if one of the conditions is not met!
+ *
+ * @param pFile Path of the file to be imported.
+ * @param pScene The scene object to hold the imported data.
+ * NULL is not a valid parameter.
+ * @param pIOHandler The IO handler to use for any file access.
+ * NULL is not a valid parameter. */
+ virtual void InternReadFile(
+ const std::string& pFile,
+ aiScene* pScene,
+ IOSystem* pIOHandler
+ ) = 0;
+
+public: // static utilities
+
+ // -------------------------------------------------------------------
+ /** A utility for CanRead().
+ *
+ * The function searches the header of a file for a specific token
+ * and returns true if this token is found. This works for text
+ * files only. There is a rudimentary handling of UNICODE files.
+ * The comparison is case independent.
+ *
+ * @param pIOSystem IO System to work with
+ * @param file File name of the file
+ * @param tokens List of tokens to search for
+ * @param numTokens Size of the token array
+ * @param searchBytes Number of bytes to be searched for the tokens.
+ */
+ static bool SearchFileHeaderForToken(
+ IOSystem* pIOSystem,
+ const std::string& file,
+ const char** tokens,
+ unsigned int numTokens,
+ unsigned int searchBytes = 200,
+ bool tokensSol = false,
+ bool noAlphaBeforeTokens = false);
+
+ // -------------------------------------------------------------------
+ /** @brief Check whether a file has a specific file extension
+ * @param pFile Input file
+ * @param ext0 Extension to check for. Lowercase characters only, no dot!
+ * @param ext1 Optional second extension
+ * @param ext2 Optional third extension
+ * @note Case-insensitive
+ */
+ static bool SimpleExtensionCheck (
+ const std::string& pFile,
+ const char* ext0,
+ const char* ext1 = NULL,
+ const char* ext2 = NULL);
+
+ // -------------------------------------------------------------------
+ /** @brief Extract file extension from a string
+ * @param pFile Input file
+ * @return Extension without trailing dot, all lowercase
+ */
+ static std::string GetExtension (
+ const std::string& pFile);
+
+ // -------------------------------------------------------------------
+ /** @brief Check whether a file starts with one or more magic tokens
+ * @param pFile Input file
+ * @param pIOHandler IO system to be used
+ * @param magic n magic tokens
+ * @params num Size of magic
+ * @param offset Offset from file start where tokens are located
+ * @param Size of one token, in bytes. Maximally 16 bytes.
+ * @return true if one of the given tokens was found
+ *
+ * @note For convenience, the check is also performed for the
+ * byte-swapped variant of all tokens (big endian). Only for
+ * tokens of size 2,4.
+ */
+ static bool CheckMagicToken(
+ IOSystem* pIOHandler,
+ const std::string& pFile,
+ const void* magic,
+ unsigned int num,
+ unsigned int offset = 0,
+ unsigned int size = 4);
+
+ // -------------------------------------------------------------------
+ /** An utility for all text file loaders. It converts a file to our
+ * UTF8 character set. Errors are reported, but ignored.
+ *
+ * @param data File buffer to be converted to UTF8 data. The buffer
+ * is resized as appropriate. */
+ static void ConvertToUTF8(
+ std::vector<char>& data);
+
+ // -------------------------------------------------------------------
+ /** An utility for all text file loaders. It converts a file from our
+ * UTF8 character set back to ISO-8859-1. Errors are reported, but ignored.
+ *
+ * @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer
+ * is resized as appropriate. */
+ static void ConvertUTF8toISO8859_1(
+ std::string& data);
+
+ // -------------------------------------------------------------------
+ /// @brief Enum to define, if empty files are ok or not.
+ enum TextFileMode {
+ ALLOW_EMPTY,
+ FORBID_EMPTY
+ };
+
+ // -------------------------------------------------------------------
+ /** Utility for text file loaders which copies the contents of the
+ * file into a memory buffer and converts it to our UTF8
+ * representation.
+ * @param stream Stream to read from.
+ * @param data Output buffer to be resized and filled with the
+ * converted text file data. The buffer is terminated with
+ * a binary 0.
+ * @param mode Whether it is OK to load empty text files. */
+ static void TextFileToBuffer(
+ IOStream* stream,
+ std::vector<char>& data,
+ TextFileMode mode = FORBID_EMPTY);
+
+ // -------------------------------------------------------------------
+ /** Utility function to move a std::vector into a aiScene array
+ * @param vec The vector to be moved
+ * @param out The output pointer to the allocated array.
+ * @param numOut The output count of elements copied. */
+ template<typename T>
+ AI_FORCE_INLINE
+ static void CopyVector(
+ std::vector<T>& vec,
+ T*& out,
+ unsigned int& outLength)
+ {
+ outLength = unsigned(vec.size());
+ if (outLength) {
+ out = new T[outLength];
+ std::swap_ranges(vec.begin(), vec.end(), out);
+ }
+ }
+
+protected:
+ /// Error description in case there was one.
+ std::string m_ErrorText;
+ /// Currently set progress handler.
+ ProgressHandler* m_progress;
+};
+
+
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC
diff --git a/thirdparty/assimp/include/assimp/Bitmap.h b/thirdparty/assimp/include/assimp/Bitmap.h
new file mode 100644
index 0000000000..e6b5fb1327
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Bitmap.h
@@ -0,0 +1,125 @@
+/*
+---------------------------------------------------------------------------
+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.h
+ * @brief Defines bitmap format helper for textures
+ *
+ * Used for file formats which embed their textures into the model file.
+ */
+
+#ifndef AI_BITMAP_H_INC
+#define AI_BITMAP_H_INC
+
+#include "defs.h"
+#include <stdint.h>
+#include <cstddef>
+
+struct aiTexture;
+
+namespace Assimp {
+
+class IOStream;
+
+class ASSIMP_API Bitmap {
+protected:
+
+ struct Header {
+ uint16_t type;
+ uint32_t size;
+ uint16_t reserved1;
+ uint16_t reserved2;
+ uint32_t offset;
+
+ // We define the struct size because sizeof(Header) might return a wrong result because of structure padding.
+ // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field).
+ static const std::size_t header_size =
+ sizeof(uint16_t) + // type
+ sizeof(uint32_t) + // size
+ sizeof(uint16_t) + // reserved1
+ sizeof(uint16_t) + // reserved2
+ sizeof(uint32_t); // offset
+ };
+
+ struct DIB {
+ uint32_t size;
+ int32_t width;
+ int32_t height;
+ uint16_t planes;
+ uint16_t bits_per_pixel;
+ uint32_t compression;
+ uint32_t image_size;
+ int32_t x_resolution;
+ int32_t y_resolution;
+ uint32_t nb_colors;
+ uint32_t nb_important_colors;
+
+ // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding.
+ // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field).
+ static const std::size_t dib_size =
+ sizeof(uint32_t) + // size
+ sizeof(int32_t) + // width
+ sizeof(int32_t) + // height
+ sizeof(uint16_t) + // planes
+ sizeof(uint16_t) + // bits_per_pixel
+ sizeof(uint32_t) + // compression
+ sizeof(uint32_t) + // image_size
+ sizeof(int32_t) + // x_resolution
+ sizeof(int32_t) + // y_resolution
+ sizeof(uint32_t) + // nb_colors
+ sizeof(uint32_t); // nb_important_colors
+ };
+
+ static const std::size_t mBytesPerPixel = 4;
+
+public:
+ static void Save(aiTexture* texture, IOStream* file);
+
+protected:
+ static void WriteHeader(Header& header, IOStream* file);
+ static void WriteDIB(DIB& dib, IOStream* file);
+ static void WriteData(aiTexture* texture, IOStream* file);
+};
+
+}
+
+#endif // AI_BITMAP_H_INC
diff --git a/thirdparty/assimp/include/assimp/BlobIOSystem.h b/thirdparty/assimp/include/assimp/BlobIOSystem.h
new file mode 100644
index 0000000000..d005e5c119
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/BlobIOSystem.h
@@ -0,0 +1,338 @@
+/*
+---------------------------------------------------------------------------
+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 Provides cheat implementations for IOSystem and IOStream to
+ * redirect exporter output to a blob chain.*/
+
+#ifndef AI_BLOBIOSYSTEM_H_INCLUDED
+#define AI_BLOBIOSYSTEM_H_INCLUDED
+
+#include <assimp/IOStream.hpp>
+#include <assimp/cexport.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/DefaultLogger.hpp>
+#include <stdint.h>
+#include <set>
+#include <vector>
+
+namespace Assimp {
+ class BlobIOSystem;
+
+// --------------------------------------------------------------------------------------------
+/** Redirect IOStream to a blob */
+// --------------------------------------------------------------------------------------------
+class BlobIOStream : public IOStream
+{
+public:
+
+ BlobIOStream(BlobIOSystem* creator, const std::string& file, size_t initial = 4096)
+ : buffer()
+ , cur_size()
+ , file_size()
+ , cursor()
+ , initial(initial)
+ , file(file)
+ , creator(creator)
+ {
+ }
+
+
+ virtual ~BlobIOStream();
+
+public:
+
+ // -------------------------------------------------------------------
+ aiExportDataBlob* GetBlob()
+ {
+ aiExportDataBlob* blob = new aiExportDataBlob();
+ blob->size = file_size;
+ blob->data = buffer;
+
+ buffer = NULL;
+
+ return blob;
+ }
+
+
+public:
+
+
+ // -------------------------------------------------------------------
+ virtual size_t Read( void *,
+ size_t,
+ size_t )
+ {
+ return 0;
+ }
+
+ // -------------------------------------------------------------------
+ virtual size_t Write(const void* pvBuffer,
+ size_t pSize,
+ size_t pCount)
+ {
+ pSize *= pCount;
+ if (cursor + pSize > cur_size) {
+ Grow(cursor + pSize);
+ }
+
+ memcpy(buffer+cursor, pvBuffer, pSize);
+ cursor += pSize;
+
+ file_size = std::max(file_size,cursor);
+ return pCount;
+ }
+
+ // -------------------------------------------------------------------
+ virtual aiReturn Seek(size_t pOffset,
+ aiOrigin pOrigin)
+ {
+ switch(pOrigin)
+ {
+ case aiOrigin_CUR:
+ cursor += pOffset;
+ break;
+
+ case aiOrigin_END:
+ cursor = file_size - pOffset;
+ break;
+
+ case aiOrigin_SET:
+ cursor = pOffset;
+ break;
+
+ default:
+ return AI_FAILURE;
+ }
+
+ if (cursor > file_size) {
+ Grow(cursor);
+ }
+
+ file_size = std::max(cursor,file_size);
+ return AI_SUCCESS;
+ }
+
+ // -------------------------------------------------------------------
+ virtual size_t Tell() const
+ {
+ return cursor;
+ }
+
+ // -------------------------------------------------------------------
+ virtual size_t FileSize() const
+ {
+ return file_size;
+ }
+
+ // -------------------------------------------------------------------
+ virtual void Flush()
+ {
+ // ignore
+ }
+
+
+
+private:
+
+ // -------------------------------------------------------------------
+ void Grow(size_t need = 0)
+ {
+ // 1.5 and phi are very heap-friendly growth factors (the first
+ // allows for frequent re-use of heap blocks, the second
+ // forms a fibonacci sequence with similar characteristics -
+ // since this heavily depends on the heap implementation
+ // and other factors as well, i'll just go with 1.5 since
+ // it is quicker to compute).
+ size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) ));
+
+ const uint8_t* const old = buffer;
+ buffer = new uint8_t[new_size];
+
+ if (old) {
+ memcpy(buffer,old,cur_size);
+ delete[] old;
+ }
+
+ cur_size = new_size;
+ }
+
+private:
+
+ uint8_t* buffer;
+ size_t cur_size,file_size, cursor, initial;
+
+ const std::string file;
+ BlobIOSystem* const creator;
+};
+
+
+#define AI_BLOBIO_MAGIC "$blobfile"
+
+// --------------------------------------------------------------------------------------------
+/** Redirect IOSystem to a blob */
+// --------------------------------------------------------------------------------------------
+class BlobIOSystem : public IOSystem
+{
+
+ friend class BlobIOStream;
+ typedef std::pair<std::string, aiExportDataBlob*> BlobEntry;
+
+public:
+
+ BlobIOSystem()
+ {
+ }
+
+ virtual ~BlobIOSystem()
+ {
+ for(BlobEntry& blobby : blobs) {
+ delete blobby.second;
+ }
+ }
+
+public:
+
+ // -------------------------------------------------------------------
+ const char* GetMagicFileName() const
+ {
+ return AI_BLOBIO_MAGIC;
+ }
+
+
+ // -------------------------------------------------------------------
+ aiExportDataBlob* GetBlobChain()
+ {
+ // one must be the master
+ aiExportDataBlob* master = NULL, *cur;
+ for(const BlobEntry& blobby : blobs) {
+ if (blobby.first == AI_BLOBIO_MAGIC) {
+ master = blobby.second;
+ break;
+ }
+ }
+ if (!master) {
+ ASSIMP_LOG_ERROR("BlobIOSystem: no data written or master file was not closed properly.");
+ return NULL;
+ }
+
+ master->name.Set("");
+
+ cur = master;
+ for(const BlobEntry& blobby : blobs) {
+ if (blobby.second == master) {
+ continue;
+ }
+
+ cur->next = blobby.second;
+ cur = cur->next;
+
+ // extract the file extension from the file written
+ const std::string::size_type s = blobby.first.find_first_of('.');
+ cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s+1));
+ }
+
+ // give up blob ownership
+ blobs.clear();
+ return master;
+ }
+
+public:
+
+ // -------------------------------------------------------------------
+ virtual bool Exists( const char* pFile) const {
+ return created.find(std::string(pFile)) != created.end();
+ }
+
+
+ // -------------------------------------------------------------------
+ virtual char getOsSeparator() const {
+ return '/';
+ }
+
+
+ // -------------------------------------------------------------------
+ virtual IOStream* Open(const char* pFile,
+ const char* pMode)
+ {
+ if (pMode[0] != 'w') {
+ return NULL;
+ }
+
+ created.insert(std::string(pFile));
+ return new BlobIOStream(this,std::string(pFile));
+ }
+
+ // -------------------------------------------------------------------
+ virtual void Close( IOStream* pFile)
+ {
+ delete pFile;
+ }
+
+private:
+
+ // -------------------------------------------------------------------
+ void OnDestruct(const std::string& filename, BlobIOStream* child)
+ {
+ // we don't know in which the files are closed, so we
+ // can't reliably say that the first must be the master
+ // file ...
+ blobs.push_back( BlobEntry(filename,child->GetBlob()) );
+ }
+
+private:
+ std::set<std::string> created;
+ std::vector< BlobEntry > blobs;
+};
+
+
+// --------------------------------------------------------------------------------------------
+BlobIOStream :: ~BlobIOStream()
+{
+ creator->OnDestruct(file,this);
+ delete[] buffer;
+}
+
+
+} // end Assimp
+
+#endif
diff --git a/thirdparty/assimp/include/assimp/ByteSwapper.h b/thirdparty/assimp/include/assimp/ByteSwapper.h
new file mode 100644
index 0000000000..20a2463fb8
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/ByteSwapper.h
@@ -0,0 +1,287 @@
+/*
+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 Helper class tp perform various byte oder swappings
+ (e.g. little to big endian) */
+#ifndef AI_BYTESWAPPER_H_INC
+#define AI_BYTESWAPPER_H_INC
+
+#include <assimp/ai_assert.h>
+#include <assimp/types.h>
+#include <stdint.h>
+
+#if _MSC_VER >= 1400
+#include <stdlib.h>
+#endif
+
+namespace Assimp {
+// --------------------------------------------------------------------------------------
+/** 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() AI_NO_EXCEPT {}
+
+public:
+
+ // ----------------------------------------------------------------------
+ /** Swap two bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap2(void* _szOut)
+ {
+ ai_assert(_szOut);
+
+#if _MSC_VER >= 1400
+ uint16_t* const szOut = reinterpret_cast<uint16_t*>(_szOut);
+ *szOut = _byteswap_ushort(*szOut);
+#else
+ uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
+ std::swap(szOut[0],szOut[1]);
+#endif
+ }
+
+ // ----------------------------------------------------------------------
+ /** Swap four bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap4(void* _szOut)
+ {
+ ai_assert(_szOut);
+
+#if _MSC_VER >= 1400
+ uint32_t* const szOut = reinterpret_cast<uint32_t*>(_szOut);
+ *szOut = _byteswap_ulong(*szOut);
+#else
+ uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
+ std::swap(szOut[0],szOut[3]);
+ std::swap(szOut[1],szOut[2]);
+#endif
+ }
+
+ // ----------------------------------------------------------------------
+ /** Swap eight bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap8(void* _szOut)
+ {
+ ai_assert(_szOut);
+
+#if _MSC_VER >= 1400
+ uint64_t* const szOut = reinterpret_cast<uint64_t*>(_szOut);
+ *szOut = _byteswap_uint64(*szOut);
+#else
+ 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]);
+#endif
+ }
+
+ // ----------------------------------------------------------------------
+ /** 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) {
+#ifdef AI_BUILD_BIG_ENDIAN
+ le = le;
+#else
+ le = !le;
+#endif
+ 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);
+ }
+};
+} // end Intern
+} // end Assimp
+
+#endif //!! AI_BYTESWAPPER_H_INC
diff --git a/thirdparty/assimp/include/assimp/Compiler/poppack1.h b/thirdparty/assimp/include/assimp/Compiler/poppack1.h
new file mode 100644
index 0000000000..e033bc1472
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Compiler/poppack1.h
@@ -0,0 +1,22 @@
+
+// ===============================================================================
+// May be included multiple times - resets structure packing to the defaults
+// for all supported compilers. Reverts the changes made by #include <pushpack1.h>
+//
+// Currently this works on the following compilers:
+// MSVC 7,8,9
+// GCC
+// BORLAND (complains about 'pack state changed but not reverted', but works)
+// ===============================================================================
+
+#ifndef AI_PUSHPACK_IS_DEFINED
+# error pushpack1.h must be included after poppack1.h
+#endif
+
+// reset packing to the original value
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+# pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+#undef AI_PUSHPACK_IS_DEFINED
diff --git a/thirdparty/assimp/include/assimp/Compiler/pstdint.h b/thirdparty/assimp/include/assimp/Compiler/pstdint.h
new file mode 100644
index 0000000000..4de4ce2a90
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Compiler/pstdint.h
@@ -0,0 +1,912 @@
+/* A portable stdint.h
+ ****************************************************************************
+ * BSD License:
+ ****************************************************************************
+ *
+ * Copyright (c) 2005-2016 Paul Hsieh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ ****************************************************************************
+ *
+ * Version 0.1.15.4
+ *
+ * The ANSI C standard committee, for the C99 standard, specified the
+ * inclusion of a new standard include file called stdint.h. This is
+ * a very useful and long desired include file which contains several
+ * very precise definitions for integer scalar types that is
+ * critically important for making portable several classes of
+ * applications including cryptography, hashing, variable length
+ * integer libraries and so on. But for most developers its likely
+ * useful just for programming sanity.
+ *
+ * The problem is that some compiler vendors chose to ignore the C99
+ * standard and some older compilers have no opportunity to be updated.
+ * Because of this situation, simply including stdint.h in your code
+ * makes it unportable.
+ *
+ * So that's what this file is all about. Its an attempt to build a
+ * single universal include file that works on as many platforms as
+ * possible to deliver what stdint.h is supposed to. Even compilers
+ * that already come with stdint.h can use this file instead without
+ * any loss of functionality. A few things that should be noted about
+ * this file:
+ *
+ * 1) It is not guaranteed to be portable and/or present an identical
+ * interface on all platforms. The extreme variability of the
+ * ANSI C standard makes this an impossibility right from the
+ * very get go. Its really only meant to be useful for the vast
+ * majority of platforms that possess the capability of
+ * implementing usefully and precisely defined, standard sized
+ * integer scalars. Systems which are not intrinsically 2s
+ * complement may produce invalid constants.
+ *
+ * 2) There is an unavoidable use of non-reserved symbols.
+ *
+ * 3) Other standard include files are invoked.
+ *
+ * 4) This file may come in conflict with future platforms that do
+ * include stdint.h. The hope is that one or the other can be
+ * used with no real difference.
+ *
+ * 5) In the current version, if your platform can't represent
+ * int32_t, int16_t and int8_t, it just dumps out with a compiler
+ * error.
+ *
+ * 6) 64 bit integers may or may not be defined. Test for their
+ * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX.
+ * Note that this is different from the C99 specification which
+ * requires the existence of 64 bit support in the compiler. If
+ * this is not defined for your platform, yet it is capable of
+ * dealing with 64 bits then it is because this file has not yet
+ * been extended to cover all of your system's capabilities.
+ *
+ * 7) (u)intptr_t may or may not be defined. Test for its presence
+ * with the test: #ifdef PTRDIFF_MAX. If this is not defined
+ * for your platform, then it is because this file has not yet
+ * been extended to cover all of your system's capabilities, not
+ * because its optional.
+ *
+ * 8) The following might not been defined even if your platform is
+ * capable of defining it:
+ *
+ * WCHAR_MIN
+ * WCHAR_MAX
+ * (u)int64_t
+ * PTRDIFF_MIN
+ * PTRDIFF_MAX
+ * (u)intptr_t
+ *
+ * 9) The following have not been defined:
+ *
+ * WINT_MIN
+ * WINT_MAX
+ *
+ * 10) The criteria for defining (u)int_least(*)_t isn't clear,
+ * except for systems which don't have a type that precisely
+ * defined 8, 16, or 32 bit types (which this include file does
+ * not support anyways). Default definitions have been given.
+ *
+ * 11) The criteria for defining (u)int_fast(*)_t isn't something I
+ * would trust to any particular compiler vendor or the ANSI C
+ * committee. It is well known that "compatible systems" are
+ * commonly created that have very different performance
+ * characteristics from the systems they are compatible with,
+ * especially those whose vendors make both the compiler and the
+ * system. Default definitions have been given, but its strongly
+ * recommended that users never use these definitions for any
+ * reason (they do *NOT* deliver any serious guarantee of
+ * improved performance -- not in this file, nor any vendor's
+ * stdint.h).
+ *
+ * 12) The following macros:
+ *
+ * PRINTF_INTMAX_MODIFIER
+ * PRINTF_INT64_MODIFIER
+ * PRINTF_INT32_MODIFIER
+ * PRINTF_INT16_MODIFIER
+ * PRINTF_LEAST64_MODIFIER
+ * PRINTF_LEAST32_MODIFIER
+ * PRINTF_LEAST16_MODIFIER
+ * PRINTF_INTPTR_MODIFIER
+ *
+ * are strings which have been defined as the modifiers required
+ * for the "d", "u" and "x" printf formats to correctly output
+ * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t,
+ * (u)least32_t, (u)least16_t and (u)intptr_t types respectively.
+ * PRINTF_INTPTR_MODIFIER is not defined for some systems which
+ * provide their own stdint.h. PRINTF_INT64_MODIFIER is not
+ * defined if INT64_MAX is not defined. These are an extension
+ * beyond what C99 specifies must be in stdint.h.
+ *
+ * In addition, the following macros are defined:
+ *
+ * PRINTF_INTMAX_HEX_WIDTH
+ * PRINTF_INT64_HEX_WIDTH
+ * PRINTF_INT32_HEX_WIDTH
+ * PRINTF_INT16_HEX_WIDTH
+ * PRINTF_INT8_HEX_WIDTH
+ * PRINTF_INTMAX_DEC_WIDTH
+ * PRINTF_INT64_DEC_WIDTH
+ * PRINTF_INT32_DEC_WIDTH
+ * PRINTF_INT16_DEC_WIDTH
+ * PRINTF_UINT8_DEC_WIDTH
+ * PRINTF_UINTMAX_DEC_WIDTH
+ * PRINTF_UINT64_DEC_WIDTH
+ * PRINTF_UINT32_DEC_WIDTH
+ * PRINTF_UINT16_DEC_WIDTH
+ * PRINTF_UINT8_DEC_WIDTH
+ *
+ * Which specifies the maximum number of characters required to
+ * print the number of that type in either hexadecimal or decimal.
+ * These are an extension beyond what C99 specifies must be in
+ * stdint.h.
+ *
+ * Compilers tested (all with 0 warnings at their highest respective
+ * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32
+ * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio
+ * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3
+ *
+ * This file should be considered a work in progress. Suggestions for
+ * improvements, especially those which increase coverage are strongly
+ * encouraged.
+ *
+ * Acknowledgements
+ *
+ * The following people have made significant contributions to the
+ * development and testing of this file:
+ *
+ * Chris Howie
+ * John Steele Scott
+ * Dave Thorup
+ * John Dill
+ * Florian Wobbe
+ * Christopher Sean Morrison
+ * Mikkel Fahnoe Jorgensen
+ *
+ */
+
+#include <stddef.h>
+#include <limits.h>
+#include <signal.h>
+
+/*
+ * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and
+ * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_.
+ */
+
+#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED)
+#include <stdint.h>
+#define _PSTDINT_H_INCLUDED
+# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__))
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "l"
+# endif
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+# else
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# ifndef PRINTF_INT32_MODIFIER
+# if (UINT_MAX == UINT32_MAX)
+# define PRINTF_INT32_MODIFIER ""
+# else
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+# endif
+# endif
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_UINT64_HEX_WIDTH
+# define PRINTF_UINT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_UINT32_HEX_WIDTH
+# define PRINTF_UINT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_UINT16_HEX_WIDTH
+# define PRINTF_UINT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_UINT8_HEX_WIDTH
+# define PRINTF_UINT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "19"
+# endif
+# ifndef PRINTF_UINT64_DEC_WIDTH
+# define PRINTF_UINT64_DEC_WIDTH "20"
+# endif
+# ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_UINT32_DEC_WIDTH
+# define PRINTF_UINT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_UINT16_DEC_WIDTH
+# define PRINTF_UINT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_UINT8_DEC_WIDTH
+# define PRINTF_UINT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_UINTMAX_HEX_WIDTH
+# define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH
+# endif
+# ifndef PRINTF_UINTMAX_DEC_WIDTH
+# define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH
+# endif
+
+/*
+ * Something really weird is going on with Open Watcom. Just pull some of
+ * these duplicated definitions from Open Watcom's stdint.h file for now.
+ */
+
+# if defined (__WATCOMC__) && __WATCOMC__ >= 1250
+# if !defined (INT64_C)
+# define INT64_C(x) (x + (INT64_MAX - INT64_MAX))
+# endif
+# if !defined (UINT64_C)
+# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
+# endif
+# if !defined (INT32_C)
+# define INT32_C(x) (x + (INT32_MAX - INT32_MAX))
+# endif
+# if !defined (UINT32_C)
+# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX))
+# endif
+# if !defined (INT16_C)
+# define INT16_C(x) (x)
+# endif
+# if !defined (UINT16_C)
+# define UINT16_C(x) (x)
+# endif
+# if !defined (INT8_C)
+# define INT8_C(x) (x)
+# endif
+# if !defined (UINT8_C)
+# define UINT8_C(x) (x)
+# endif
+# if !defined (UINT64_MAX)
+# define UINT64_MAX 18446744073709551615ULL
+# endif
+# if !defined (INT64_MAX)
+# define INT64_MAX 9223372036854775807LL
+# endif
+# if !defined (UINT32_MAX)
+# define UINT32_MAX 4294967295UL
+# endif
+# if !defined (INT32_MAX)
+# define INT32_MAX 2147483647L
+# endif
+# if !defined (INTMAX_MAX)
+# define INTMAX_MAX INT64_MAX
+# endif
+# if !defined (INTMAX_MIN)
+# define INTMAX_MIN INT64_MIN
+# endif
+# endif
+#endif
+
+/*
+ * I have no idea what is the truly correct thing to do on older Solaris.
+ * From some online discussions, this seems to be what is being
+ * recommended. For people who actually are developing on older Solaris,
+ * what I would like to know is, does this define all of the relevant
+ * macros of a complete stdint.h? Remember, in pstdint.h 64 bit is
+ * considered optional.
+ */
+
+#if (defined(__SUNPRO_C) && __SUNPRO_C >= 0x420) && !defined(_PSTDINT_H_INCLUDED)
+#include <sys/inttypes.h>
+#define _PSTDINT_H_INCLUDED
+#endif
+
+#ifndef _PSTDINT_H_INCLUDED
+#define _PSTDINT_H_INCLUDED
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~(size_t)0)
+#endif
+
+/*
+ * Deduce the type assignments from limits.h under the assumption that
+ * integer sizes in bits are powers of 2, and follow the ANSI
+ * definitions.
+ */
+
+#ifndef UINT8_MAX
+# define UINT8_MAX 0xff
+#endif
+#if !defined(uint8_t) && !defined(_UINT8_T) && !defined(vxWorks)
+# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S)
+ typedef unsigned char uint8_t;
+# define UINT8_C(v) ((uint8_t) v)
+# else
+# error "Platform not supported"
+# endif
+#endif
+
+#ifndef INT8_MAX
+# define INT8_MAX 0x7f
+#endif
+#ifndef INT8_MIN
+# define INT8_MIN INT8_C(0x80)
+#endif
+#if !defined(int8_t) && !defined(_INT8_T) && !defined(vxWorks)
+# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S)
+ typedef signed char int8_t;
+# define INT8_C(v) ((int8_t) v)
+# else
+# error "Platform not supported"
+# endif
+#endif
+
+#ifndef UINT16_MAX
+# define UINT16_MAX 0xffff
+#endif
+#if !defined(uint16_t) && !defined(_UINT16_T) && !defined(vxWorks)
+#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S)
+ typedef unsigned int uint16_t;
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER ""
+# endif
+# define UINT16_C(v) ((uint16_t) (v))
+#elif (USHRT_MAX == UINT16_MAX)
+ typedef unsigned short uint16_t;
+# define UINT16_C(v) ((uint16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT16_MAX
+# define INT16_MAX 0x7fff
+#endif
+#ifndef INT16_MIN
+# define INT16_MIN INT16_C(0x8000)
+#endif
+#if !defined(int16_t) && !defined(_INT16_T) && !defined(vxWorks)
+#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S)
+ typedef signed int int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT16_MAX)
+ typedef signed short int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef UINT32_MAX
+# define UINT32_MAX (0xffffffffUL)
+#endif
+#if !defined(uint32_t) && !defined(_UINT32_T) && !defined(vxWorks)
+#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S)
+ typedef unsigned long uint32_t;
+# define UINT32_C(v) v ## UL
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (UINT_MAX == UINT32_MAX)
+ typedef unsigned int uint32_t;
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+# define UINT32_C(v) v ## U
+#elif (USHRT_MAX == UINT32_MAX)
+ typedef unsigned short uint32_t;
+# define UINT32_C(v) ((unsigned short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT32_MAX
+# define INT32_MAX (0x7fffffffL)
+#endif
+#ifndef INT32_MIN
+# define INT32_MIN INT32_C(0x80000000)
+#endif
+#if !defined(int32_t) && !defined(_INT32_T) && !defined(vxWorks)
+#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S)
+ typedef signed long int32_t;
+# define INT32_C(v) v ## L
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (INT_MAX == INT32_MAX)
+ typedef signed int int32_t;
+# define INT32_C(v) v
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT32_MAX)
+ typedef signed short int32_t;
+# define INT32_C(v) ((short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+/*
+ * The macro stdint_int64_defined is temporarily used to record
+ * whether or not 64 integer support is available. It must be
+ * defined for any 64 integer extensions for new platforms that are
+ * added.
+ */
+
+#undef stdint_int64_defined
+#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S)
+# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S)
+# define stdint_int64_defined
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# endif
+#endif
+
+#if !defined (stdint_int64_defined)
+# if defined(__GNUC__) && !defined(vxWorks)
+# define stdint_int64_defined
+ __extension__ typedef long long int64_t;
+ __extension__ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S)
+# define stdint_int64_defined
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC)
+# define stdint_int64_defined
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+# define UINT64_C(v) v ## UI64
+# define INT64_C(v) v ## I64
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "I64"
+# endif
+# endif
+#endif
+
+#if !defined (LONG_LONG_MAX) && defined (INT64_C)
+# define LONG_LONG_MAX INT64_C (9223372036854775807)
+#endif
+#ifndef ULONG_LONG_MAX
+# define ULONG_LONG_MAX UINT64_C (18446744073709551615)
+#endif
+
+#if !defined (INT64_MAX) && defined (INT64_C)
+# define INT64_MAX INT64_C (9223372036854775807)
+#endif
+#if !defined (INT64_MIN) && defined (INT64_C)
+# define INT64_MIN INT64_C (-9223372036854775808)
+#endif
+#if !defined (UINT64_MAX) && defined (INT64_C)
+# define UINT64_MAX UINT64_C (18446744073709551615)
+#endif
+
+/*
+ * Width of hexadecimal for number field.
+ */
+
+#ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+#endif
+#ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+#endif
+#ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+#endif
+#ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+#endif
+#ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "19"
+#endif
+#ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+#endif
+#ifndef PRINTF_UINT64_DEC_WIDTH
+# define PRINTF_UINT64_DEC_WIDTH "20"
+#endif
+#ifndef PRINTF_UINT32_DEC_WIDTH
+# define PRINTF_UINT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_UINT16_DEC_WIDTH
+# define PRINTF_UINT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_UINT8_DEC_WIDTH
+# define PRINTF_UINT8_DEC_WIDTH "3"
+#endif
+
+/*
+ * Ok, lets not worry about 128 bit integers for now. Moore's law says
+ * we don't need to worry about that until about 2040 at which point
+ * we'll have bigger things to worry about.
+ */
+
+#ifdef stdint_int64_defined
+ typedef int64_t intmax_t;
+ typedef uint64_t uintmax_t;
+# define INTMAX_MAX INT64_MAX
+# define INTMAX_MIN INT64_MIN
+# define UINTMAX_MAX UINT64_MAX
+# define UINTMAX_C(v) UINT64_C(v)
+# define INTMAX_C(v) INT64_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
+# endif
+#else
+ typedef int32_t intmax_t;
+ typedef uint32_t uintmax_t;
+# define INTMAX_MAX INT32_MAX
+# define UINTMAX_MAX UINT32_MAX
+# define UINTMAX_C(v) UINT32_C(v)
+# define INTMAX_C(v) INT32_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH
+# endif
+#endif
+
+/*
+ * Because this file currently only supports platforms which have
+ * precise powers of 2 as bit sizes for the default integers, the
+ * least definitions are all trivial. Its possible that a future
+ * version of this file could have different definitions.
+ */
+
+#ifndef stdint_least_defined
+ typedef int8_t int_least8_t;
+ typedef uint8_t uint_least8_t;
+ typedef int16_t int_least16_t;
+ typedef uint16_t uint_least16_t;
+ typedef int32_t int_least32_t;
+ typedef uint32_t uint_least32_t;
+# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER
+# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER
+# define UINT_LEAST8_MAX UINT8_MAX
+# define INT_LEAST8_MAX INT8_MAX
+# define UINT_LEAST16_MAX UINT16_MAX
+# define INT_LEAST16_MAX INT16_MAX
+# define UINT_LEAST32_MAX UINT32_MAX
+# define INT_LEAST32_MAX INT32_MAX
+# define INT_LEAST8_MIN INT8_MIN
+# define INT_LEAST16_MIN INT16_MIN
+# define INT_LEAST32_MIN INT32_MIN
+# ifdef stdint_int64_defined
+ typedef int64_t int_least64_t;
+ typedef uint64_t uint_least64_t;
+# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER
+# define UINT_LEAST64_MAX UINT64_MAX
+# define INT_LEAST64_MAX INT64_MAX
+# define INT_LEAST64_MIN INT64_MIN
+# endif
+#endif
+#undef stdint_least_defined
+
+/*
+ * The ANSI C committee pretending to know or specify anything about
+ * performance is the epitome of misguided arrogance. The mandate of
+ * this file is to *ONLY* ever support that absolute minimum
+ * definition of the fast integer types, for compatibility purposes.
+ * No extensions, and no attempt to suggest what may or may not be a
+ * faster integer type will ever be made in this file. Developers are
+ * warned to stay away from these types when using this or any other
+ * stdint.h.
+ */
+
+typedef int_least8_t int_fast8_t;
+typedef uint_least8_t uint_fast8_t;
+typedef int_least16_t int_fast16_t;
+typedef uint_least16_t uint_fast16_t;
+typedef int_least32_t int_fast32_t;
+typedef uint_least32_t uint_fast32_t;
+#define UINT_FAST8_MAX UINT_LEAST8_MAX
+#define INT_FAST8_MAX INT_LEAST8_MAX
+#define UINT_FAST16_MAX UINT_LEAST16_MAX
+#define INT_FAST16_MAX INT_LEAST16_MAX
+#define UINT_FAST32_MAX UINT_LEAST32_MAX
+#define INT_FAST32_MAX INT_LEAST32_MAX
+#define INT_FAST8_MIN INT_LEAST8_MIN
+#define INT_FAST16_MIN INT_LEAST16_MIN
+#define INT_FAST32_MIN INT_LEAST32_MIN
+#ifdef stdint_int64_defined
+ typedef int_least64_t int_fast64_t;
+ typedef uint_least64_t uint_fast64_t;
+# define UINT_FAST64_MAX UINT_LEAST64_MAX
+# define INT_FAST64_MAX INT_LEAST64_MAX
+# define INT_FAST64_MIN INT_LEAST64_MIN
+#endif
+
+#undef stdint_int64_defined
+
+/*
+ * Whatever piecemeal, per compiler thing we can do about the wchar_t
+ * type limits.
+ */
+
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) && !defined(vxWorks)
+# include <wchar.h>
+# ifndef WCHAR_MIN
+# define WCHAR_MIN 0
+# endif
+# ifndef WCHAR_MAX
+# define WCHAR_MAX ((wchar_t)-1)
+# endif
+#endif
+
+/*
+ * Whatever piecemeal, per compiler/platform thing we can do about the
+ * (u)intptr_t types and limits.
+ */
+
+#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T)
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+#ifndef STDINT_H_UINTPTR_T_DEFINED
+# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__)
+# define stdint_intptr_bits 64
+# elif defined (__WATCOMC__) || defined (__TURBOC__)
+# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
+# define stdint_intptr_bits 16
+# else
+# define stdint_intptr_bits 32
+# endif
+# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__)
+# define stdint_intptr_bits 32
+# elif defined (__INTEL_COMPILER)
+/* TODO -- what did Intel do about x86-64? */
+# else
+/* #error "This platform might not be supported yet" */
+# endif
+
+# ifdef stdint_intptr_bits
+# define stdint_intptr_glue3_i(a,b,c) a##b##c
+# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c)
+# ifndef PRINTF_INTPTR_MODIFIER
+# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER)
+# endif
+# ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef PTRDIFF_MIN
+# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+# endif
+# ifndef UINTPTR_MAX
+# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef INTPTR_MAX
+# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef INTPTR_MIN
+# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+# endif
+# ifndef INTPTR_C
+# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x)
+# endif
+# ifndef UINTPTR_C
+# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x)
+# endif
+ typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t;
+ typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t;
+# else
+/* TODO -- This following is likely wrong for some platforms, and does
+ nothing for the definition of uintptr_t. */
+ typedef ptrdiff_t intptr_t;
+# endif
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+/*
+ * Assumes sig_atomic_t is signed and we have a 2s complement machine.
+ */
+
+#ifndef SIG_ATOMIC_MAX
+# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1)
+#endif
+
+#endif
+
+#if defined (__TEST_PSTDINT_FOR_CORRECTNESS)
+
+/*
+ * Please compile with the maximum warning settings to make sure macros are
+ * not defined more than once.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define glue3_aux(x,y,z) x ## y ## z
+#define glue3(x,y,z) glue3_aux(x,y,z)
+
+#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0);
+#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0);
+
+#define DECL(us,bits) glue3(DECL,us,) (bits)
+
+#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits)
+
+#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; }
+
+int main () {
+ int err_n = 0;
+ int err_first = 0;
+ DECL(I,8)
+ DECL(U,8)
+ DECL(I,16)
+ DECL(U,16)
+ DECL(I,32)
+ DECL(U,32)
+#ifdef INT64_MAX
+ DECL(I,64)
+ DECL(U,64)
+#endif
+ intmax_t imax = INTMAX_C(0);
+ uintmax_t umax = UINTMAX_C(0);
+ char str0[256], str1[256];
+
+ sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647));
+ if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0));
+ if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH));
+ sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295));
+ if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0));
+ if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH));
+#ifdef INT64_MAX
+ sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807));
+ if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1));
+ if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1)));
+ sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591));
+ if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1));
+ if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1)));
+#endif
+
+ sprintf (str0, "%d %x\n", 0, ~0);
+
+ sprintf (str1, "%d %x\n", i8, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1));
+ sprintf (str1, "%u %x\n", u8, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1));
+ sprintf (str1, "%d %x\n", i16, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1));
+ sprintf (str1, "%u %x\n", u16, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1));
+ sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1));
+ sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1));
+#ifdef INT64_MAX
+ sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1));
+#endif
+ sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1));
+ sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1));
+
+ TESTUMAX(8);
+ TESTUMAX(16);
+ TESTUMAX(32);
+#ifdef INT64_MAX
+ TESTUMAX(64);
+#endif
+
+#define STR(v) #v
+#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v));
+ if (err_n) {
+ printf ("pstdint.h is not correct. Please use sizes below to correct it:\n");
+ }
+
+ Q(int)
+ Q(unsigned)
+ Q(long int)
+ Q(short int)
+ Q(int8_t)
+ Q(int16_t)
+ Q(int32_t)
+#ifdef INT64_MAX
+ Q(int64_t)
+#endif
+
+ return EXIT_SUCCESS;
+}
+
+#endif
diff --git a/thirdparty/assimp/include/assimp/Compiler/pushpack1.h b/thirdparty/assimp/include/assimp/Compiler/pushpack1.h
new file mode 100644
index 0000000000..4c9fbb857f
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Compiler/pushpack1.h
@@ -0,0 +1,43 @@
+
+
+// ===============================================================================
+// May be included multiple times - sets structure packing to 1
+// for all supported compilers. #include <poppack1.h> reverts the changes.
+//
+// Currently this works on the following compilers:
+// MSVC 7,8,9
+// GCC
+// BORLAND (complains about 'pack state changed but not reverted', but works)
+// Clang
+//
+//
+// USAGE:
+//
+// struct StructToBePacked {
+// } PACK_STRUCT;
+//
+// ===============================================================================
+
+#ifdef AI_PUSHPACK_IS_DEFINED
+# error poppack1.h must be included after pushpack1.h
+#endif
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+# pragma pack(push,1)
+# define PACK_STRUCT
+#elif defined( __GNUC__ ) || defined(__clang__)
+# if !defined(HOST_MINGW)
+# define PACK_STRUCT __attribute__((__packed__))
+# else
+# define PACK_STRUCT __attribute__((gcc_struct, __packed__))
+# endif
+#else
+# error Compiler not supported
+#endif
+
+#if defined(_MSC_VER)
+// C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop
+# pragma warning (disable : 4103)
+#endif
+
+#define AI_PUSHPACK_IS_DEFINED
diff --git a/thirdparty/assimp/include/assimp/CreateAnimMesh.h b/thirdparty/assimp/include/assimp/CreateAnimMesh.h
new file mode 100644
index 0000000000..a60173588f
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/CreateAnimMesh.h
@@ -0,0 +1,58 @@
+/*
+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 CreateAnimMesh.h
+ * Create AnimMesh from Mesh
+ */
+#ifndef INCLUDED_AI_CREATE_ANIM_MESH_H
+#define INCLUDED_AI_CREATE_ANIM_MESH_H
+
+#include <assimp/mesh.h>
+
+namespace Assimp {
+
+/** Create aiAnimMesh from aiMesh. */
+ASSIMP_API aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh);
+
+} // end of namespace Assimp
+#endif // INCLUDED_AI_CREATE_ANIM_MESH_H
+
diff --git a/thirdparty/assimp/include/assimp/DefaultIOStream.h b/thirdparty/assimp/include/assimp/DefaultIOStream.h
new file mode 100644
index 0000000000..994d728ff5
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/DefaultIOStream.h
@@ -0,0 +1,140 @@
+/*
+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 file I/O using fXXX()-family of functions */
+#ifndef AI_DEFAULTIOSTREAM_H_INC
+#define AI_DEFAULTIOSTREAM_H_INC
+
+#include <stdio.h>
+#include <assimp/IOStream.hpp>
+#include <assimp/importerdesc.h>
+#include <assimp/Defines.h>
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+//! @class DefaultIOStream
+//! @brief Default IO implementation, use standard IO operations
+//! @note An instance of this class can exist without a valid file handle
+//! attached to it. All calls fail, but the instance can nevertheless be
+//! used with no restrictions.
+class ASSIMP_API DefaultIOStream : public IOStream
+{
+ friend class DefaultIOSystem;
+#if __ANDROID__
+# if __ANDROID_API__ > 9
+# if defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
+ friend class AndroidJNIIOSystem;
+# endif // defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
+# endif // __ANDROID_API__ > 9
+#endif // __ANDROID__
+
+protected:
+ DefaultIOStream() AI_NO_EXCEPT;
+ DefaultIOStream(FILE* pFile, const std::string &strFilename);
+
+public:
+ /** Destructor public to allow simple deletion to close the file. */
+ ~DefaultIOStream ();
+
+ // -------------------------------------------------------------------
+ /// Read from stream
+ size_t Read(void* pvBuffer,
+ size_t pSize,
+ size_t pCount);
+
+
+ // -------------------------------------------------------------------
+ /// Write to stream
+ size_t Write(const void* pvBuffer,
+ size_t pSize,
+ size_t pCount);
+
+ // -------------------------------------------------------------------
+ /// Seek specific position
+ aiReturn Seek(size_t pOffset,
+ aiOrigin pOrigin);
+
+ // -------------------------------------------------------------------
+ /// Get current seek position
+ size_t Tell() const;
+
+ // -------------------------------------------------------------------
+ /// Get size of file
+ size_t FileSize() const;
+
+ // -------------------------------------------------------------------
+ /// Flush file contents
+ void Flush();
+
+private:
+ // File data-structure, using clib
+ FILE* mFile;
+ // Filename
+ std::string mFilename;
+ // Cached file size
+ mutable size_t mCachedSize;
+};
+
+// ----------------------------------------------------------------------------------
+inline
+DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT
+: mFile(nullptr)
+, mFilename("")
+, mCachedSize(SIZE_MAX) {
+ // empty
+}
+
+// ----------------------------------------------------------------------------------
+inline
+DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename)
+: mFile(pFile)
+, mFilename(strFilename)
+, mCachedSize(SIZE_MAX) {
+ // empty
+}
+// ----------------------------------------------------------------------------------
+
+} // ns assimp
+
+#endif //!!AI_DEFAULTIOSTREAM_H_INC
+
diff --git a/thirdparty/assimp/include/assimp/DefaultIOSystem.h b/thirdparty/assimp/include/assimp/DefaultIOSystem.h
new file mode 100644
index 0000000000..2dd5c801b5
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/DefaultIOSystem.h
@@ -0,0 +1,93 @@
+/*
+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 */
+#ifndef AI_DEFAULTIOSYSTEM_H_INC
+#define AI_DEFAULTIOSYSTEM_H_INC
+
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Default implementation of IOSystem using the standard C file functions */
+class ASSIMP_API DefaultIOSystem : public IOSystem {
+public:
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists( const char* pFile) const;
+
+ // -------------------------------------------------------------------
+ /** Returns the directory separator. */
+ char getOsSeparator() const;
+
+ // -------------------------------------------------------------------
+ /** Open a new file with a given path. */
+ IOStream* Open( const char* pFile, const char* pMode = "rb");
+
+ // -------------------------------------------------------------------
+ /** Closes the given file and releases all resources associated with it. */
+ void Close( IOStream* pFile);
+
+ // -------------------------------------------------------------------
+ /** Compare two paths */
+ bool ComparePaths (const char* one, const char* second) const;
+
+ /** @brief get the file name of a full filepath
+ * example: /tmp/archive.tar.gz -> archive.tar.gz
+ */
+ static std::string fileName( const std::string &path );
+
+ /** @brief get the complete base name of a full filepath
+ * example: /tmp/archive.tar.gz -> archive.tar
+ */
+ static std::string completeBaseName( const std::string &path);
+
+ /** @brief get the path of a full filepath
+ * example: /tmp/archive.tar.gz -> /tmp/
+ */
+ static std::string absolutePath( const std::string &path);
+};
+
+} //!ns Assimp
+
+#endif //AI_DEFAULTIOSYSTEM_H_INC
diff --git a/thirdparty/assimp/include/assimp/DefaultLogger.hpp b/thirdparty/assimp/include/assimp/DefaultLogger.hpp
new file mode 100644
index 0000000000..1946e250a6
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/DefaultLogger.hpp
@@ -0,0 +1,188 @@
+/*
+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.hpp
+*/
+
+#ifndef INCLUDED_AI_DEFAULTLOGGER
+#define INCLUDED_AI_DEFAULTLOGGER
+
+#include "Logger.hpp"
+#include "LogStream.hpp"
+#include "NullLogger.hpp"
+#include <vector>
+
+namespace Assimp {
+// ------------------------------------------------------------------------------------
+class IOStream;
+struct LogStreamInfo;
+
+/** default name of logfile */
+#define ASSIMP_DEFAULT_LOG_NAME "AssimpLog.txt"
+
+// ------------------------------------------------------------------------------------
+/** @brief CPP-API: Primary logging facility of Assimp.
+ *
+ * The library stores its primary #Logger as a static member of this class.
+ * #get() returns this primary logger. By default the underlying implementation is
+ * just a #NullLogger which rejects all log messages. By calling #create(), logging
+ * is turned on. To capture the log output multiple log streams (#LogStream) can be
+ * attach to the logger. Some default streams for common streaming locations (such as
+ * a file, std::cout, OutputDebugString()) are also provided.
+ *
+ * If you wish to customize the logging at an even deeper level supply your own
+ * implementation of #Logger to #set().
+ * @note The whole logging stuff causes a small extra overhead for all imports. */
+class ASSIMP_API DefaultLogger :
+ public Logger {
+
+public:
+
+ // ----------------------------------------------------------------------
+ /** @brief Creates a logging instance.
+ * @param name Name for log file. Only valid in combination
+ * with the aiDefaultLogStream_FILE flag.
+ * @param severity Log severity, VERBOSE turns on debug messages
+ * @param defStreams Default log streams to be attached. Any bitwise
+ * combination of the aiDefaultLogStream enumerated values.
+ * If #aiDefaultLogStream_FILE is specified but an empty string is
+ * passed for 'name', no log file is created at all.
+ * @param io IOSystem to be used to open external files (such as the
+ * log file). Pass NULL to rely on the default implementation.
+ * This replaces the default #NullLogger with a #DefaultLogger instance. */
+ static Logger *create(const char* name = ASSIMP_DEFAULT_LOG_NAME,
+ LogSeverity severity = NORMAL,
+ unsigned int defStreams = aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE,
+ IOSystem* io = NULL);
+
+ // ----------------------------------------------------------------------
+ /** @brief Setup a custom #Logger implementation.
+ *
+ * Use this if the provided #DefaultLogger class doesn't fit into
+ * your needs. If the provided message formatting is OK for you,
+ * it's much easier to use #create() and to attach your own custom
+ * output streams to it.
+ * @param logger Pass NULL to setup a default NullLogger*/
+ static void set (Logger *logger);
+
+ // ----------------------------------------------------------------------
+ /** @brief Getter for singleton instance
+ * @return Only instance. This is never null, but it could be a
+ * NullLogger. Use isNullLogger to check this.*/
+ static Logger *get();
+
+ // ----------------------------------------------------------------------
+ /** @brief Return whether a #NullLogger is currently active
+ * @return true if the current logger is a #NullLogger.
+ * Use create() or set() to setup a logger that does actually do
+ * something else than just rejecting all log messages. */
+ static bool isNullLogger();
+
+ // ----------------------------------------------------------------------
+ /** @brief Kills the current singleton logger and replaces it with a
+ * #NullLogger instance. */
+ static void kill();
+
+ // ----------------------------------------------------------------------
+ /** @copydoc Logger::attachStream */
+ bool attachStream(LogStream *pStream,
+ unsigned int severity);
+
+ // ----------------------------------------------------------------------
+ /** @copydoc Logger::detatchStream */
+ bool detatchStream(LogStream *pStream,
+ unsigned int severity);
+
+private:
+ // ----------------------------------------------------------------------
+ /** @briefPrivate construction for internal use by create().
+ * @param severity Logging granularity */
+ explicit DefaultLogger(LogSeverity severity);
+
+ // ----------------------------------------------------------------------
+ /** @briefDestructor */
+ ~DefaultLogger();
+
+ /** @brief Logs debug infos, only been written when severity level VERBOSE is set */
+ void OnDebug(const char* message);
+
+ /** @brief Logs an info message */
+ void OnInfo(const char* message);
+
+ /** @brief Logs a warning message */
+ void OnWarn(const char* message);
+
+ /** @brief Logs an error message */
+ void OnError(const char* message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes a message to all streams */
+ void WriteToStreams(const char* message, ErrorSeverity ErrorSev );
+
+ // ----------------------------------------------------------------------
+ /** @brief Returns the thread id.
+ * @note This is an OS specific feature, if not supported, a
+ * zero will be returned.
+ */
+ unsigned int GetThreadID();
+
+private:
+ // Aliases for stream container
+ typedef std::vector<LogStreamInfo*> StreamArray;
+ typedef std::vector<LogStreamInfo*>::iterator StreamIt;
+ typedef std::vector<LogStreamInfo*>::const_iterator ConstStreamIt;
+
+ //! only logging instance
+ static Logger *m_pLogger;
+ static NullLogger s_pNullLogger;
+
+ //! Attached streams
+ StreamArray m_StreamArray;
+
+ bool noRepeatMsg;
+ char lastMsg[MAX_LOG_MESSAGE_LENGTH*2];
+ size_t lastLen;
+};
+// ------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! INCLUDED_AI_DEFAULTLOGGER
diff --git a/thirdparty/assimp/include/assimp/Defines.h b/thirdparty/assimp/include/assimp/Defines.h
new file mode 100644
index 0000000000..15e1d83c26
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Defines.h
@@ -0,0 +1,49 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+// We need those constants, workaround for any platforms where nobody defined them yet
+#if (!defined SIZE_MAX)
+# define SIZE_MAX (~((size_t)0))
+#endif
+
+#if (!defined UINT_MAX)
+# define UINT_MAX (~((unsigned int)0))
+#endif
+
diff --git a/thirdparty/assimp/include/assimp/Exceptional.h b/thirdparty/assimp/include/assimp/Exceptional.h
new file mode 100644
index 0000000000..5109b8f077
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Exceptional.h
@@ -0,0 +1,125 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef INCLUDED_EXCEPTIONAL_H
+#define INCLUDED_EXCEPTIONAL_H
+
+#include <stdexcept>
+#include <assimp/DefaultIOStream.h>
+using std::runtime_error;
+
+#ifdef _MSC_VER
+# pragma warning(disable : 4275)
+#endif
+
+// ---------------------------------------------------------------------------
+/** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an
+ * unrecoverable error occurs while importing. Loading APIs return
+ * NULL instead of a valid aiScene then. */
+class DeadlyImportError
+ : public runtime_error
+{
+public:
+ /** Constructor with arguments */
+ explicit DeadlyImportError( const std::string& errorText)
+ : runtime_error(errorText)
+ {
+ }
+
+private:
+};
+
+typedef DeadlyImportError DeadlyExportError;
+
+#ifdef _MSC_VER
+# pragma warning(default : 4275)
+#endif
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ExceptionSwallower {
+ T operator ()() const {
+ return T();
+ }
+};
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ExceptionSwallower<T*> {
+ T* operator ()() const {
+ return NULL;
+ }
+};
+
+// ---------------------------------------------------------------------------
+template <>
+struct ExceptionSwallower<aiReturn> {
+ aiReturn operator ()() const {
+ try {
+ throw;
+ }
+ catch (std::bad_alloc&) {
+ return aiReturn_OUTOFMEMORY;
+ }
+ catch (...) {
+ return aiReturn_FAILURE;
+ }
+ }
+};
+
+// ---------------------------------------------------------------------------
+template <>
+struct ExceptionSwallower<void> {
+ void operator ()() const {
+ return;
+ }
+};
+
+#define ASSIMP_BEGIN_EXCEPTION_REGION()\
+{\
+ try {
+
+#define ASSIMP_END_EXCEPTION_REGION(type)\
+ } catch(...) {\
+ return ExceptionSwallower<type>()();\
+ }\
+}
+
+#endif // INCLUDED_EXCEPTIONAL_H
diff --git a/thirdparty/assimp/include/assimp/Exporter.hpp b/thirdparty/assimp/include/assimp/Exporter.hpp
new file mode 100644
index 0000000000..bf0096e7e9
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Exporter.hpp
@@ -0,0 +1,505 @@
+/*
+---------------------------------------------------------------------------
+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.hpp
+* @brief Defines the CPP-API for the Assimp export interface
+*/
+#pragma once
+#ifndef AI_EXPORT_HPP_INC
+#define AI_EXPORT_HPP_INC
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
+#include "cexport.h"
+#include <map>
+
+namespace Assimp {
+
+class ExporterPimpl;
+class IOSystem;
+class ProgressHandler;
+
+// ----------------------------------------------------------------------------------
+/** CPP-API: The Exporter class forms an C++ interface to the export functionality
+ * of the Open Asset Import Library. Note that the export interface is available
+ * only if Assimp has been built with ASSIMP_BUILD_NO_EXPORT not defined.
+ *
+ * The interface is modeled after the importer interface and mostly
+ * symmetric. The same rules for threading etc. apply.
+ *
+ * In a nutshell, there are two export interfaces: #Export, which writes the
+ * output file(s) either to the regular file system or to a user-supplied
+ * #IOSystem, and #ExportToBlob which returns a linked list of memory
+ * buffers (blob), each referring to one output file (in most cases
+ * there will be only one output file of course, but this extra complexity is
+ * needed since Assimp aims at supporting a wide range of file formats).
+ *
+ * #ExportToBlob is especially useful if you intend to work
+ * with the data in-memory.
+*/
+class ASSIMP_API ExportProperties;
+
+class ASSIMP_API Exporter {
+public:
+ /** Function pointer type of a Export worker function */
+ typedef void (*fpExportFunc)(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+
+ /** Internal description of an Assimp export format option */
+ struct ExportFormatEntry {
+ /// Public description structure to be returned by aiGetExportFormatDescription()
+ aiExportFormatDesc mDescription;
+
+ // Worker function to do the actual exporting
+ fpExportFunc mExportFunction;
+
+ // Post-processing steps to be executed PRIOR to invoking mExportFunction
+ unsigned int mEnforcePP;
+
+ // Constructor to fill all entries
+ ExportFormatEntry( const char* pId, const char* pDesc, const char* pExtension, fpExportFunc pFunction, unsigned int pEnforcePP = 0u)
+ {
+ mDescription.id = pId;
+ mDescription.description = pDesc;
+ mDescription.fileExtension = pExtension;
+ mExportFunction = pFunction;
+ mEnforcePP = pEnforcePP;
+ }
+
+ ExportFormatEntry() :
+ mExportFunction()
+ , mEnforcePP()
+ {
+ mDescription.id = NULL;
+ mDescription.description = NULL;
+ mDescription.fileExtension = NULL;
+ }
+ };
+
+ /**
+ * @brief The class constructor.
+ */
+ Exporter();
+
+ /**
+ * @brief The class destructor.
+ */
+ ~Exporter();
+
+ // -------------------------------------------------------------------
+ /** Supplies a custom IO handler to the exporter to use to open and
+ * access files.
+ *
+ * If you need #Export to use custom IO logic to access the files,
+ * you need to supply a custom implementation of IOSystem and
+ * IOFile to the exporter.
+ *
+ * #Exporter takes ownership of the object and will destroy it
+ * afterwards. The previously assigned handler will be deleted.
+ * Pass NULL to take again ownership of your IOSystem and reset Assimp
+ * to use its default implementation, which uses plain file IO.
+ *
+ * @param pIOHandler The IO handler to be used in all file accesses
+ * of the Importer. */
+ void SetIOHandler( IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Retrieves the IO handler that is currently set.
+ * You can use #IsDefaultIOHandler() to check whether the returned
+ * interface is the default IO handler provided by ASSIMP. The default
+ * handler is active as long the application doesn't supply its own
+ * custom IO handler via #SetIOHandler().
+ * @return A valid IOSystem interface, never NULL. */
+ IOSystem* GetIOHandler() const;
+
+ // -------------------------------------------------------------------
+ /** Checks whether a default IO handler is active
+ * A default handler is active as long the application doesn't
+ * supply its own custom IO handler via #SetIOHandler().
+ * @return true by default */
+ bool IsDefaultIOHandler() const;
+
+ // -------------------------------------------------------------------
+ /** Supplies a custom progress handler to the exporter. This
+ * interface exposes an #Update() callback, which is called
+ * more or less periodically (please don't sue us if it
+ * isn't as periodically as you'd like it to have ...).
+ * This can be used to implement progress bars and loading
+ * timeouts.
+ * @param pHandler Progress callback interface. Pass nullptr to
+ * disable progress reporting.
+ * @note Progress handlers can be used to abort the loading
+ * at almost any time.*/
+ void SetProgressHandler(ProgressHandler* pHandler);
+
+ // -------------------------------------------------------------------
+ /** Exports the given scene to a chosen file format. Returns the exported
+ * data as a binary blob which you can write into a file or something.
+ * When you're done with the data, simply let the #Exporter instance go
+ * out of scope to have it released automatically.
+ * @param pScene The scene to export. Stays in possession of the caller,
+ * is not changed by the function.
+ * @param pFormatId ID string to specify to which format you want to
+ * export to. Use
+ * #GetExportFormatCount / #GetExportFormatDescription to learn which
+ * export formats are available.
+ * @param pPreprocessing See the documentation for #Export
+ * @return the exported data or NULL in case of error.
+ * @note If the Exporter instance did already hold a blob from
+ * a previous call to #ExportToBlob, it will be disposed.
+ * Any IO handlers set via #SetIOHandler are ignored here.
+ * @note Use aiCopyScene() to get a modifiable copy of a previously
+ * imported scene. */
+ const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId,
+ unsigned int pPreprocessing = 0u, const ExportProperties* = nullptr);
+ const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId,
+ unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
+
+ // -------------------------------------------------------------------
+ /** Convenience function to export directly to a file. Use
+ * #SetIOSystem to supply a custom IOSystem to gain fine-grained control
+ * about the output data flow of the export process.
+ * @param pBlob A data blob obtained from a previous call to #aiExportScene. Must not be NULL.
+ * @param pPath Full target file name. Target must be accessible.
+ * @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated
+ * flags, but in reality only a subset of them makes sense here. Specifying
+ * 'preprocessing' flags is useful if the input scene does not conform to
+ * Assimp's default conventions as specified in the @link data Data Structures Page @endlink.
+ * In short, this means the geometry data should use a right-handed coordinate systems, face
+ * winding should be counter-clockwise and the UV coordinate origin is assumed to be in
+ * the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and
+ * #aiProcess_FlipWindingOrder flags are used in the import side to allow users
+ * to have those defaults automatically adapted to their conventions. Specifying those flags
+ * for exporting has the opposite effect, respectively. Some other of the
+ * #aiPostProcessSteps enumerated values may be useful as well, but you'll need
+ * to try out what their effect on the exported file is. Many formats impose
+ * their own restrictions on the structure of the geometry stored therein,
+ * so some preprocessing may have little or no effect at all, or may be
+ * redundant as exporters would apply them anyhow. A good example
+ * is triangulation - whilst you can enforce it by specifying
+ * the #aiProcess_Triangulate flag, most export formats support only
+ * triangulate data so they would run the step even if it wasn't requested.
+ *
+ * If assimp detects that the input scene was directly taken from the importer side of
+ * the library (i.e. not copied using aiCopyScene and potentially modified afterwards),
+ * any post-processing steps already applied to the scene will not be applied again, unless
+ * they show non-idempotent behavior (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and
+ * #aiProcess_FlipWindingOrder).
+ * @return AI_SUCCESS if everything was fine.
+ * @note Use aiCopyScene() to get a modifiable copy of a previously
+ * imported scene.*/
+ aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
+ unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
+ aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath,
+ unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr);
+
+ // -------------------------------------------------------------------
+ /** Returns an error description of an error that occurred in #Export
+ * or #ExportToBlob
+ *
+ * Returns an empty string if no error occurred.
+ * @return A description of the last error, an empty string if no
+ * error occurred. The string is never NULL.
+ *
+ * @note The returned function remains valid until one of the
+ * following methods is called: #Export, #ExportToBlob, #FreeBlob */
+ const char* GetErrorString() const;
+
+ // -------------------------------------------------------------------
+ /** Return the blob obtained from the last call to #ExportToBlob */
+ const aiExportDataBlob* GetBlob() const;
+
+ // -------------------------------------------------------------------
+ /** Orphan the blob from the last call to #ExportToBlob. This means
+ * the caller takes ownership and is thus responsible for calling
+ * the C API function #aiReleaseExportBlob to release it. */
+ const aiExportDataBlob* GetOrphanedBlob() const;
+
+ // -------------------------------------------------------------------
+ /** Frees the current blob.
+ *
+ * The function does nothing if no blob has previously been
+ * previously produced via #ExportToBlob. #FreeBlob is called
+ * automatically by the destructor. The only reason to call
+ * it manually would be to reclaim as much storage as possible
+ * without giving up the #Exporter instance yet. */
+ void FreeBlob( );
+
+ // -------------------------------------------------------------------
+ /** Returns the number of export file formats available in the current
+ * Assimp build. Use #Exporter::GetExportFormatDescription to
+ * retrieve infos of a specific export format.
+ *
+ * This includes built-in exporters as well as exporters registered
+ * using #RegisterExporter.
+ **/
+ size_t GetExportFormatCount() const;
+
+ // -------------------------------------------------------------------
+ /** Returns a description of the nth export file format. Use #
+ * #Exporter::GetExportFormatCount to learn how many export
+ * formats are supported.
+ *
+ * The returned pointer is of static storage duration if the
+ * pIndex pertains to a built-in exporter (i.e. one not registered
+ * via #RegistrerExporter). It is restricted to the life-time of the
+ * #Exporter instance otherwise.
+ *
+ * @param pIndex Index of the export format to retrieve information
+ * for. Valid range is 0 to #Exporter::GetExportFormatCount
+ * @return A description of that specific export format.
+ * NULL if pIndex is out of range. */
+ const aiExportFormatDesc* GetExportFormatDescription( size_t pIndex ) const;
+
+ // -------------------------------------------------------------------
+ /** Register a custom exporter. Custom export formats are limited to
+ * to the current #Exporter instance and do not affect the
+ * library globally. The indexes under which the format's
+ * export format description can be queried are assigned
+ * monotonously.
+ * @param desc Exporter description.
+ * @return aiReturn_SUCCESS if the export format was successfully
+ * registered. A common cause that would prevent an exporter
+ * from being registered is that its format id is already
+ * occupied by another format. */
+ aiReturn RegisterExporter(const ExportFormatEntry& desc);
+
+ // -------------------------------------------------------------------
+ /** Remove an export format previously registered with #RegisterExporter
+ * from the #Exporter instance (this can also be used to drop
+ * built-in exporters because those are implicitly registered
+ * using #RegisterExporter).
+ * @param id Format id to be unregistered, this refers to the
+ * 'id' field of #aiExportFormatDesc.
+ * @note Calling this method on a format description not yet registered
+ * has no effect.*/
+ void UnregisterExporter(const char* id);
+
+protected:
+ // Just because we don't want you to know how we're hacking around.
+ ExporterPimpl* pimpl;
+};
+
+class ASSIMP_API ExportProperties {
+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;
+
+public:
+ /** Standard constructor
+ * @see ExportProperties()
+ */
+ ExportProperties();
+
+ // -------------------------------------------------------------------
+ /** Copy constructor.
+ *
+ * This copies the configuration properties of another ExportProperties.
+ * @see ExportProperties(const ExportProperties& other)
+ */
+ ExportProperties(const ExportProperties& other);
+
+ // -------------------------------------------------------------------
+ /** Set an integer configuration property.
+ * @param szName Name of the property. All supported properties
+ * are defined in the aiConfig.g header (all constants share the
+ * prefix AI_CONFIG_XXX and are simple strings).
+ * @param iValue New value of the property
+ * @return true if the property was set before. The new value replaces
+ * the previous value in this case.
+ * @note Property of different types (float, int, string ..) are kept
+ * on different stacks, so calling SetPropertyInteger() for a
+ * floating-point property has no effect - the loader will call
+ * GetPropertyFloat() to read the property, but it won't be there.
+ */
+ bool SetPropertyInteger(const char* szName, int iValue);
+
+ // -------------------------------------------------------------------
+ /** Set a boolean configuration property. Boolean properties
+ * are stored on the integer stack internally so it's possible
+ * to set them via #SetPropertyBool and query them with
+ * #GetPropertyBool and vice versa.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyBool(const char* szName, bool value) {
+ return SetPropertyInteger(szName,value);
+ }
+
+ // -------------------------------------------------------------------
+ /** Set a floating-point configuration property.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyFloat(const char* szName, ai_real fValue);
+
+ // -------------------------------------------------------------------
+ /** Set a string configuration property.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyString(const char* szName, const std::string& sValue);
+
+ // -------------------------------------------------------------------
+ /** Set a matrix configuration property.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue);
+
+ // -------------------------------------------------------------------
+ /** Get a configuration property.
+ * @param szName Name of the property. All supported properties
+ * are defined in the aiConfig.g header (all constants share the
+ * prefix AI_CONFIG_XXX).
+ * @param iErrorReturn Value that is returned if the property
+ * is not found.
+ * @return Current value of the property
+ * @note Property of different types (float, int, string ..) are kept
+ * on different lists, so calling SetPropertyInteger() for a
+ * floating-point property has no effect - the loader will call
+ * GetPropertyFloat() to read the property, but it won't be there.
+ */
+ int GetPropertyInteger(const char* szName,
+ int iErrorReturn = 0xffffffff) const;
+
+ // -------------------------------------------------------------------
+ /** Get a boolean configuration property. Boolean properties
+ * are stored on the integer stack internally so it's possible
+ * to set them via #SetPropertyBool and query them with
+ * #GetPropertyBool and vice versa.
+ * @see GetPropertyInteger()
+ */
+ bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const {
+ return GetPropertyInteger(szName,bErrorReturn)!=0;
+ }
+
+ // -------------------------------------------------------------------
+ /** Get a floating-point configuration property
+ * @see GetPropertyInteger()
+ */
+ ai_real GetPropertyFloat(const char* szName,
+ ai_real fErrorReturn = 10e10f) const;
+
+ // -------------------------------------------------------------------
+ /** Get a string configuration property
+ *
+ * The return value remains valid until the property is modified.
+ * @see GetPropertyInteger()
+ */
+ const std::string GetPropertyString(const char* szName,
+ const std::string& sErrorReturn = "") const;
+
+ // -------------------------------------------------------------------
+ /** Get a matrix configuration property
+ *
+ * The return value remains valid until the property is modified.
+ * @see GetPropertyInteger()
+ */
+ const aiMatrix4x4 GetPropertyMatrix(const char* szName,
+ const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const;
+
+ // -------------------------------------------------------------------
+ /** Determine a integer configuration property has been set.
+ * @see HasPropertyInteger()
+ */
+ bool HasPropertyInteger(const char* szName) const;
+
+ /** Determine a boolean configuration property has been set.
+ * @see HasPropertyBool()
+ */
+ bool HasPropertyBool(const char* szName) const;
+
+ /** Determine a boolean configuration property has been set.
+ * @see HasPropertyFloat()
+ */
+ bool HasPropertyFloat(const char* szName) const;
+
+ /** Determine a String configuration property has been set.
+ * @see HasPropertyString()
+ */
+ bool HasPropertyString(const char* szName) const;
+
+ /** Determine a Matrix configuration property has been set.
+ * @see HasPropertyMatrix()
+ */
+ bool HasPropertyMatrix(const char* szName) const;
+
+protected:
+
+ /** 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;
+};
+
+// ----------------------------------------------------------------------------------
+inline
+const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const std::string& pFormatId,
+ unsigned int pPreprocessing, const ExportProperties* pProperties)
+{
+ return ExportToBlob(pScene,pFormatId.c_str(),pPreprocessing, pProperties);
+}
+
+// ----------------------------------------------------------------------------------
+inline
+aiReturn Exporter :: Export( const aiScene* pScene, const std::string& pFormatId,
+ const std::string& pPath, unsigned int pPreprocessing,
+ const ExportProperties* pProperties)
+{
+ return Export(pScene,pFormatId.c_str(),pPath.c_str(),pPreprocessing, pProperties);
+}
+
+} // namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_EXPORT
+#endif // AI_EXPORT_HPP_INC
diff --git a/thirdparty/assimp/include/assimp/GenericProperty.h b/thirdparty/assimp/include/assimp/GenericProperty.h
new file mode 100644
index 0000000000..183ecd5197
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/GenericProperty.h
@@ -0,0 +1,133 @@
+/*
+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 AI_GENERIC_PROPERTY_H_INCLUDED
+#define AI_GENERIC_PROPERTY_H_INCLUDED
+
+#include <assimp/Importer.hpp>
+#include <assimp/ai_assert.h>
+#include "Hash.h"
+
+#include <map>
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline
+bool SetGenericProperty(std::map< unsigned int, T >& list,
+ const char* szName, const T& value) {
+ ai_assert(nullptr != szName);
+ const uint32_t hash = SuperFastHash(szName);
+
+ typename std::map<unsigned int, T>::iterator it = list.find(hash);
+ if (it == list.end()) {
+ list.insert(std::pair<unsigned int, T>( hash, value ));
+ return false;
+ }
+ (*it).second = value;
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline
+const T& GetGenericProperty(const std::map< unsigned int, T >& list,
+ const char* szName, const T& errorReturn) {
+ ai_assert(nullptr != szName);
+ const uint32_t hash = SuperFastHash(szName);
+
+ typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
+ if (it == list.end()) {
+ return errorReturn;
+ }
+
+ return (*it).second;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Special version for pointer types - they will be deleted when replaced with another value
+// passing NULL removes the whole property
+template <class T>
+inline
+void SetGenericPropertyPtr(std::map< unsigned int, T* >& list,
+ const char* szName, T* value, bool* bWasExisting = nullptr ) {
+ ai_assert(nullptr != szName);
+ const uint32_t hash = SuperFastHash(szName);
+
+ typename std::map<unsigned int, T*>::iterator it = list.find(hash);
+ if (it == list.end()) {
+ if (bWasExisting) {
+ *bWasExisting = false;
+ }
+
+ list.insert(std::pair<unsigned int,T*>( hash, value ));
+ return;
+ }
+ if ((*it).second != value) {
+ delete (*it).second;
+ (*it).second = value;
+ }
+ if (!value) {
+ list.erase(it);
+ }
+ if (bWasExisting) {
+ *bWasExisting = true;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline
+bool HasGenericProperty(const std::map< unsigned int, T >& list,
+ const char* szName) {
+ ai_assert(nullptr != szName);
+ const uint32_t hash = SuperFastHash(szName);
+
+ typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
+ if (it == list.end()) {
+ return false;
+ }
+
+ return true;
+}
+
+#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/Hash.h b/thirdparty/assimp/include/assimp/Hash.h
new file mode 100644
index 0000000000..30657be198
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Hash.h
@@ -0,0 +1,118 @@
+/*
+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 AI_HASH_H_INCLUDED
+#define AI_HASH_H_INCLUDED
+
+#include <stdint.h>
+#include <string.h>
+
+// ------------------------------------------------------------------------------------------------
+// Hashing function taken from
+// http://www.azillionmonkeys.com/qed/hash.html
+// (incremental version)
+//
+// This code is Copyright 2004-2008 by Paul Hsieh. It is used here in the belief that
+// Assimp's license is considered compatible with Pauls's derivative license as specified
+// on his web page.
+//
+// (stdint.h should have been been included here)
+// ------------------------------------------------------------------------------------------------
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+// ------------------------------------------------------------------------------------------------
+inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) {
+uint32_t tmp;
+int rem;
+
+ if (!data) return 0;
+ if (!len)len = (uint32_t)::strlen(data);
+
+ rem = len & 3;
+ len >>= 2;
+
+ /* Main loop */
+ for (;len > 0; len--) {
+ hash += get16bits (data);
+ tmp = (get16bits (data+2) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ data += 2*sizeof (uint16_t);
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (rem) {
+ case 3: hash += get16bits (data);
+ hash ^= hash << 16;
+ hash ^= data[sizeof (uint16_t)] << 18;
+ hash += hash >> 11;
+ break;
+ case 2: hash += get16bits (data);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+ case 1: hash += *data;
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
+
+#endif // !! AI_HASH_H_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/IOStream.hpp b/thirdparty/assimp/include/assimp/IOStream.hpp
new file mode 100644
index 0000000000..0623d0f70b
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/IOStream.hpp
@@ -0,0 +1,142 @@
+/*
+---------------------------------------------------------------------------
+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 IOStream.hpp
+ * @brief File I/O wrappers for C++.
+ */
+
+#pragma once
+#ifndef AI_IOSTREAM_H_INC
+#define AI_IOSTREAM_H_INC
+
+#include "types.h"
+
+#ifndef __cplusplus
+# error This header requires C++ to be used. aiFileIO.h is the \
+ corresponding C interface.
+#endif
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** @brief CPP-API: Class to handle file I/O for C++
+ *
+ * Derive an own implementation from this interface to provide custom IO handling
+ * to the Importer. If you implement this interface, be sure to also provide an
+ * implementation for IOSystem that creates instances of your custom IO class.
+*/
+class ASSIMP_API IOStream
+#ifndef SWIG
+ : public Intern::AllocateFromAssimpHeap
+#endif
+{
+protected:
+ /** Constructor protected, use IOSystem::Open() to create an instance. */
+ IOStream() AI_NO_EXCEPT;
+
+public:
+ // -------------------------------------------------------------------
+ /** @brief Destructor. Deleting the object closes the underlying file,
+ * alternatively you may use IOSystem::Close() to release the file.
+ */
+ virtual ~IOStream();
+
+ // -------------------------------------------------------------------
+ /** @brief Read from the file
+ *
+ * See fread() for more details
+ * This fails for write-only files */
+ virtual size_t Read(void* pvBuffer,
+ size_t pSize,
+ size_t pCount) = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Write to the file
+ *
+ * See fwrite() for more details
+ * This fails for read-only files */
+ virtual size_t Write(const void* pvBuffer,
+ size_t pSize,
+ size_t pCount) = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Set the read/write cursor of the file
+ *
+ * Note that the offset is _negative_ for aiOrigin_END.
+ * See fseek() for more details */
+ virtual aiReturn Seek(size_t pOffset,
+ aiOrigin pOrigin) = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Get the current position of the read/write cursor
+ *
+ * See ftell() for more details */
+ virtual size_t Tell() const = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Returns filesize
+ * Returns the filesize. */
+ virtual size_t FileSize() const = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Flush the contents of the file buffer (for writers)
+ * See fflush() for more details.
+ */
+ virtual void Flush() = 0;
+}; //! class IOStream
+
+// ----------------------------------------------------------------------------------
+inline
+IOStream::IOStream() AI_NO_EXCEPT {
+ // empty
+}
+
+// ----------------------------------------------------------------------------------
+inline
+IOStream::~IOStream() {
+ // empty
+}
+// ----------------------------------------------------------------------------------
+
+} //!namespace Assimp
+
+#endif //!!AI_IOSTREAM_H_INC
diff --git a/thirdparty/assimp/include/assimp/IOStreamBuffer.h b/thirdparty/assimp/include/assimp/IOStreamBuffer.h
new file mode 100644
index 0000000000..58abd97a02
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/IOStreamBuffer.h
@@ -0,0 +1,355 @@
+#pragma once
+
+/*
+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/types.h>
+#include <assimp/IOStream.hpp>
+
+#include "ParsingUtils.h"
+
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/**
+ * Implementation of a cached stream buffer.
+ */
+template<class T>
+class IOStreamBuffer {
+public:
+ /// @brief The class constructor.
+ IOStreamBuffer( size_t cache = 4096 * 4096 );
+
+ /// @brief The class destructor.
+ ~IOStreamBuffer();
+
+ /// @brief Will open the cached access for a given stream.
+ /// @param stream The stream to cache.
+ /// @return true if successful.
+ bool open( IOStream *stream );
+
+ /// @brief Will close the cached access.
+ /// @return true if successful.
+ bool close();
+
+ /// @brief Returns the file-size.
+ /// @return The file-size.
+ size_t size() const;
+
+ /// @brief Returns the cache size.
+ /// @return The cache size.
+ size_t cacheSize() const;
+
+ /// @brief Will read the next block.
+ /// @return true if successful.
+ bool readNextBlock();
+
+ /// @brief Returns the number of blocks to read.
+ /// @return The number of blocks.
+ size_t getNumBlocks() const;
+
+ /// @brief Returns the current block index.
+ /// @return The current block index.
+ size_t getCurrentBlockIndex() const;
+
+ /// @brief Returns the current file pos.
+ /// @return The current file pos.
+ size_t getFilePos() const;
+
+ /// @brief Will read the next line.
+ /// @param buffer The buffer for the next line.
+ /// @return true if successful.
+ bool getNextDataLine( std::vector<T> &buffer, T continuationToken );
+
+ /// @brief Will read the next line ascii or binary end line char.
+ /// @param buffer The buffer for the next line.
+ /// @return true if successful.
+ bool getNextLine(std::vector<T> &buffer);
+
+ /// @brief Will read the next block.
+ /// @param buffer The buffer for the next block.
+ /// @return true if successful.
+ bool getNextBlock( std::vector<T> &buffer );
+
+private:
+ IOStream *m_stream;
+ size_t m_filesize;
+ size_t m_cacheSize;
+ size_t m_numBlocks;
+ size_t m_blockIdx;
+ std::vector<T> m_cache;
+ size_t m_cachePos;
+ size_t m_filePos;
+};
+
+template<class T>
+inline
+IOStreamBuffer<T>::IOStreamBuffer( size_t cache )
+: m_stream( nullptr )
+, m_filesize( 0 )
+, m_cacheSize( cache )
+, m_numBlocks( 0 )
+, m_blockIdx( 0 )
+, m_cachePos( 0 )
+, m_filePos( 0 ) {
+ m_cache.resize( cache );
+ std::fill( m_cache.begin(), m_cache.end(), '\n' );
+}
+
+template<class T>
+inline
+IOStreamBuffer<T>::~IOStreamBuffer() {
+ // empty
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::open( IOStream *stream ) {
+ // file still opened!
+ if ( nullptr != m_stream ) {
+ return false;
+ }
+
+ // Invalid stream pointer
+ if ( nullptr == stream ) {
+ return false;
+ }
+
+ m_stream = stream;
+ m_filesize = m_stream->FileSize();
+ if ( m_filesize == 0 ) {
+ return false;
+ }
+ if ( m_filesize < m_cacheSize ) {
+ m_cacheSize = m_filesize;
+ }
+
+ m_numBlocks = m_filesize / m_cacheSize;
+ if ( ( m_filesize % m_cacheSize ) > 0 ) {
+ m_numBlocks++;
+ }
+
+ return true;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::close() {
+ if ( nullptr == m_stream ) {
+ return false;
+ }
+
+ // init counters and state vars
+ m_stream = nullptr;
+ m_filesize = 0;
+ m_numBlocks = 0;
+ m_blockIdx = 0;
+ m_cachePos = 0;
+ m_filePos = 0;
+
+ return true;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::size() const {
+ return m_filesize;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::cacheSize() const {
+ return m_cacheSize;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::readNextBlock() {
+ m_stream->Seek( m_filePos, aiOrigin_SET );
+ size_t readLen = m_stream->Read( &m_cache[ 0 ], sizeof( T ), m_cacheSize );
+ if ( readLen == 0 ) {
+ return false;
+ }
+ if ( readLen < m_cacheSize ) {
+ m_cacheSize = readLen;
+ }
+ m_filePos += m_cacheSize;
+ m_cachePos = 0;
+ m_blockIdx++;
+
+ return true;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getNumBlocks() const {
+ return m_numBlocks;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getCurrentBlockIndex() const {
+ return m_blockIdx;
+}
+
+template<class T>
+inline
+size_t IOStreamBuffer<T>::getFilePos() const {
+ return m_filePos;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::getNextDataLine( std::vector<T> &buffer, T continuationToken ) {
+ buffer.resize( m_cacheSize );
+ if ( m_cachePos >= m_cacheSize || 0 == m_filePos ) {
+ if ( !readNextBlock() ) {
+ return false;
+ }
+ }
+
+ bool continuationFound( false );
+ size_t i = 0;
+ for( ;; ) {
+ if ( continuationToken == m_cache[ m_cachePos ] ) {
+ continuationFound = true;
+ ++m_cachePos;
+ }
+ if ( IsLineEnd( m_cache[ m_cachePos ] ) ) {
+ if ( !continuationFound ) {
+ // the end of the data line
+ break;
+ } else {
+ // skip line end
+ while ( m_cache[m_cachePos] != '\n') {
+ ++m_cachePos;
+ }
+ ++m_cachePos;
+ continuationFound = false;
+ }
+ }
+
+ buffer[ i ] = m_cache[ m_cachePos ];
+ ++m_cachePos;
+ ++i;
+ if (m_cachePos >= size()) {
+ break;
+ }
+ if ( m_cachePos >= m_cacheSize ) {
+ if ( !readNextBlock() ) {
+ return false;
+ }
+ }
+ }
+
+ buffer[ i ] = '\n';
+ ++m_cachePos;
+
+ return true;
+}
+
+static inline
+bool isEndOfCache( size_t pos, size_t cacheSize ) {
+ return ( pos == cacheSize );
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
+ buffer.resize(m_cacheSize);
+ if ( isEndOfCache( m_cachePos, m_cacheSize ) || 0 == m_filePos) {
+ if (!readNextBlock()) {
+ return false;
+ }
+ }
+
+ if (IsLineEnd(m_cache[m_cachePos])) {
+ // skip line end
+ while (m_cache[m_cachePos] != '\n') {
+ ++m_cachePos;
+ }
+ ++m_cachePos;
+ if ( isEndOfCache( m_cachePos, m_cacheSize ) ) {
+ if ( !readNextBlock() ) {
+ return false;
+ }
+ }
+ }
+
+ size_t i( 0 );
+ while (!IsLineEnd(m_cache[ m_cachePos ])) {
+ buffer[i] = m_cache[ m_cachePos ];
+ ++m_cachePos;
+ ++i;
+ if (m_cachePos >= m_cacheSize) {
+ if (!readNextBlock()) {
+ return false;
+ }
+ }
+ }
+ buffer[i] = '\n';
+ ++m_cachePos;
+
+ return true;
+}
+
+template<class T>
+inline
+bool IOStreamBuffer<T>::getNextBlock( std::vector<T> &buffer) {
+ // Return the last block-value if getNextLine was used before
+ if ( 0 != m_cachePos ) {
+ buffer = std::vector<T>( m_cache.begin() + m_cachePos, m_cache.end() );
+ m_cachePos = 0;
+ } else {
+ if ( !readNextBlock() ) {
+ return false;
+ }
+
+ buffer = std::vector<T>(m_cache.begin(), m_cache.end());
+ }
+
+ return true;
+}
+
+} // !ns Assimp
diff --git a/thirdparty/assimp/include/assimp/IOSystem.hpp b/thirdparty/assimp/include/assimp/IOSystem.hpp
new file mode 100644
index 0000000000..78139c2839
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/IOSystem.hpp
@@ -0,0 +1,357 @@
+/*
+---------------------------------------------------------------------------
+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 IOSystem.hpp
+ * @brief File system wrapper for C++. Inherit this class to supply
+ * custom file handling logic to the Import library.
+*/
+
+#pragma once
+#ifndef AI_IOSYSTEM_H_INC
+#define AI_IOSYSTEM_H_INC
+
+#ifndef __cplusplus
+# error This header requires C++ to be used. aiFileIO.h is the \
+ corresponding C interface.
+#endif
+
+#include "types.h"
+
+#ifdef _WIN32
+# include <direct.h>
+# include <stdlib.h>
+# include <stdio.h>
+#else
+# include <sys/stat.h>
+# include <sys/types.h>
+# include <unistd.h>
+#endif // _WIN32
+
+#include <vector>
+
+namespace Assimp {
+
+ class IOStream;
+
+// ---------------------------------------------------------------------------
+/** @brief CPP-API: Interface to the file system.
+ *
+ * Derive an own implementation from this interface to supply custom file handling
+ * to the importer library. If you implement this interface, you also want to
+ * supply a custom implementation for IOStream.
+ *
+ * @see Importer::SetIOHandler()
+ */
+class ASSIMP_API IOSystem
+#ifndef SWIG
+ : public Intern::AllocateFromAssimpHeap
+#endif
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Default constructor.
+ *
+ * Create an instance of your derived class and assign it to an
+ * #Assimp::Importer instance by calling Importer::SetIOHandler().
+ */
+ IOSystem() AI_NO_EXCEPT;
+
+ // -------------------------------------------------------------------
+ /** @brief Virtual destructor.
+ *
+ * It is safe to be called from within DLL Assimp, we're constructed
+ * on Assimp's heap.
+ */
+ virtual ~IOSystem();
+
+ // -------------------------------------------------------------------
+ /** @brief For backward compatibility
+ * @see Exists(const char*)
+ */
+ AI_FORCE_INLINE bool Exists( const std::string& pFile) const;
+
+ // -------------------------------------------------------------------
+ /** @brief Tests for the existence of a file at the given path.
+ *
+ * @param pFile Path to the file
+ * @return true if there is a file with this path, else false.
+ */
+ virtual bool Exists( const char* pFile) const = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Returns the system specific directory separator
+ * @return System specific directory separator
+ */
+ virtual char getOsSeparator() const = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Open a new file with a given path.
+ *
+ * When the access to the file is finished, call Close() to release
+ * all associated resources (or the virtual dtor of the IOStream).
+ *
+ * @param pFile Path to the file
+ * @param pMode Desired file I/O mode. Required are: "wb", "w", "wt",
+ * "rb", "r", "rt".
+ *
+ * @return New IOStream interface allowing the lib to access
+ * the underlying file.
+ * @note When implementing this class to provide custom IO handling,
+ * you probably have to supply an own implementation of IOStream as well.
+ */
+ virtual IOStream* Open(const char* pFile,
+ const char* pMode = "rb") = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief For backward compatibility
+ * @see Open(const char*, const char*)
+ */
+ inline IOStream* Open(const std::string& pFile,
+ const std::string& pMode = std::string("rb"));
+
+ // -------------------------------------------------------------------
+ /** @brief Closes the given file and releases all resources
+ * associated with it.
+ * @param pFile The file instance previously created by Open().
+ */
+ virtual void Close( IOStream* pFile) = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Compares two paths and check whether the point to
+ * identical files.
+ *
+ * The dummy implementation of this virtual member performs a
+ * case-insensitive comparison of the given strings. The default IO
+ * system implementation uses OS mechanisms to convert relative into
+ * absolute paths, so the result can be trusted.
+ * @param one First file
+ * @param second Second file
+ * @return true if the paths point to the same file. The file needn't
+ * be existing, however.
+ */
+ virtual bool ComparePaths (const char* one,
+ const char* second) const;
+
+ // -------------------------------------------------------------------
+ /** @brief For backward compatibility
+ * @see ComparePaths(const char*, const char*)
+ */
+ inline bool ComparePaths (const std::string& one,
+ const std::string& second) const;
+
+ // -------------------------------------------------------------------
+ /** @brief Pushes a new directory onto the directory stack.
+ * @param path Path to push onto the stack.
+ * @return True, when push was successful, false if path is empty.
+ */
+ virtual bool PushDirectory( const std::string &path );
+
+ // -------------------------------------------------------------------
+ /** @brief Returns the top directory from the stack.
+ * @return The directory on the top of the stack.
+ * Returns empty when no directory was pushed to the stack.
+ */
+ virtual const std::string &CurrentDirectory() const;
+
+ // -------------------------------------------------------------------
+ /** @brief Returns the number of directories stored on the stack.
+ * @return The number of directories of the stack.
+ */
+ virtual size_t StackSize() const;
+
+ // -------------------------------------------------------------------
+ /** @brief Pops the top directory from the stack.
+ * @return True, when a directory was on the stack. False if no
+ * directory was on the stack.
+ */
+ virtual bool PopDirectory();
+
+ // -------------------------------------------------------------------
+ /** @brief CReates an new directory at the given path.
+ * @param path [in] The path to create.
+ * @return True, when a directory was created. False if the directory
+ * cannot be created.
+ */
+ virtual bool CreateDirectory( const std::string &path );
+
+ // -------------------------------------------------------------------
+ /** @brief Will change the current directory to the given path.
+ * @param path [in] The path to change to.
+ * @return True, when the directory has changed successfully.
+ */
+ virtual bool ChangeDirectory( const std::string &path );
+
+ virtual bool DeleteFile( const std::string &file );
+
+private:
+ std::vector<std::string> m_pathStack;
+};
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+IOSystem::IOSystem() AI_NO_EXCEPT
+: m_pathStack() {
+ // empty
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+IOSystem::~IOSystem() {
+ // empty
+}
+
+// ----------------------------------------------------------------------------
+// For compatibility, the interface of some functions taking a std::string was
+// changed to const char* to avoid crashes between binary incompatible STL
+// versions. This code her is inlined, so it shouldn't cause any problems.
+// ----------------------------------------------------------------------------
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) {
+ // NOTE:
+ // For compatibility, interface was changed to const char* to
+ // avoid crashes between binary incompatible STL versions
+ return Open(pFile.c_str(),pMode.c_str());
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+bool IOSystem::Exists( const std::string& pFile) const {
+ // NOTE:
+ // For compatibility, interface was changed to const char* to
+ // avoid crashes between binary incompatible STL versions
+ return Exists(pFile.c_str());
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+bool IOSystem::ComparePaths (const std::string& one, const std::string& second) const {
+ // NOTE:
+ // For compatibility, interface was changed to const char* to
+ // avoid crashes between binary incompatible STL versions
+ return ComparePaths(one.c_str(),second.c_str());
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+bool IOSystem::PushDirectory( const std::string &path ) {
+ if ( path.empty() ) {
+ return false;
+ }
+
+ m_pathStack.push_back( path );
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+const std::string &IOSystem::CurrentDirectory() const {
+ if ( m_pathStack.empty() ) {
+ static const std::string Dummy("");
+ return Dummy;
+ }
+ return m_pathStack[ m_pathStack.size()-1 ];
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+size_t IOSystem::StackSize() const {
+ return m_pathStack.size();
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+bool IOSystem::PopDirectory() {
+ if ( m_pathStack.empty() ) {
+ return false;
+ }
+
+ m_pathStack.pop_back();
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+bool IOSystem::CreateDirectory( const std::string &path ) {
+ if ( path.empty() ) {
+ return false;
+ }
+
+#ifdef _WIN32
+ return 0 != ::_mkdir( path.c_str() );
+#else
+ return 0 != ::mkdir( path.c_str(), 0777 );
+#endif // _WIN32
+}
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+bool IOSystem::ChangeDirectory( const std::string &path ) {
+ if ( path.empty() ) {
+ return false;
+ }
+
+#ifdef _WIN32
+ return 0 != ::_chdir( path.c_str() );
+#else
+ return 0 != ::chdir( path.c_str() );
+#endif // _WIN32
+}
+
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE
+bool IOSystem::DeleteFile( const std::string &file ) {
+ if ( file.empty() ) {
+ return false;
+ }
+ const int retCode( ::remove( file.c_str() ) );
+ return ( 0 == retCode );
+}
+} //!ns Assimp
+
+#endif //AI_IOSYSTEM_H_INC
diff --git a/thirdparty/assimp/include/assimp/Importer.hpp b/thirdparty/assimp/include/assimp/Importer.hpp
new file mode 100644
index 0000000000..4941df4122
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Importer.hpp
@@ -0,0 +1,659 @@
+/*
+---------------------------------------------------------------------------
+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.hpp
+ * @brief Defines the C++-API to the Open Asset Import Library.
+ */
+#pragma once
+#ifndef AI_ASSIMP_HPP_INC
+#define AI_ASSIMP_HPP_INC
+
+#ifndef __cplusplus
+# error This header requires C++ to be used. Use assimp.h for plain C.
+#endif // __cplusplus
+
+// Public ASSIMP data structures
+#include <assimp/types.h>
+
+namespace Assimp {
+ // =======================================================================
+ // Public interface to Assimp
+ class Importer;
+ class IOStream;
+ class IOSystem;
+ class ProgressHandler;
+
+ // =======================================================================
+ // Plugin development
+ //
+ // Include the following headers for the declarations:
+ // BaseImporter.h
+ // BaseProcess.h
+ class BaseImporter;
+ class BaseProcess;
+ class SharedPostProcessInfo;
+ class BatchLoader;
+
+ // =======================================================================
+ // Holy stuff, only for members of the high council of the Jedi.
+ class ImporterPimpl;
+} //! namespace Assimp
+
+#define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff
+
+struct aiScene;
+
+// importerdesc.h
+struct aiImporterDesc;
+
+/** @namespace Assimp Assimp's CPP-API and all internal APIs */
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** CPP-API: The Importer class forms an C++ interface to the functionality of the
+* Open Asset Import Library.
+*
+* Create an object of this class and call ReadFile() to import a file.
+* If the import succeeds, the function returns a pointer to the imported data.
+* The data remains property of the object, it is intended to be accessed
+* read-only. The imported data will be destroyed along with the Importer
+* object. If the import fails, ReadFile() returns a NULL pointer. In this
+* case you can retrieve a human-readable error description be calling
+* GetErrorString(). You can call ReadFile() multiple times with a single Importer
+* instance. Actually, constructing Importer objects involves quite many
+* allocations and may take some time, so it's better to reuse them as often as
+* possible.
+*
+* If you need the Importer to do custom file handling to access the files,
+* implement IOSystem and IOStream and supply an instance of your custom
+* IOSystem implementation by calling SetIOHandler() before calling ReadFile().
+* If you do not assign a custion IO handler, a default handler using the
+* standard C++ IO logic will be used.
+*
+* @note One Importer instance is not thread-safe. If you use multiple
+* threads for loading, each thread should maintain its own Importer instance.
+*/
+class ASSIMP_API Importer {
+public:
+ /**
+ * @brief The upper limit for hints.
+ */
+ static const unsigned int MaxLenHint = 200;
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructor. Creates an empty importer object.
+ *
+ * Call ReadFile() to start the import process. The configuration
+ * property table is initially empty.
+ */
+ Importer();
+
+ // -------------------------------------------------------------------
+ /** Copy constructor.
+ *
+ * This copies the configuration properties of another Importer.
+ * If this Importer owns a scene it won't be copied.
+ * Call ReadFile() to start the import process.
+ */
+ Importer(const Importer& other)=delete;
+
+ // -------------------------------------------------------------------
+ /** Assignment operator has been deleted
+ */
+ Importer &operator=(const Importer &) = delete;
+
+ // -------------------------------------------------------------------
+ /** Destructor. The object kept ownership of the imported data,
+ * which now will be destroyed along with the object.
+ */
+ ~Importer();
+
+
+ // -------------------------------------------------------------------
+ /** Registers a new loader.
+ *
+ * @param pImp Importer to be added. The Importer instance takes
+ * ownership of the pointer, so it will be automatically deleted
+ * with the Importer instance.
+ * @return AI_SUCCESS if the loader has been added. The registration
+ * fails if there is already a loader for a specific file extension.
+ */
+ aiReturn RegisterLoader(BaseImporter* pImp);
+
+ // -------------------------------------------------------------------
+ /** Unregisters a loader.
+ *
+ * @param pImp Importer to be unregistered.
+ * @return AI_SUCCESS if the loader has been removed. The function
+ * fails if the loader is currently in use (this could happen
+ * if the #Importer instance is used by more than one thread) or
+ * if it has not yet been registered.
+ */
+ aiReturn UnregisterLoader(BaseImporter* pImp);
+
+ // -------------------------------------------------------------------
+ /** Registers a new post-process step.
+ *
+ * At the moment, there's a small limitation: new post processing
+ * steps are added to end of the list, or in other words, executed
+ * last, after all built-in steps.
+ * @param pImp Post-process step to be added. The Importer instance
+ * takes ownership of the pointer, so it will be automatically
+ * deleted with the Importer instance.
+ * @return AI_SUCCESS if the step has been added correctly.
+ */
+ aiReturn RegisterPPStep(BaseProcess* pImp);
+
+ // -------------------------------------------------------------------
+ /** Unregisters a post-process step.
+ *
+ * @param pImp Step to be unregistered.
+ * @return AI_SUCCESS if the step has been removed. The function
+ * fails if the step is currently in use (this could happen
+ * if the #Importer instance is used by more than one thread) or
+ * if it has not yet been registered.
+ */
+ aiReturn UnregisterPPStep(BaseProcess* pImp);
+
+ // -------------------------------------------------------------------
+ /** Set an integer configuration property.
+ * @param szName Name of the property. All supported properties
+ * are defined in the aiConfig.g header (all constants share the
+ * prefix AI_CONFIG_XXX and are simple strings).
+ * @param iValue New value of the property
+ * @return true if the property was set before. The new value replaces
+ * the previous value in this case.
+ * @note Property of different types (float, int, string ..) are kept
+ * on different stacks, so calling SetPropertyInteger() for a
+ * floating-point property has no effect - the loader will call
+ * GetPropertyFloat() to read the property, but it won't be there.
+ */
+ bool SetPropertyInteger(const char* szName, int iValue);
+
+ // -------------------------------------------------------------------
+ /** Set a boolean configuration property. Boolean properties
+ * are stored on the integer stack internally so it's possible
+ * to set them via #SetPropertyBool and query them with
+ * #GetPropertyBool and vice versa.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyBool(const char* szName, bool value) {
+ return SetPropertyInteger(szName,value);
+ }
+
+ // -------------------------------------------------------------------
+ /** Set a floating-point configuration property.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyFloat(const char* szName, ai_real fValue);
+
+ // -------------------------------------------------------------------
+ /** Set a string configuration property.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyString(const char* szName, const std::string& sValue);
+
+ // -------------------------------------------------------------------
+ /** Set a matrix configuration property.
+ * @see SetPropertyInteger()
+ */
+ bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue);
+
+ // -------------------------------------------------------------------
+ /** Get a configuration property.
+ * @param szName Name of the property. All supported properties
+ * are defined in the aiConfig.g header (all constants share the
+ * prefix AI_CONFIG_XXX).
+ * @param iErrorReturn Value that is returned if the property
+ * is not found.
+ * @return Current value of the property
+ * @note Property of different types (float, int, string ..) are kept
+ * on different lists, so calling SetPropertyInteger() for a
+ * floating-point property has no effect - the loader will call
+ * GetPropertyFloat() to read the property, but it won't be there.
+ */
+ int GetPropertyInteger(const char* szName,
+ int iErrorReturn = 0xffffffff) const;
+
+ // -------------------------------------------------------------------
+ /** Get a boolean configuration property. Boolean properties
+ * are stored on the integer stack internally so it's possible
+ * to set them via #SetPropertyBool and query them with
+ * #GetPropertyBool and vice versa.
+ * @see GetPropertyInteger()
+ */
+ bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const {
+ return GetPropertyInteger(szName,bErrorReturn)!=0;
+ }
+
+ // -------------------------------------------------------------------
+ /** Get a floating-point configuration property
+ * @see GetPropertyInteger()
+ */
+ ai_real GetPropertyFloat(const char* szName,
+ ai_real fErrorReturn = 10e10) const;
+
+ // -------------------------------------------------------------------
+ /** Get a string configuration property
+ *
+ * The return value remains valid until the property is modified.
+ * @see GetPropertyInteger()
+ */
+ const std::string GetPropertyString(const char* szName,
+ const std::string& sErrorReturn = "") const;
+
+ // -------------------------------------------------------------------
+ /** Get a matrix configuration property
+ *
+ * The return value remains valid until the property is modified.
+ * @see GetPropertyInteger()
+ */
+ const aiMatrix4x4 GetPropertyMatrix(const char* szName,
+ const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const;
+
+ // -------------------------------------------------------------------
+ /** Supplies a custom IO handler to the importer to use to open and
+ * access files. If you need the importer to use custom IO logic to
+ * access the files, you need to provide a custom implementation of
+ * IOSystem and IOFile to the importer. Then create an instance of
+ * your custom IOSystem implementation and supply it by this function.
+ *
+ * The Importer takes ownership of the object and will destroy it
+ * afterwards. The previously assigned handler will be deleted.
+ * Pass NULL to take again ownership of your IOSystem and reset Assimp
+ * to use its default implementation.
+ *
+ * @param pIOHandler The IO handler to be used in all file accesses
+ * of the Importer.
+ */
+ void SetIOHandler( IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Retrieves the IO handler that is currently set.
+ * You can use #IsDefaultIOHandler() to check whether the returned
+ * interface is the default IO handler provided by ASSIMP. The default
+ * handler is active as long the application doesn't supply its own
+ * custom IO handler via #SetIOHandler().
+ * @return A valid IOSystem interface, never NULL.
+ */
+ IOSystem* GetIOHandler() const;
+
+ // -------------------------------------------------------------------
+ /** Checks whether a default IO handler is active
+ * A default handler is active as long the application doesn't
+ * supply its own custom IO handler via #SetIOHandler().
+ * @return true by default
+ */
+ bool IsDefaultIOHandler() const;
+
+ // -------------------------------------------------------------------
+ /** Supplies a custom progress handler to the importer. This
+ * interface exposes an #Update() callback, which is called
+ * more or less periodically (please don't sue us if it
+ * isn't as periodically as you'd like it to have ...).
+ * This can be used to implement progress bars and loading
+ * timeouts.
+ * @param pHandler Progress callback interface. Pass NULL to
+ * disable progress reporting.
+ * @note Progress handlers can be used to abort the loading
+ * at almost any time.*/
+ void SetProgressHandler ( ProgressHandler* pHandler );
+
+ // -------------------------------------------------------------------
+ /** Retrieves the progress handler that is currently set.
+ * You can use #IsDefaultProgressHandler() to check whether the returned
+ * interface is the default handler provided by ASSIMP. The default
+ * handler is active as long the application doesn't supply its own
+ * custom handler via #SetProgressHandler().
+ * @return A valid ProgressHandler interface, never NULL.
+ */
+ ProgressHandler* GetProgressHandler() const;
+
+ // -------------------------------------------------------------------
+ /** Checks whether a default progress handler is active
+ * A default handler is active as long the application doesn't
+ * supply its own custom progress handler via #SetProgressHandler().
+ * @return true by default
+ */
+ bool IsDefaultProgressHandler() const;
+
+ // -------------------------------------------------------------------
+ /** @brief Check whether a given set of post-processing flags
+ * is supported.
+ *
+ * Some flags are mutually exclusive, others are probably
+ * not available because your excluded them from your
+ * Assimp builds. Calling this function is recommended if
+ * you're unsure.
+ *
+ * @param pFlags Bitwise combination of the aiPostProcess flags.
+ * @return true if this flag combination is fine.
+ */
+ bool ValidateFlags(unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** Reads the given file and returns its contents if successful.
+ *
+ * If the call succeeds, the contents of the file are returned as a
+ * pointer to an aiScene object. The returned data is intended to be
+ * read-only, the importer object keeps ownership of the data and will
+ * destroy it upon destruction. If the import fails, NULL is returned.
+ * A human-readable error description can be retrieved by calling
+ * GetErrorString(). The previous scene will be deleted during this call.
+ * @param pFile Path and filename to the file to be imported.
+ * @param pFlags Optional post processing steps to be executed after
+ * a successful import. Provide a bitwise combination of the
+ * #aiPostProcessSteps flags. If you wish to inspect the imported
+ * scene first in order to fine-tune your post-processing setup,
+ * consider to use #ApplyPostProcessing().
+ * @return A pointer to the imported data, NULL if the import failed.
+ * The pointer to the scene remains in possession of the Importer
+ * instance. Use GetOrphanedScene() to take ownership of it.
+ *
+ * @note Assimp is able to determine the file format of a file
+ * automatically.
+ */
+ const aiScene* ReadFile(
+ const char* pFile,
+ unsigned int pFlags);
+
+ // -------------------------------------------------------------------
+ /** Reads the given file from a memory buffer and returns its
+ * contents if successful.
+ *
+ * If the call succeeds, the contents of the file are returned as a
+ * pointer to an aiScene object. The returned data is intended to be
+ * read-only, the importer object keeps ownership of the data and will
+ * destroy it upon destruction. If the import fails, NULL is returned.
+ * A human-readable error description can be retrieved by calling
+ * GetErrorString(). The previous scene will be deleted during this call.
+ * Calling this method doesn't affect the active IOSystem.
+ * @param pBuffer Pointer to the file data
+ * @param pLength Length of pBuffer, in bytes
+ * @param pFlags Optional post processing steps to be executed after
+ * a successful import. Provide a bitwise combination of the
+ * #aiPostProcessSteps flags. If you wish to inspect the imported
+ * scene first in order to fine-tune your post-processing setup,
+ * consider to use #ApplyPostProcessing().
+ * @param pHint An additional hint to the library. If this is a non
+ * empty string, the library looks for a loader to support
+ * the file extension specified by pHint and passes the file to
+ * the first matching loader. If this loader is unable to completely
+ * the request, the library continues and tries to determine the
+ * file format on its own, a task that may or may not be successful.
+ * Check the return value, and you'll know ...
+ * @return A pointer to the imported data, NULL if the import failed.
+ * The pointer to the scene remains in possession of the Importer
+ * instance. Use GetOrphanedScene() to take ownership of it.
+ *
+ * @note This is a straightforward way to decode models from memory
+ * buffers, but it doesn't handle model formats that spread their
+ * data across multiple files or even directories. Examples include
+ * OBJ or MD3, which outsource parts of their material info into
+ * external scripts. If you need full functionality, provide
+ * a custom IOSystem to make Assimp find these files and use
+ * the regular ReadFile() API.
+ */
+ const aiScene* ReadFileFromMemory(
+ const void* pBuffer,
+ size_t pLength,
+ unsigned int pFlags,
+ const char* pHint = "");
+
+ // -------------------------------------------------------------------
+ /** Apply post-processing to an already-imported scene.
+ *
+ * This is strictly equivalent to calling #ReadFile() with the same
+ * flags. However, you can use this separate function to inspect
+ * the imported scene first to fine-tune your post-processing setup.
+ * @param pFlags Provide a bitwise combination of the
+ * #aiPostProcessSteps flags.
+ * @return A pointer to the post-processed data. This is still the
+ * same as the pointer returned by #ReadFile(). However, if
+ * post-processing fails, the scene could now be NULL.
+ * That's quite a rare case, post processing steps are not really
+ * designed to 'fail'. To be exact, the #aiProcess_ValidateDS
+ * flag is currently the only post processing step which can actually
+ * cause the scene to be reset to NULL.
+ *
+ * @note The method does nothing if no scene is currently bound
+ * to the #Importer instance. */
+ const aiScene* ApplyPostProcessing(unsigned int pFlags);
+
+ const aiScene* ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation );
+
+ // -------------------------------------------------------------------
+ /** @brief Reads the given file and returns its contents if successful.
+ *
+ * This function is provided for backward compatibility.
+ * See the const char* version for detailed docs.
+ * @see ReadFile(const char*, pFlags) */
+ const aiScene* ReadFile(
+ const std::string& pFile,
+ unsigned int pFlags);
+
+ // -------------------------------------------------------------------
+ /** Frees the current scene.
+ *
+ * The function does nothing if no scene has previously been
+ * read via ReadFile(). FreeScene() is called automatically by the
+ * destructor and ReadFile() itself. */
+ void FreeScene( );
+
+ // -------------------------------------------------------------------
+ /** Returns an error description of an error that occurred in ReadFile().
+ *
+ * Returns an empty string if no error occurred.
+ * @return A description of the last error, an empty string if no
+ * error occurred. The string is never NULL.
+ *
+ * @note The returned function remains valid until one of the
+ * following methods is called: #ReadFile(), #FreeScene(). */
+ const char* GetErrorString() const;
+
+ // -------------------------------------------------------------------
+ /** Returns the scene loaded by the last successful call to ReadFile()
+ *
+ * @return Current scene or NULL if there is currently no scene loaded */
+ const aiScene* GetScene() const;
+
+ // -------------------------------------------------------------------
+ /** Returns the scene loaded by the last successful call to ReadFile()
+ * and releases the scene from the ownership of the Importer
+ * instance. The application is now responsible for deleting the
+ * scene. Any further calls to GetScene() or GetOrphanedScene()
+ * will return NULL - until a new scene has been loaded via ReadFile().
+ *
+ * @return Current scene or NULL if there is currently no scene loaded
+ * @note Use this method with maximal caution, and only if you have to.
+ * By design, aiScene's are exclusively maintained, allocated and
+ * deallocated by Assimp and no one else. The reasoning behind this
+ * is the golden rule that deallocations should always be done
+ * by the module that did the original allocation because heaps
+ * are not necessarily shared. GetOrphanedScene() enforces you
+ * to delete the returned scene by yourself, but this will only
+ * be fine if and only if you're using the same heap as assimp.
+ * On Windows, it's typically fine provided everything is linked
+ * against the multithreaded-dll version of the runtime library.
+ * It will work as well for static linkage with Assimp.*/
+ aiScene* GetOrphanedScene();
+
+ // -------------------------------------------------------------------
+ /** Returns whether a given file extension is supported by ASSIMP.
+ *
+ * @param szExtension Extension to be checked.
+ * Must include a trailing dot '.'. Example: ".3ds", ".md3".
+ * Cases-insensitive.
+ * @return true if the extension is supported, false otherwise */
+ bool IsExtensionSupported(const char* szExtension) const;
+
+ // -------------------------------------------------------------------
+ /** @brief Returns whether a given file extension is supported by ASSIMP.
+ *
+ * This function is provided for backward compatibility.
+ * See the const char* version for detailed and up-to-date docs.
+ * @see IsExtensionSupported(const char*) */
+ inline bool IsExtensionSupported(const std::string& szExtension) const;
+
+ // -------------------------------------------------------------------
+ /** Get a full list of all file extensions supported by ASSIMP.
+ *
+ * If a file extension is contained in the list this does of course not
+ * mean that ASSIMP is able to load all files with this extension ---
+ * it simply means there is an importer loaded which claims to handle
+ * files with this file extension.
+ * @param szOut String to receive the extension list.
+ * Format of the list: "*.3ds;*.obj;*.dae". This is useful for
+ * use with the WinAPI call GetOpenFileName(Ex). */
+ void GetExtensionList(aiString& szOut) const;
+
+ // -------------------------------------------------------------------
+ /** @brief Get a full list of all file extensions supported by ASSIMP.
+ *
+ * This function is provided for backward compatibility.
+ * See the aiString version for detailed and up-to-date docs.
+ * @see GetExtensionList(aiString&)*/
+ inline void GetExtensionList(std::string& szOut) const;
+
+ // -------------------------------------------------------------------
+ /** Get the number of importers currently registered with Assimp. */
+ size_t GetImporterCount() const;
+
+ // -------------------------------------------------------------------
+ /** Get meta data for the importer corresponding to a specific index..
+ *
+ * For the declaration of #aiImporterDesc, include <assimp/importerdesc.h>.
+ * @param index Index to query, must be within [0,GetImporterCount())
+ * @return Importer meta data structure, NULL if the index does not
+ * exist or if the importer doesn't offer meta information (
+ * importers may do this at the cost of being hated by their peers).*/
+ const aiImporterDesc* GetImporterInfo(size_t index) const;
+
+ // -------------------------------------------------------------------
+ /** Find the importer corresponding to a specific index.
+ *
+ * @param index Index to query, must be within [0,GetImporterCount())
+ * @return Importer instance. NULL if the index does not
+ * exist. */
+ BaseImporter* GetImporter(size_t index) const;
+
+ // -------------------------------------------------------------------
+ /** Find the importer corresponding to a specific file extension.
+ *
+ * This is quite similar to #IsExtensionSupported except a
+ * BaseImporter instance is returned.
+ * @param szExtension Extension to check for. The following formats
+ * are recognized (BAH being the file extension): "BAH" (comparison
+ * is case-insensitive), ".bah", "*.bah" (wild card and dot
+ * characters at the beginning of the extension are skipped).
+ * @return NULL if no importer is found*/
+ BaseImporter* GetImporter (const char* szExtension) const;
+
+ // -------------------------------------------------------------------
+ /** Find the importer index corresponding to a specific file extension.
+ *
+ * @param szExtension Extension to check for. The following formats
+ * are recognized (BAH being the file extension): "BAH" (comparison
+ * is case-insensitive), ".bah", "*.bah" (wild card and dot
+ * characters at the beginning of the extension are skipped).
+ * @return (size_t)-1 if no importer is found */
+ size_t GetImporterIndex (const char* szExtension) const;
+
+ // -------------------------------------------------------------------
+ /** Returns the storage allocated by ASSIMP to hold the scene data
+ * in memory.
+ *
+ * This refers to the currently loaded file, see #ReadFile().
+ * @param in Data structure to be filled.
+ * @note The returned memory statistics refer to the actual
+ * size of the use data of the aiScene. Heap-related overhead
+ * is (naturally) not included.*/
+ void GetMemoryRequirements(aiMemoryInfo& in) const;
+
+ // -------------------------------------------------------------------
+ /** Enables "extra verbose" mode.
+ *
+ * 'Extra verbose' means the data structure is validated after *every*
+ * single post processing step to make sure everyone modifies the data
+ * structure in a well-defined manner. This is a debug feature and not
+ * intended for use in production environments. */
+ void SetExtraVerbose(bool bDo);
+
+ // -------------------------------------------------------------------
+ /** Private, do not use. */
+ ImporterPimpl* Pimpl() { return pimpl; }
+ const ImporterPimpl* Pimpl() const { return pimpl; }
+
+protected:
+
+ // Just because we don't want you to know how we're hacking around.
+ ImporterPimpl* pimpl;
+}; //! class Importer
+
+
+// ----------------------------------------------------------------------------
+// For compatibility, the interface of some functions taking a std::string was
+// changed to const char* to avoid crashes between binary incompatible STL
+// versions. This code her is inlined, so it shouldn't cause any problems.
+// ----------------------------------------------------------------------------
+
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE const aiScene* Importer::ReadFile( const std::string& pFile,unsigned int pFlags){
+ return ReadFile(pFile.c_str(),pFlags);
+}
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE void Importer::GetExtensionList(std::string& szOut) const {
+ aiString s;
+ GetExtensionList(s);
+ szOut = s.data;
+}
+// ----------------------------------------------------------------------------
+AI_FORCE_INLINE bool Importer::IsExtensionSupported(const std::string& szExtension) const {
+ return IsExtensionSupported(szExtension.c_str());
+}
+
+} // !namespace Assimp
+
+#endif // AI_ASSIMP_HPP_INC
diff --git a/thirdparty/assimp/include/assimp/LineSplitter.h b/thirdparty/assimp/include/assimp/LineSplitter.h
new file mode 100644
index 0000000000..4afe45b92a
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/LineSplitter.h
@@ -0,0 +1,285 @@
+/*
+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 LineSplitter.h
+ * @brief LineSplitter, a helper class to iterate through all lines
+ * of a file easily. Works with StreamReader.
+ */
+#pragma once
+#ifndef INCLUDED_LINE_SPLITTER_H
+#define INCLUDED_LINE_SPLITTER_H
+
+#include <stdexcept>
+#include "StreamReader.h"
+#include "ParsingUtils.h"
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+/** Usage:
+@code
+for(LineSplitter splitter(stream);splitter;++splitter) {
+
+ if (*splitter == "hi!") {
+ ...
+ }
+ else if (splitter->substr(0,5) == "hello") {
+ ...
+ // access the third token in the line (tokens are space-separated)
+ if (strtol(splitter[2]) > 5) { .. }
+ }
+
+ std::cout << "Current line is: " << splitter.get_index() << std::endl;
+}
+@endcode
+*/
+// ------------------------------------------------------------------------------------------------
+class LineSplitter {
+public:
+ typedef size_t line_idx;
+
+ // -----------------------------------------
+ /** construct from existing stream reader
+ note: trim is *always* assumed true if skyp_empty_lines==true
+ */
+ LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true);
+
+ ~LineSplitter();
+
+ // -----------------------------------------
+ /** pseudo-iterator increment */
+ LineSplitter& operator++();
+
+ // -----------------------------------------
+ LineSplitter& operator++(int);
+
+ // -----------------------------------------
+ /** get a pointer to the beginning of a particular token */
+ const char* operator[] (size_t idx) const;
+
+ // -----------------------------------------
+ /** extract the start positions of N tokens from the current line*/
+ template <size_t N>
+ void get_tokens(const char* (&tokens)[N]) const;
+
+ // -----------------------------------------
+ /** member access */
+ const std::string* operator -> () const;
+
+ std::string operator* () const;
+
+ // -----------------------------------------
+ /** boolean context */
+ operator bool() const;
+
+ // -----------------------------------------
+ /** line indices are zero-based, empty lines are included */
+ operator line_idx() const;
+
+ line_idx get_index() const;
+
+ // -----------------------------------------
+ /** access the underlying stream object */
+ StreamReaderLE& get_stream();
+
+ // -----------------------------------------
+ /** !strcmp((*this)->substr(0,strlen(check)),check) */
+ bool match_start(const char* check);
+
+ // -----------------------------------------
+ /** swallow the next call to ++, return the previous value. */
+ void swallow_next_increment();
+
+ LineSplitter( const LineSplitter & ) = delete;
+ LineSplitter(LineSplitter &&) = delete;
+ LineSplitter &operator = ( const LineSplitter & ) = delete;
+
+private:
+ line_idx mIdx;
+ std::string mCur;
+ StreamReaderLE& mStream;
+ bool mSwallow, mSkip_empty_lines, mTrim;
+};
+
+inline
+LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim )
+: mIdx(0)
+, mCur()
+, mStream(stream)
+, mSwallow()
+, mSkip_empty_lines(skip_empty_lines)
+, mTrim(trim) {
+ mCur.reserve(1024);
+ operator++();
+ mIdx = 0;
+}
+
+inline
+LineSplitter::~LineSplitter() {
+ // empty
+}
+
+inline
+LineSplitter& LineSplitter::operator++() {
+ if (mSwallow) {
+ mSwallow = false;
+ return *this;
+ }
+
+ if (!*this) {
+ throw std::logic_error("End of file, no more lines to be retrieved.");
+ }
+
+ char s;
+ mCur.clear();
+ while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) {
+ if (s == '\n' || s == '\r') {
+ if (mSkip_empty_lines) {
+ while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n'));
+ if (mStream.GetRemainingSize()) {
+ mStream.IncPtr(-1);
+ }
+ } else {
+ // skip both potential line terminators but don't read past this line.
+ if (mStream.GetRemainingSize() && (s == '\r' && mStream.GetI1() != '\n')) {
+ mStream.IncPtr(-1);
+ }
+ if (mTrim) {
+ while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t'));
+ if (mStream.GetRemainingSize()) {
+ mStream.IncPtr(-1);
+ }
+ }
+ }
+ break;
+ }
+ mCur += s;
+ }
+ ++mIdx;
+
+ return *this;
+}
+
+inline
+LineSplitter &LineSplitter::operator++(int) {
+ return ++(*this);
+}
+
+inline
+const char *LineSplitter::operator[] (size_t idx) const {
+ const char* s = operator->()->c_str();
+
+ SkipSpaces(&s);
+ for (size_t i = 0; i < idx; ++i) {
+
+ for (; !IsSpace(*s); ++s) {
+ if (IsLineEnd(*s)) {
+ throw std::range_error("Token index out of range, EOL reached");
+ }
+ }
+ SkipSpaces(&s);
+ }
+ return s;
+}
+
+template <size_t N>
+inline
+void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
+ const char* s = operator->()->c_str();
+
+ SkipSpaces(&s);
+ for (size_t i = 0; i < N; ++i) {
+ if (IsLineEnd(*s)) {
+ throw std::range_error("Token count out of range, EOL reached");
+ }
+ tokens[i] = s;
+
+ for (; *s && !IsSpace(*s); ++s);
+ SkipSpaces(&s);
+ }
+}
+
+inline
+const std::string* LineSplitter::operator -> () const {
+ return &mCur;
+}
+
+inline
+std::string LineSplitter::operator* () const {
+ return mCur;
+}
+
+inline
+LineSplitter::operator bool() const {
+ return mStream.GetRemainingSize() > 0;
+}
+
+inline
+LineSplitter::operator line_idx() const {
+ return mIdx;
+}
+
+inline
+LineSplitter::line_idx LineSplitter::get_index() const {
+ return mIdx;
+}
+
+inline
+StreamReaderLE &LineSplitter::get_stream() {
+ return mStream;
+}
+
+inline
+bool LineSplitter::match_start(const char* check) {
+ const size_t len = ::strlen(check);
+
+ return len <= mCur.length() && std::equal(check, check + len, mCur.begin());
+}
+
+inline
+void LineSplitter::swallow_next_increment() {
+ mSwallow = true;
+}
+
+} // Namespace Assimp
+
+#endif // INCLUDED_LINE_SPLITTER_H
diff --git a/thirdparty/assimp/include/assimp/LogAux.h b/thirdparty/assimp/include/assimp/LogAux.h
new file mode 100644
index 0000000000..558485272e
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/LogAux.h
@@ -0,0 +1,131 @@
+/*
+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 LogAux.h
+ * @brief Common logging usage patterns for importer implementations
+ */
+#ifndef INCLUDED_AI_LOGAUX_H
+#define INCLUDED_AI_LOGAUX_H
+
+#include <assimp/TinyFormatter.h>
+#include <assimp/Exceptional.h>
+#include <assimp/DefaultLogger.hpp>
+
+namespace Assimp {
+
+template<class TDeriving>
+class LogFunctions {
+public:
+ // ------------------------------------------------------------------------------------------------
+ static void ThrowException(const std::string& msg)
+ {
+ throw DeadlyImportError(Prefix()+msg);
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogWarn(const Formatter::format& message) {
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_WARN(Prefix()+(std::string)message);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogError(const Formatter::format& message) {
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_ERROR(Prefix()+(std::string)message);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogInfo(const Formatter::format& message) {
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_INFO(Prefix()+(std::string)message);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogDebug(const Formatter::format& message) {
+ if (!DefaultLogger::isNullLogger()) {
+ ASSIMP_LOG_DEBUG(Prefix()+(std::string)message);
+ }
+ }
+
+ // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462
+#if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogWarn (const char* message) {
+ if (!DefaultLogger::isNullLogger()) {
+ LogWarn(Formatter::format(message));
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogError (const char* message) {
+ if (!DefaultLogger::isNullLogger()) {
+ LogError(Formatter::format(message));
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogInfo (const char* message) {
+ if (!DefaultLogger::isNullLogger()) {
+ LogInfo(Formatter::format(message));
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------
+ static void LogDebug (const char* message) {
+ if (!DefaultLogger::isNullLogger()) {
+ LogDebug(Formatter::format(message));
+ }
+ }
+
+#endif
+
+private:
+ static const char* Prefix();
+
+};
+} // ! Assimp
+
+#endif
diff --git a/thirdparty/assimp/include/assimp/LogStream.hpp b/thirdparty/assimp/include/assimp/LogStream.hpp
new file mode 100644
index 0000000000..d0281e2d02
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/LogStream.hpp
@@ -0,0 +1,111 @@
+/*
+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 LogStream.hpp
+ * @brief Abstract base class 'LogStream', representing an output log stream.
+ */
+#ifndef INCLUDED_AI_LOGSTREAM_H
+#define INCLUDED_AI_LOGSTREAM_H
+
+#include "types.h"
+
+namespace Assimp {
+
+class IOSystem;
+
+// ------------------------------------------------------------------------------------
+/** @brief CPP-API: Abstract interface for log stream implementations.
+ *
+ * Several default implementations are provided, see #aiDefaultLogStream for more
+ * details. Writing your own implementation of LogStream is just necessary if these
+ * are not enough for your purpose. */
+class ASSIMP_API LogStream
+#ifndef SWIG
+ : public Intern::AllocateFromAssimpHeap
+#endif
+{
+protected:
+ /** @brief Default constructor */
+ LogStream() AI_NO_EXCEPT;
+
+public:
+ /** @brief Virtual destructor */
+ virtual ~LogStream();
+
+ // -------------------------------------------------------------------
+ /** @brief Overwrite this for your own output methods
+ *
+ * Log messages *may* consist of multiple lines and you shouldn't
+ * expect a consistent formatting. If you want custom formatting
+ * (e.g. generate HTML), supply a custom instance of Logger to
+ * #DefaultLogger:set(). Usually you can *expect* that a log message
+ * is exactly one line and terminated with a single \n character.
+ * @param message Message to be written */
+ virtual void write(const char* message) = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Creates a default log stream
+ * @param streams Type of the default stream
+ * @param name For aiDefaultLogStream_FILE: name of the output file
+ * @param io For aiDefaultLogStream_FILE: IOSystem to be used to open the output
+ * file. Pass NULL for the default implementation.
+ * @return New LogStream instance. */
+ static LogStream* createDefaultStream(aiDefaultLogStream stream,
+ const char* name = "AssimpLog.txt",
+ IOSystem* io = nullptr );
+
+}; // !class LogStream
+
+inline
+LogStream::LogStream() AI_NO_EXCEPT {
+ // empty
+}
+
+inline
+LogStream::~LogStream() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------
+} // Namespace Assimp
+
+#endif
diff --git a/thirdparty/assimp/include/assimp/Logger.hpp b/thirdparty/assimp/include/assimp/Logger.hpp
new file mode 100644
index 0000000000..89cade6c33
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Logger.hpp
@@ -0,0 +1,305 @@
+/*
+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 Logger.hpp
+ * @brief Abstract base class 'Logger', base of the logging system.
+ */
+#ifndef INCLUDED_AI_LOGGER_H
+#define INCLUDED_AI_LOGGER_H
+
+#include <assimp/types.h>
+#include <assimp/TinyFormatter.h>
+
+namespace Assimp {
+
+class LogStream;
+
+// Maximum length of a log message. Longer messages are rejected.
+#define MAX_LOG_MESSAGE_LENGTH 1024u
+
+// ----------------------------------------------------------------------------------
+/** @brief CPP-API: Abstract interface for logger implementations.
+ * Assimp provides a default implementation and uses it for almost all
+ * logging stuff ('DefaultLogger'). This class defines just basic logging
+ * behavior and is not of interest for you. Instead, take a look at #DefaultLogger. */
+class ASSIMP_API Logger
+#ifndef SWIG
+ : public Intern::AllocateFromAssimpHeap
+#endif
+{
+public:
+
+ // ----------------------------------------------------------------------
+ /** @enum LogSeverity
+ * @brief Log severity to describe the granularity of logging.
+ */
+ enum LogSeverity {
+ NORMAL, //!< Normal granularity of logging
+ VERBOSE //!< Debug infos will be logged, too
+ };
+
+ // ----------------------------------------------------------------------
+ /** @enum ErrorSeverity
+ * @brief Description for severity of a log message.
+ *
+ * Every LogStream has a bitwise combination of these flags.
+ * A LogStream doesn't receive any messages of a specific type
+ * if it doesn't specify the corresponding ErrorSeverity flag.
+ */
+ enum ErrorSeverity {
+ Debugging = 1, //!< Debug log message
+ Info = 2, //!< Info log message
+ Warn = 4, //!< Warn log message
+ Err = 8 //!< Error log message
+ };
+
+public:
+
+ /** @brief Virtual destructor */
+ virtual ~Logger();
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes a debug message
+ * @param message Debug message*/
+ void debug(const char* message);
+ void debug(const std::string &message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes a info message
+ * @param message Info message*/
+ void info(const char* message);
+ void info(const std::string &message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes a warning message
+ * @param message Warn message*/
+ void warn(const char* message);
+ void warn(const std::string &message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Writes an error message
+ * @param message Error message*/
+ void error(const char* message);
+ void error(const std::string &message);
+
+ // ----------------------------------------------------------------------
+ /** @brief Set a new log severity.
+ * @param log_severity New severity for logging*/
+ void setLogSeverity(LogSeverity log_severity);
+
+ // ----------------------------------------------------------------------
+ /** @brief Get the current log severity*/
+ LogSeverity getLogSeverity() const;
+
+ // ----------------------------------------------------------------------
+ /** @brief Attach a new log-stream
+ *
+ * The logger takes ownership of the stream and is responsible
+ * for its destruction (which is done using ::delete when the logger
+ * itself is destroyed). Call detachStream to detach a stream and to
+ * gain ownership of it again.
+ * @param pStream Log-stream to attach
+ * @param severity Message filter, specified which types of log
+ * messages are dispatched to the stream. Provide a bitwise
+ * combination of the ErrorSeverity flags.
+ * @return true if the stream has been attached, false otherwise.*/
+ virtual bool attachStream(LogStream *pStream,
+ unsigned int severity = Debugging | Err | Warn | Info) = 0;
+
+ // ----------------------------------------------------------------------
+ /** @brief Detach a still attached stream from the logger (or
+ * modify the filter flags bits)
+ * @param pStream Log-stream instance for detaching
+ * @param severity Provide a bitwise combination of the ErrorSeverity
+ * flags. This value is &~ed with the current flags of the stream,
+ * if the result is 0 the stream is detached from the Logger and
+ * the caller retakes the possession of the stream.
+ * @return true if the stream has been detached, false otherwise.*/
+ virtual bool detatchStream(LogStream *pStream,
+ unsigned int severity = Debugging | Err | Warn | Info) = 0;
+
+protected:
+ /**
+ * Default constructor
+ */
+ Logger() AI_NO_EXCEPT;
+
+ /**
+ * Construction with a given log severity
+ */
+ explicit Logger(LogSeverity severity);
+
+ // ----------------------------------------------------------------------
+ /**
+ * @brief Called as a request to write a specific debug message
+ * @param message Debug message. Never longer than
+ * MAX_LOG_MESSAGE_LENGTH characters (excluding the '0').
+ * @note The message string is only valid until the scope of
+ * the function is left.
+ */
+ virtual void OnDebug(const char* message)= 0;
+
+ // ----------------------------------------------------------------------
+ /**
+ * @brief Called as a request to write a specific info message
+ * @param message Info message. Never longer than
+ * MAX_LOG_MESSAGE_LENGTH characters (ecxluding the '0').
+ * @note The message string is only valid until the scope of
+ * the function is left.
+ */
+ virtual void OnInfo(const char* message) = 0;
+
+ // ----------------------------------------------------------------------
+ /**
+ * @brief Called as a request to write a specific warn message
+ * @param message Warn message. Never longer than
+ * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0').
+ * @note The message string is only valid until the scope of
+ * the function is left.
+ */
+ virtual void OnWarn(const char* essage) = 0;
+
+ // ----------------------------------------------------------------------
+ /**
+ * @brief Called as a request to write a specific error message
+ * @param message Error message. Never longer than
+ * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0').
+ * @note The message string is only valid until the scope of
+ * the function is left.
+ */
+ virtual void OnError(const char* message) = 0;
+
+protected:
+ LogSeverity m_Severity;
+};
+
+// ----------------------------------------------------------------------------------
+// Default constructor
+inline
+Logger::Logger() AI_NO_EXCEPT
+: m_Severity(NORMAL) {
+ // empty
+}
+
+// ----------------------------------------------------------------------------------
+// Virtual destructor
+inline
+Logger::~Logger() {
+ // empty
+}
+
+// ----------------------------------------------------------------------------------
+// Construction with given logging severity
+inline
+Logger::Logger(LogSeverity severity)
+: m_Severity(severity) {
+ // empty
+}
+
+// ----------------------------------------------------------------------------------
+// Log severity setter
+inline
+void Logger::setLogSeverity(LogSeverity log_severity){
+ m_Severity = log_severity;
+}
+
+// ----------------------------------------------------------------------------------
+// Log severity getter
+inline
+Logger::LogSeverity Logger::getLogSeverity() const {
+ return m_Severity;
+}
+
+// ----------------------------------------------------------------------------------
+inline
+void Logger::debug(const std::string &message) {
+ return debug(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+inline
+void Logger::error(const std::string &message) {
+ return error(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+inline
+void Logger::warn(const std::string &message) {
+ return warn(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+inline
+void Logger::info(const std::string &message) {
+ return info(message.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+#define ASSIMP_LOG_WARN_F(string,...)\
+ DefaultLogger::get()->warn((Formatter::format(string),__VA_ARGS__))
+
+#define ASSIMP_LOG_ERROR_F(string,...)\
+ DefaultLogger::get()->error((Formatter::format(string),__VA_ARGS__))
+
+#define ASSIMP_LOG_DEBUG_F(string,...)\
+ DefaultLogger::get()->debug((Formatter::format(string),__VA_ARGS__))
+
+#define ASSIMP_LOG_INFO_F(string,...)\
+ DefaultLogger::get()->info((Formatter::format(string),__VA_ARGS__))
+
+
+#define ASSIMP_LOG_WARN(string)\
+ DefaultLogger::get()->warn(string)
+
+#define ASSIMP_LOG_ERROR(string)\
+ DefaultLogger::get()->error(string)
+
+#define ASSIMP_LOG_DEBUG(string)\
+ DefaultLogger::get()->debug(string)
+
+#define ASSIMP_LOG_INFO(string)\
+ DefaultLogger::get()->info(string)
+
+
+} // Namespace Assimp
+
+#endif // !! INCLUDED_AI_LOGGER_H
diff --git a/thirdparty/assimp/include/assimp/Macros.h b/thirdparty/assimp/include/assimp/Macros.h
new file mode 100644
index 0000000000..6515303372
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Macros.h
@@ -0,0 +1,49 @@
+/*
+---------------------------------------------------------------------------
+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.
+---------------------------------------------------------------------------
+*/
+
+/* Helper macro to set a pointer to NULL in debug builds
+ */
+#if (defined ASSIMP_BUILD_DEBUG)
+# define AI_DEBUG_INVALIDATE_PTR(x) x = NULL;
+#else
+# define AI_DEBUG_INVALIDATE_PTR(x)
+#endif
+
diff --git a/thirdparty/assimp/include/assimp/MathFunctions.h b/thirdparty/assimp/include/assimp/MathFunctions.h
new file mode 100644
index 0000000000..cb3b696076
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/MathFunctions.h
@@ -0,0 +1,77 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2016, 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 MathFunctions.h
+ * @brief Implementation of the math functions (gcd and lcm)
+ *
+ * Copied from BoostWorkaround/math
+ */
+
+namespace Assimp {
+namespace Math {
+
+// TODO: use binary GCD for unsigned integers ....
+template < typename IntegerType >
+IntegerType gcd( IntegerType a, IntegerType b )
+{
+ const IntegerType zero = (IntegerType)0;
+ while ( true )
+ {
+ if ( a == zero )
+ return b;
+ b %= a;
+
+ if ( b == zero )
+ return a;
+ a %= b;
+ }
+}
+
+template < typename IntegerType >
+IntegerType lcm( IntegerType a, IntegerType b )
+{
+ const IntegerType t = gcd (a,b);
+ if (!t)return t;
+ return a / t * b;
+}
+
+}
+}
diff --git a/thirdparty/assimp/include/assimp/MemoryIOWrapper.h b/thirdparty/assimp/include/assimp/MemoryIOWrapper.h
new file mode 100644
index 0000000000..c522787184
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/MemoryIOWrapper.h
@@ -0,0 +1,244 @@
+/*
+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 MemoryIOWrapper.h
+ * Handy IOStream/IOSystem implemetation to read directly from a memory buffer */
+#ifndef AI_MEMORYIOSTREAM_H_INC
+#define AI_MEMORYIOSTREAM_H_INC
+
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/ai_assert.h>
+#include <stdint.h>
+
+namespace Assimp {
+
+#define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$"
+#define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17
+
+// ----------------------------------------------------------------------------------
+/** Implementation of IOStream to read directly from a memory buffer */
+// ----------------------------------------------------------------------------------
+class MemoryIOStream : public IOStream {
+public:
+ MemoryIOStream (const uint8_t* buff, size_t len, bool own = false)
+ : buffer (buff)
+ , length(len)
+ , pos((size_t)0)
+ , own(own) {
+ // empty
+ }
+
+ ~MemoryIOStream () {
+ if(own) {
+ delete[] buffer;
+ }
+ }
+
+ // -------------------------------------------------------------------
+ // Read from stream
+ size_t Read(void* pvBuffer, size_t pSize, size_t pCount) {
+ ai_assert(nullptr != pvBuffer);
+ ai_assert(0 != pSize);
+
+ const size_t cnt = std::min( pCount, (length-pos) / pSize);
+ const size_t ofs = pSize * cnt;
+
+ ::memcpy(pvBuffer,buffer+pos,ofs);
+ pos += ofs;
+
+ return cnt;
+ }
+
+ // -------------------------------------------------------------------
+ // Write to stream
+ size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/,size_t /*pCount*/) {
+ ai_assert(false); // won't be needed
+ return 0;
+ }
+
+ // -------------------------------------------------------------------
+ // Seek specific position
+ aiReturn Seek(size_t pOffset, aiOrigin pOrigin) {
+ if (aiOrigin_SET == pOrigin) {
+ if (pOffset > length) {
+ return AI_FAILURE;
+ }
+ pos = pOffset;
+ } else if (aiOrigin_END == pOrigin) {
+ if (pOffset > length) {
+ return AI_FAILURE;
+ }
+ pos = length-pOffset;
+ } else {
+ if (pOffset+pos > length) {
+ return AI_FAILURE;
+ }
+ pos += pOffset;
+ }
+ return AI_SUCCESS;
+ }
+
+ // -------------------------------------------------------------------
+ // Get current seek position
+ size_t Tell() const {
+ return pos;
+ }
+
+ // -------------------------------------------------------------------
+ // Get size of file
+ size_t FileSize() const {
+ return length;
+ }
+
+ // -------------------------------------------------------------------
+ // Flush file contents
+ void Flush() {
+ ai_assert(false); // won't be needed
+ }
+
+private:
+ const uint8_t* buffer;
+ size_t length,pos;
+ bool own;
+};
+
+// ---------------------------------------------------------------------------
+/** Dummy IO system to read from a memory buffer */
+class MemoryIOSystem : public IOSystem {
+public:
+ /** Constructor. */
+ MemoryIOSystem(const uint8_t* buff, size_t len, IOSystem* io)
+ : buffer(buff)
+ , length(len)
+ , existing_io(io)
+ , created_streams() {
+ // empty
+ }
+
+ /** Destructor. */
+ ~MemoryIOSystem() {
+ }
+
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists(const char* pFile) const override {
+ if (0 == strncmp( pFile, AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH ) ) {
+ return true;
+ }
+ return existing_io ? existing_io->Exists(pFile) : false;
+ }
+
+ // -------------------------------------------------------------------
+ /** Returns the directory separator. */
+ char getOsSeparator() const override {
+ return existing_io ? existing_io->getOsSeparator()
+ : '/'; // why not? it doesn't care
+ }
+
+ // -------------------------------------------------------------------
+ /** Open a new file with a given path. */
+ IOStream* Open(const char* pFile, const char* pMode = "rb") override {
+ if ( 0 == strncmp( pFile, AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH ) ) {
+ created_streams.emplace_back(new MemoryIOStream(buffer, length));
+ return created_streams.back();
+ }
+ return existing_io ? existing_io->Open(pFile, pMode) : NULL;
+ }
+
+ // -------------------------------------------------------------------
+ /** Closes the given file and releases all resources associated with it. */
+ void Close( IOStream* pFile) override {
+ auto it = std::find(created_streams.begin(), created_streams.end(), pFile);
+ if (it != created_streams.end()) {
+ delete pFile;
+ created_streams.erase(it);
+ } else if (existing_io) {
+ existing_io->Close(pFile);
+ }
+ }
+
+ // -------------------------------------------------------------------
+ /** Compare two paths */
+ bool ComparePaths(const char* one, const char* second) const override {
+ return existing_io ? existing_io->ComparePaths(one, second) : false;
+ }
+
+ bool PushDirectory( const std::string &path ) override {
+ return existing_io ? existing_io->PushDirectory(path) : false;
+ }
+
+ const std::string &CurrentDirectory() const override {
+ static std::string empty;
+ return existing_io ? existing_io->CurrentDirectory() : empty;
+ }
+
+ size_t StackSize() const override {
+ return existing_io ? existing_io->StackSize() : 0;
+ }
+
+ bool PopDirectory() override {
+ return existing_io ? existing_io->PopDirectory() : false;
+ }
+
+ bool CreateDirectory( const std::string &path ) override {
+ return existing_io ? existing_io->CreateDirectory(path) : false;
+ }
+
+ bool ChangeDirectory( const std::string &path ) override {
+ return existing_io ? existing_io->ChangeDirectory(path) : false;
+ }
+
+ bool DeleteFile( const std::string &file ) override {
+ return existing_io ? existing_io->DeleteFile(file) : false;
+ }
+
+private:
+ const uint8_t* buffer;
+ size_t length;
+ IOSystem* existing_io;
+ std::vector<IOStream*> created_streams;
+};
+
+} // end namespace Assimp
+
+#endif
diff --git a/thirdparty/assimp/include/assimp/NullLogger.hpp b/thirdparty/assimp/include/assimp/NullLogger.hpp
new file mode 100644
index 0000000000..c45d01bd48
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/NullLogger.hpp
@@ -0,0 +1,99 @@
+/*
+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 NullLogger.hpp
+ * @brief Dummy logger
+*/
+
+#ifndef INCLUDED_AI_NULLLOGGER_H
+#define INCLUDED_AI_NULLLOGGER_H
+
+#include "Logger.hpp"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** @brief CPP-API: Empty logging implementation.
+ *
+ * Does nothing! Used by default if the application hasn't requested a
+ * custom logger via #DefaultLogger::set() or #DefaultLogger::create(); */
+class ASSIMP_API NullLogger
+ : public Logger {
+
+public:
+
+ /** @brief Logs a debug message */
+ void OnDebug(const char* message) {
+ (void)message; //this avoids compiler warnings
+ }
+
+ /** @brief Logs an info message */
+ void OnInfo(const char* message) {
+ (void)message; //this avoids compiler warnings
+ }
+
+ /** @brief Logs a warning message */
+ void OnWarn(const char* message) {
+ (void)message; //this avoids compiler warnings
+ }
+
+ /** @brief Logs an error message */
+ void OnError(const char* message) {
+ (void)message; //this avoids compiler warnings
+ }
+
+ /** @brief Detach a still attached stream from logger */
+ bool attachStream(LogStream *pStream, unsigned int severity) {
+ (void)pStream; (void)severity; //this avoids compiler warnings
+ return false;
+ }
+
+ /** @brief Detach a still attached stream from logger */
+ bool detatchStream(LogStream *pStream, unsigned int severity) {
+ (void)pStream; (void)severity; //this avoids compiler warnings
+ return false;
+ }
+
+private:
+};
+}
+#endif // !! AI_NULLLOGGER_H_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/ParsingUtils.h b/thirdparty/assimp/include/assimp/ParsingUtils.h
new file mode 100644
index 0000000000..ca30ce13b0
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/ParsingUtils.h
@@ -0,0 +1,260 @@
+/*
+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 ParsingUtils.h
+ * @brief Defines helper functions for text parsing
+ */
+#ifndef AI_PARSING_UTILS_H_INC
+#define AI_PARSING_UTILS_H_INC
+
+#include "StringComparison.h"
+#include "StringUtils.h"
+#include <assimp/defs.h>
+
+namespace Assimp {
+
+// NOTE: the functions below are mostly intended as replacement for
+// std::upper, std::lower, std::isupper, std::islower, std::isspace.
+// we don't bother of locales. We don't want them. We want reliable
+// (i.e. identical) results across all locales.
+
+// The functions below accept any character type, but know only
+// about ASCII. However, UTF-32 is the only safe ASCII superset to
+// use since it doesn't have multi-byte sequences.
+
+static const unsigned int BufferSize = 4096;
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+char_t ToLower( char_t in ) {
+ return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in;
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+char_t ToUpper( char_t in) {
+ return (in >= (char_t)'a' && in <= (char_t)'z') ? (char_t)(in-0x20) : in;
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool IsUpper( char_t in) {
+ return (in >= (char_t)'A' && in <= (char_t)'Z');
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool IsLower( char_t in) {
+ return (in >= (char_t)'a' && in <= (char_t)'z');
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool IsSpace( char_t in) {
+ return (in == (char_t)' ' || in == (char_t)'\t');
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool IsLineEnd( char_t in) {
+ return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f');
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool IsSpaceOrNewLine( char_t in) {
+ return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool SkipSpaces( const char_t* in, const char_t** out) {
+ while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) {
+ ++in;
+ }
+ *out = in;
+ return !IsLineEnd<char_t>(*in);
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool SkipSpaces( const char_t** inout) {
+ return SkipSpaces<char_t>(*inout,inout);
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool SkipLine( const char_t* in, const char_t** out) {
+ while( *in != ( char_t )'\r' && *in != ( char_t )'\n' && *in != ( char_t )'\0' ) {
+ ++in;
+ }
+
+ // files are opened in binary mode. Ergo there are both NL and CR
+ while( *in == ( char_t )'\r' || *in == ( char_t )'\n' ) {
+ ++in;
+ }
+ *out = in;
+ return *in != (char_t)'\0';
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool SkipLine( const char_t** inout) {
+ return SkipLine<char_t>(*inout,inout);
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) {
+ while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) {
+ ++in;
+ }
+ *out = in;
+ return *in != '\0';
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool SkipSpacesAndLineEnd( const char_t** inout) {
+ return SkipSpacesAndLineEnd<char_t>(*inout,inout);
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) {
+ if( ( char_t )'\0' == *buffer ) {
+ return false;
+ }
+
+ char* _out = out;
+ char* const end = _out + BufferSize;
+ while( !IsLineEnd( *buffer ) && _out < end ) {
+ *_out++ = *buffer++;
+ }
+ *_out = (char_t)'\0';
+
+ while( IsLineEnd( *buffer ) && '\0' != *buffer ) {
+ ++buffer;
+ }
+
+ return true;
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsNumeric( char_t in)
+{
+ return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in;
+}
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE
+bool TokenMatch(char_t*& in, const char* token, unsigned int len)
+{
+ if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
+ if (in[len] != '\0') {
+ in += len+1;
+ } else {
+ // If EOF after the token make sure we don't go past end of buffer
+ in += len;
+ }
+ return true;
+ }
+
+ return false;
+}
+// ---------------------------------------------------------------------------------
+/** @brief Case-ignoring version of TokenMatch
+ * @param in Input
+ * @param token Token to check for
+ * @param len Number of characters to check
+ */
+AI_FORCE_INLINE
+bool TokenMatchI(const char*& in, const char* token, unsigned int len) {
+ if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
+ in += len+1;
+ return true;
+ }
+ return false;
+}
+
+// ---------------------------------------------------------------------------------
+AI_FORCE_INLINE
+void SkipToken(const char*& in) {
+ SkipSpaces(&in);
+ while ( !IsSpaceOrNewLine( *in ) ) {
+ ++in;
+ }
+}
+
+// ---------------------------------------------------------------------------------
+AI_FORCE_INLINE
+std::string GetNextToken(const char*& in) {
+ SkipSpacesAndLineEnd(&in);
+ const char* cur = in;
+ while ( !IsSpaceOrNewLine( *in ) ) {
+ ++in;
+ }
+ return std::string(cur,(size_t)(in-cur));
+}
+
+// ---------------------------------------------------------------------------------
+
+} // ! namespace Assimp
+
+#endif // ! AI_PARSING_UTILS_H_INC
diff --git a/thirdparty/assimp/include/assimp/Profiler.h b/thirdparty/assimp/include/assimp/Profiler.h
new file mode 100644
index 0000000000..6ff9d41c0a
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Profiler.h
@@ -0,0 +1,99 @@
+/*
+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 Profiler.h
+ * @brief Utility to measure the respective runtime of each import step
+ */
+#ifndef INCLUDED_PROFILER_H
+#define INCLUDED_PROFILER_H
+
+#include <chrono>
+#include <assimp/DefaultLogger.hpp>
+#include "TinyFormatter.h"
+
+#include <map>
+
+namespace Assimp {
+namespace Profiling {
+
+using namespace Formatter;
+
+// ------------------------------------------------------------------------------------------------
+/** Simple wrapper around boost::timer to simplify reporting. Timings are automatically
+ * dumped to the log file.
+ */
+class Profiler {
+public:
+ Profiler() {
+ // empty
+ }
+
+public:
+
+ /** Start a named timer */
+ void BeginRegion(const std::string& region) {
+ regions[region] = std::chrono::system_clock::now();
+ ASSIMP_LOG_DEBUG((format("START `"),region,"`"));
+ }
+
+
+ /** End a specific named timer and write its end time to the log */
+ void EndRegion(const std::string& region) {
+ RegionMap::const_iterator it = regions.find(region);
+ if (it == regions.end()) {
+ return;
+ }
+
+ std::chrono::duration<double> elapsedSeconds = std::chrono::system_clock::now() - regions[region];
+ ASSIMP_LOG_DEBUG((format("END `"),region,"`, dt= ", elapsedSeconds.count()," s"));
+ }
+
+private:
+ typedef std::map<std::string,std::chrono::time_point<std::chrono::system_clock>> RegionMap;
+ RegionMap regions;
+};
+
+}
+}
+
+#endif
+
diff --git a/thirdparty/assimp/include/assimp/ProgressHandler.hpp b/thirdparty/assimp/include/assimp/ProgressHandler.hpp
new file mode 100644
index 0000000000..4e47f1d0a6
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/ProgressHandler.hpp
@@ -0,0 +1,145 @@
+/*
+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'.
+ */
+#pragma once
+#ifndef AI_PROGRESSHANDLER_H_INC
+#define AI_PROGRESSHANDLER_H_INC
+
+#include "types.h"
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------
+/** @brief CPP-API: Abstract interface for custom progress report receivers.
+ *
+ * Each #Importer instance maintains its own #ProgressHandler. The default
+ * implementation provided by Assimp doesn't do anything at all. */
+class ASSIMP_API ProgressHandler
+#ifndef SWIG
+ : public Intern::AllocateFromAssimpHeap
+#endif
+{
+protected:
+ /// @brief Default constructor
+ ProgressHandler () AI_NO_EXCEPT {
+ // empty
+ }
+
+public:
+ /// @brief Virtual destructor.
+ virtual ~ProgressHandler () {
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Progress callback.
+ * @param percentage An estimate of the current loading progress,
+ * in percent. Or -1.f if such an estimate is not available.
+ *
+ * There are restriction on what you may do from within your
+ * implementation of this method: no exceptions may be thrown and no
+ * non-const #Importer methods may be called. It is
+ * not generally possible to predict the number of callbacks
+ * fired during a single import.
+ *
+ * @return Return false to abort loading at the next possible
+ * occasion (loaders and Assimp are generally allowed to perform
+ * all needed cleanup tasks prior to returning control to the
+ * caller). If the loading is aborted, #Importer::ReadFile()
+ * returns always NULL.
+ * */
+ virtual bool Update(float percentage = -1.f) = 0;
+
+ // -------------------------------------------------------------------
+ /** @brief Progress callback for file loading steps
+ * @param numberOfSteps The number of total post-processing
+ * steps
+ * @param currentStep The index of the current post-processing
+ * step that will run, or equal to numberOfSteps if all of
+ * them has finished. This number is always strictly monotone
+ * increasing, although not necessarily linearly.
+ *
+ * @note This is currently only used at the start and the end
+ * of the file parsing.
+ * */
+ virtual void UpdateFileRead(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) {
+ float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f;
+ Update( f * 0.5f );
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Progress callback for post-processing steps
+ * @param numberOfSteps The number of total post-processing
+ * steps
+ * @param currentStep The index of the current post-processing
+ * step that will run, or equal to numberOfSteps if all of
+ * them has finished. This number is always strictly monotone
+ * increasing, although not necessarily linearly.
+ * */
+ virtual void UpdatePostProcess(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) {
+ float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f;
+ Update( f * 0.5f + 0.5f );
+ }
+
+
+ // -------------------------------------------------------------------
+ /** @brief Progress callback for export steps.
+ * @param numberOfSteps The number of total processing
+ * steps
+ * @param currentStep The index of the current post-processing
+ * step that will run, or equal to numberOfSteps if all of
+ * them has finished. This number is always strictly monotone
+ * increasing, although not necessarily linearly.
+ * */
+ virtual void UpdateFileWrite(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) {
+ float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f;
+ Update(f * 0.5f);
+ }
+}; // !class ProgressHandler
+
+// ------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // AI_PROGRESSHANDLER_H_INC
diff --git a/thirdparty/assimp/include/assimp/RemoveComments.h b/thirdparty/assimp/include/assimp/RemoveComments.h
new file mode 100644
index 0000000000..404b496719
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/RemoveComments.h
@@ -0,0 +1,91 @@
+/*
+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 Declares a helper class, "CommentRemover", which can be
+ * used to remove comments (single and multi line) from a text file.
+ */
+#ifndef AI_REMOVE_COMMENTS_H_INC
+#define AI_REMOVE_COMMENTS_H_INC
+
+
+#include <assimp/defs.h>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** \brief Helper class to remove single and multi line comments from a file
+ *
+ * Some mesh formats like MD5 have comments that are quite similar
+ * to those in C or C++ so this code has been moved to a separate
+ * module.
+ */
+class ASSIMP_API CommentRemover
+{
+ // class cannot be instanced
+ CommentRemover() {}
+
+public:
+
+ //! Remove single-line comments. The end of a line is
+ //! expected to be either NL or CR or NLCR.
+ //! \param szComment The start sequence of the comment, e.g. "//"
+ //! \param szBuffer Buffer to work with
+ //! \param chReplacement Character to be used as replacement
+ //! for commented lines. By default this is ' '
+ static void RemoveLineComments(const char* szComment,
+ char* szBuffer, char chReplacement = ' ');
+
+ //! Remove multi-line comments. The end of a line is
+ //! expected to be either NL or CR or NLCR. Multi-line comments
+ //! may not be nested (as in C).
+ //! \param szCommentStart The start sequence of the comment, e.g. "/*"
+ //! \param szCommentEnd The end sequence of the comment, e.g. "*/"
+ //! \param szBuffer Buffer to work with
+ //! \param chReplacement Character to be used as replacement
+ //! for commented lines. By default this is ' '
+ static void RemoveMultiLineComments(const char* szCommentStart,
+ const char* szCommentEnd,char* szBuffer,
+ char chReplacement = ' ');
+};
+} // ! Assimp
+
+#endif // !! AI_REMOVE_COMMENTS_H_INC
diff --git a/thirdparty/assimp/include/assimp/SGSpatialSort.h b/thirdparty/assimp/include/assimp/SGSpatialSort.h
new file mode 100644
index 0000000000..5b4f3f41f2
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/SGSpatialSort.h
@@ -0,0 +1,150 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+/** Small helper classes to optimize finding vertices close to a given location
+ */
+#ifndef AI_D3DSSPATIALSORT_H_INC
+#define AI_D3DSSPATIALSORT_H_INC
+
+#include <assimp/types.h>
+#include <vector>
+#include <stdint.h>
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** Specialized version of SpatialSort to support smoothing groups
+ * This is used in by the 3DS, ASE and LWO loaders. 3DS and ASE share their
+ * normal computation code in SmoothingGroups.inl, the LWO loader has its own
+ * implementation to handle all details of its file format correctly.
+ */
+// ----------------------------------------------------------------------------------
+class ASSIMP_API SGSpatialSort
+{
+public:
+
+ SGSpatialSort();
+
+ // -------------------------------------------------------------------
+ /** Construction from a given face array, handling smoothing groups
+ * properly
+ */
+ explicit SGSpatialSort(const std::vector<aiVector3D>& vPositions);
+
+ // -------------------------------------------------------------------
+ /** Add a vertex to the spatial sort
+ * @param vPosition Vertex position to be added
+ * @param index Index of the vrtex
+ * @param smoothingGroup SmoothingGroup for this vertex
+ */
+ void Add(const aiVector3D& vPosition, unsigned int index,
+ unsigned int smoothingGroup);
+
+ // -------------------------------------------------------------------
+ /** Prepare the spatial sorter for use. This step runs in O(logn)
+ */
+ void Prepare();
+
+ /** Destructor */
+ ~SGSpatialSort();
+
+ // -------------------------------------------------------------------
+ /** Returns an iterator for all positions close to the given position.
+ * @param pPosition The position to look for vertices.
+ * @param pSG Only included vertices with at least one shared smooth group
+ * @param pRadius Maximal distance from the position a vertex may have
+ * to be counted in.
+ * @param poResults The container to store the indices of the found
+ * positions. Will be emptied by the call so it may contain anything.
+ * @param exactMatch Specifies whether smoothing groups are bit masks
+ * (false) or integral values (true). In the latter case, a vertex
+ * cannot belong to more than one smoothing group.
+ * @return An iterator to iterate over all vertices in the given area.
+ */
+ // -------------------------------------------------------------------
+ void FindPositions( const aiVector3D& pPosition, uint32_t pSG,
+ float pRadius, std::vector<unsigned int>& poResults,
+ bool exactMatch = false) const;
+
+protected:
+ /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
+ aiVector3D mPlaneNormal;
+
+ // -------------------------------------------------------------------
+ /** An entry in a spatially sorted position array. Consists of a
+ * vertex index, its position and its pre-calculated distance from
+ * the reference plane */
+ // -------------------------------------------------------------------
+ struct Entry {
+ unsigned int mIndex; ///< The vertex referred by this entry
+ aiVector3D mPosition; ///< Position
+ uint32_t mSmoothGroups;
+ float mDistance; ///< Distance of this vertex to the sorting plane
+
+ Entry() AI_NO_EXCEPT
+ : mIndex(0)
+ , mPosition()
+ , mSmoothGroups(0)
+ , mDistance(0.0f) {
+ // empty
+ }
+
+ Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG)
+ : mIndex( pIndex)
+ , mPosition( pPosition)
+ , mSmoothGroups(pSG)
+ , mDistance( pDistance) {
+ // empty
+ }
+
+ bool operator < (const Entry& e) const {
+ return mDistance < e.mDistance;
+ }
+ };
+
+ // all positions, sorted by distance to the sorting plane
+ std::vector<Entry> mPositions;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SPATIALSORT_H_INC
diff --git a/thirdparty/assimp/include/assimp/SceneCombiner.h b/thirdparty/assimp/include/assimp/SceneCombiner.h
new file mode 100644
index 0000000000..679a2acea4
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/SceneCombiner.h
@@ -0,0 +1,401 @@
+/*
+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 Declares a helper class, "SceneCombiner" providing various
+ * utilities to merge scenes.
+ */
+#ifndef AI_SCENE_COMBINER_H_INC
+#define AI_SCENE_COMBINER_H_INC
+
+#include <assimp/ai_assert.h>
+#include <assimp/types.h>
+#include <assimp/Defines.h>
+#include <stddef.h>
+#include <set>
+#include <list>
+#include <stdint.h>
+
+#include <vector>
+
+struct aiScene;
+struct aiNode;
+struct aiMaterial;
+struct aiTexture;
+struct aiCamera;
+struct aiLight;
+struct aiMetadata;
+struct aiBone;
+struct aiMesh;
+struct aiAnimation;
+struct aiNodeAnim;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** \brief Helper data structure for SceneCombiner.
+ *
+ * Describes to which node a scene must be attached to.
+ */
+struct AttachmentInfo
+{
+ AttachmentInfo()
+ : scene (NULL)
+ , attachToNode (NULL)
+ {}
+
+ AttachmentInfo(aiScene* _scene, aiNode* _attachToNode)
+ : scene (_scene)
+ , attachToNode (_attachToNode)
+ {}
+
+ aiScene* scene;
+ aiNode* attachToNode;
+};
+
+// ---------------------------------------------------------------------------
+struct NodeAttachmentInfo
+{
+ NodeAttachmentInfo()
+ : node (NULL)
+ , attachToNode (NULL)
+ , resolved (false)
+ , src_idx (SIZE_MAX)
+ {}
+
+ NodeAttachmentInfo(aiNode* _scene, aiNode* _attachToNode,size_t idx)
+ : node (_scene)
+ , attachToNode (_attachToNode)
+ , resolved (false)
+ , src_idx (idx)
+ {}
+
+ aiNode* node;
+ aiNode* attachToNode;
+ bool resolved;
+ size_t src_idx;
+};
+
+// ---------------------------------------------------------------------------
+/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES
+ * Generate unique names for all named scene items
+ */
+#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES 0x1
+
+/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES
+ * Generate unique names for materials, too.
+ * This is not absolutely required to pass the validation.
+ */
+#define AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES 0x2
+
+/** @def AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY
+ * Use deep copies of duplicate scenes
+ */
+#define AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY 0x4
+
+/** @def AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS
+ * If attachment nodes are not found in the given master scene,
+ * search the other imported scenes for them in an any order.
+ */
+#define AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS 0x8
+
+/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY
+ * Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES.
+ * Unique names are generated, but only if this is absolutely
+ * required to avoid name conflicts.
+ */
+#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY 0x10
+
+typedef std::pair<aiBone*,unsigned int> BoneSrcIndex;
+
+// ---------------------------------------------------------------------------
+/** @brief Helper data structure for SceneCombiner::MergeBones.
+ */
+struct BoneWithHash : public std::pair<uint32_t,aiString*> {
+ std::vector<BoneSrcIndex> pSrcBones;
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Utility for SceneCombiner
+ */
+struct SceneHelper
+{
+ SceneHelper ()
+ : scene (NULL)
+ , idlen (0)
+ {
+ id[0] = 0;
+ }
+
+ explicit SceneHelper (aiScene* _scene)
+ : scene (_scene)
+ , idlen (0)
+ {
+ id[0] = 0;
+ }
+
+ AI_FORCE_INLINE aiScene* operator-> () const
+ {
+ return scene;
+ }
+
+ // scene we're working on
+ aiScene* scene;
+
+ // prefix to be added to all identifiers in the scene ...
+ char id [32];
+
+ // and its strlen()
+ unsigned int idlen;
+
+ // hash table to quickly check whether a name is contained in the scene
+ std::set<unsigned int> hashes;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Static helper class providing various utilities to merge two
+ * scenes. It is intended as internal utility and NOT for use by
+ * applications.
+ *
+ * The class is currently being used by various postprocessing steps
+ * and loaders (ie. LWS).
+ */
+class ASSIMP_API SceneCombiner {
+ // class cannot be instanced
+ SceneCombiner() {
+ // empty
+ }
+
+ ~SceneCombiner() {
+ // empty
+ }
+
+public:
+ // -------------------------------------------------------------------
+ /** Merges two or more scenes.
+ *
+ * @param dest Receives a pointer to the destination scene. If the
+ * pointer doesn't point to NULL when the function is called, the
+ * existing scene is cleared and refilled.
+ * @param src Non-empty list of scenes to be merged. The function
+ * deletes the input scenes afterwards. There may be duplicate scenes.
+ * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above
+ */
+ static void MergeScenes(aiScene** dest,std::vector<aiScene*>& src,
+ unsigned int flags = 0);
+
+ // -------------------------------------------------------------------
+ /** Merges two or more scenes and attaches all scenes to a specific
+ * position in the node graph of the master scene.
+ *
+ * @param dest Receives a pointer to the destination scene. If the
+ * pointer doesn't point to NULL when the function is called, the
+ * existing scene is cleared and refilled.
+ * @param master Master scene. It will be deleted afterwards. All
+ * other scenes will be inserted in its node graph.
+ * @param src Non-empty list of scenes to be merged along with their
+ * corresponding attachment points in the master scene. The function
+ * deletes the input scenes afterwards. There may be duplicate scenes.
+ * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above
+ */
+ static void MergeScenes(aiScene** dest, aiScene* master,
+ std::vector<AttachmentInfo>& src,
+ unsigned int flags = 0);
+
+ // -------------------------------------------------------------------
+ /** Merges two or more meshes
+ *
+ * The meshes should have equal vertex formats. Only components
+ * that are provided by ALL meshes will be present in the output mesh.
+ * An exception is made for VColors - they are set to black. The
+ * meshes should have the same material indices, too. The output
+ * material index is always the material index of the first mesh.
+ *
+ * @param dest Destination mesh. Must be empty.
+ * @param flags Currently no parameters
+ * @param begin First mesh to be processed
+ * @param end Points to the mesh after the last mesh to be processed
+ */
+ static void MergeMeshes(aiMesh** dest,unsigned int flags,
+ std::vector<aiMesh*>::const_iterator begin,
+ std::vector<aiMesh*>::const_iterator end);
+
+ // -------------------------------------------------------------------
+ /** Merges two or more bones
+ *
+ * @param out Mesh to receive the output bone list
+ * @param flags Currently no parameters
+ * @param begin First mesh to be processed
+ * @param end Points to the mesh after the last mesh to be processed
+ */
+ static void MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it,
+ std::vector<aiMesh*>::const_iterator end);
+
+ // -------------------------------------------------------------------
+ /** Merges two or more materials
+ *
+ * The materials should be complementary as much as possible. In case
+ * of a property present in different materials, the first occurrence
+ * is used.
+ *
+ * @param dest Destination material. Must be empty.
+ * @param begin First material to be processed
+ * @param end Points to the material after the last material to be processed
+ */
+ static void MergeMaterials(aiMaterial** dest,
+ std::vector<aiMaterial*>::const_iterator begin,
+ std::vector<aiMaterial*>::const_iterator end);
+
+ // -------------------------------------------------------------------
+ /** Builds a list of uniquely named bones in a mesh list
+ *
+ * @param asBones Receives the output list
+ * @param it First mesh to be processed
+ * @param end Last mesh to be processed
+ */
+ static void BuildUniqueBoneList(std::list<BoneWithHash>& asBones,
+ std::vector<aiMesh*>::const_iterator it,
+ std::vector<aiMesh*>::const_iterator end);
+
+ // -------------------------------------------------------------------
+ /** Add a name prefix to all nodes in a scene.
+ *
+ * @param Current node. This function is called recursively.
+ * @param prefix Prefix to be added to all nodes
+ * @param len STring length
+ */
+ static void AddNodePrefixes(aiNode* node, const char* prefix,
+ unsigned int len);
+
+ // -------------------------------------------------------------------
+ /** Add an offset to all mesh indices in a node graph
+ *
+ * @param Current node. This function is called recursively.
+ * @param offset Offset to be added to all mesh indices
+ */
+ static void OffsetNodeMeshIndices (aiNode* node, unsigned int offset);
+
+ // -------------------------------------------------------------------
+ /** Attach a list of node graphs to well-defined nodes in a master
+ * graph. This is a helper for MergeScenes()
+ *
+ * @param master Master scene
+ * @param srcList List of source scenes along with their attachment
+ * points. If an attachment point is NULL (or does not exist in
+ * the master graph), a scene is attached to the root of the master
+ * graph (as an additional child node)
+ * @duplicates List of duplicates. If elem[n] == n the scene is not
+ * a duplicate. Otherwise elem[n] links scene n to its first occurrence.
+ */
+ static void AttachToGraph ( aiScene* master,
+ std::vector<NodeAttachmentInfo>& srcList);
+
+ static void AttachToGraph (aiNode* attach,
+ std::vector<NodeAttachmentInfo>& srcList);
+
+
+ // -------------------------------------------------------------------
+ /** Get a deep copy of a scene
+ *
+ * @param dest Receives a pointer to the destination scene
+ * @param src Source scene - remains unmodified.
+ */
+ static void CopyScene(aiScene** dest,const aiScene* source,bool allocate = true);
+
+
+ // -------------------------------------------------------------------
+ /** Get a flat copy of a scene
+ *
+ * Only the first hierarchy layer is copied. All pointer members of
+ * aiScene are shared by source and destination scene. If the
+ * pointer doesn't point to NULL when the function is called, the
+ * existing scene is cleared and refilled.
+ * @param dest Receives a pointer to the destination scene
+ * @param src Source scene - remains unmodified.
+ */
+ static void CopySceneFlat(aiScene** dest,const aiScene* source);
+
+
+ // -------------------------------------------------------------------
+ /** Get a deep copy of a mesh
+ *
+ * @param dest Receives a pointer to the destination mesh
+ * @param src Source mesh - remains unmodified.
+ */
+ static void Copy (aiMesh** dest, const aiMesh* src);
+
+ // similar to Copy():
+ static void Copy (aiMaterial** dest, const aiMaterial* src);
+ static void Copy (aiTexture** dest, const aiTexture* src);
+ static void Copy (aiAnimation** dest, const aiAnimation* src);
+ static void Copy (aiCamera** dest, const aiCamera* src);
+ static void Copy (aiBone** dest, const aiBone* src);
+ static void Copy (aiLight** dest, const aiLight* src);
+ static void Copy (aiNodeAnim** dest, const aiNodeAnim* src);
+ static void Copy (aiMetadata** dest, const aiMetadata* src);
+
+ // recursive, of course
+ static void Copy (aiNode** dest, const aiNode* src);
+
+
+private:
+
+ // -------------------------------------------------------------------
+ // Same as AddNodePrefixes, but with an additional check
+ static void AddNodePrefixesChecked(aiNode* node, const char* prefix,
+ unsigned int len,
+ std::vector<SceneHelper>& input,
+ unsigned int cur);
+
+ // -------------------------------------------------------------------
+ // Add node identifiers to a hashing set
+ static void AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes);
+
+
+ // -------------------------------------------------------------------
+ // Search for duplicate names
+ static bool FindNameMatch(const aiString& name,
+ std::vector<SceneHelper>& input, unsigned int cur);
+};
+
+}
+
+#endif // !! AI_SCENE_COMBINER_H_INC
diff --git a/thirdparty/assimp/include/assimp/SkeletonMeshBuilder.h b/thirdparty/assimp/include/assimp/SkeletonMeshBuilder.h
new file mode 100644
index 0000000000..f9b8d9f55c
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/SkeletonMeshBuilder.h
@@ -0,0 +1,125 @@
+/** Helper class to construct a dummy mesh for file formats containing only motion data */
+
+/*
+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.h
+ * Declares SkeletonMeshBuilder, a tiny utility to build dummy meshes
+ * for animation skeletons.
+ */
+
+#ifndef AI_SKELETONMESHBUILDER_H_INC
+#define AI_SKELETONMESHBUILDER_H_INC
+
+#include <vector>
+#include <assimp/mesh.h>
+
+struct aiMaterial;
+struct aiScene;
+struct aiNode;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/**
+ * This little helper class constructs a dummy mesh for a given scene
+ * the resembles the node hierarchy. This is useful for file formats
+ * that don't carry any mesh data but only animation data.
+ */
+class ASSIMP_API SkeletonMeshBuilder
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** The constructor processes the given scene and adds a mesh there.
+ *
+ * Does nothing if the scene already has mesh data.
+ * @param pScene The scene for which a skeleton mesh should be constructed.
+ * @param root The node to start with. NULL is the scene root
+ * @param bKnobsOnly Set this to true if you don't want the connectors
+ * between the knobs representing the nodes.
+ */
+ SkeletonMeshBuilder( aiScene* pScene, aiNode* root = NULL,
+ bool bKnobsOnly = false);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Recursively builds a simple mesh representation for the given node
+ * and also creates a joint for the node that affects this part of
+ * the mesh.
+ * @param pNode The node to build geometry for.
+ */
+ void CreateGeometry( const aiNode* pNode);
+
+ // -------------------------------------------------------------------
+ /** Creates the mesh from the internally accumulated stuff and returns it.
+ */
+ aiMesh* CreateMesh();
+
+ // -------------------------------------------------------------------
+ /** Creates a dummy material and returns it. */
+ aiMaterial* CreateMaterial();
+
+protected:
+ /** space to assemble the mesh data: points */
+ std::vector<aiVector3D> mVertices;
+
+ /** faces */
+ struct Face
+ {
+ unsigned int mIndices[3];
+ Face();
+ Face( unsigned int p0, unsigned int p1, unsigned int p2)
+ { mIndices[0] = p0; mIndices[1] = p1; mIndices[2] = p2; }
+ };
+ std::vector<Face> mFaces;
+
+ /** bones */
+ std::vector<aiBone*> mBones;
+
+ bool mKnobsOnly;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SKELETONMESHBUILDER_H_INC
diff --git a/thirdparty/assimp/include/assimp/SmoothingGroups.h b/thirdparty/assimp/include/assimp/SmoothingGroups.h
new file mode 100644
index 0000000000..92d65cea02
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/SmoothingGroups.h
@@ -0,0 +1,108 @@
+/*
+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 the helper data structures for importing 3DS files.
+http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
+
+#ifndef AI_SMOOTHINGGROUPS_H_INC
+#define AI_SMOOTHINGGROUPS_H_INC
+
+#include <assimp/vector3.h>
+#include <stdint.h>
+#include <vector>
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a face with smoothing groups assigned */
+struct FaceWithSmoothingGroup {
+ FaceWithSmoothingGroup() AI_NO_EXCEPT
+ : mIndices()
+ , iSmoothGroup(0) {
+ // in debug builds set all indices to a common magic value
+#ifdef ASSIMP_BUILD_DEBUG
+ this->mIndices[0] = 0xffffffff;
+ this->mIndices[1] = 0xffffffff;
+ this->mIndices[2] = 0xffffffff;
+#endif
+ }
+
+
+ //! Indices. .3ds is using uint16. However, after
+ //! an unique vertex set has been generated,
+ //! individual index values might exceed 2^16
+ uint32_t mIndices[3];
+
+ //! specifies to which smoothing group the face belongs to
+ uint32_t iSmoothGroup;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a mesh whose faces have smoothing
+ groups assigned. This allows us to reuse the code for normal computations
+ from smoothings groups for several loaders (3DS, ASE). All of them
+ use face structures which inherit from #FaceWithSmoothingGroup,
+ but as they add extra members and need to be copied by value we
+ need to use a template here.
+ */
+template <class T>
+struct MeshWithSmoothingGroups
+{
+ //! Vertex positions
+ std::vector<aiVector3D> mPositions;
+
+ //! Face lists
+ std::vector<T> mFaces;
+
+ //! List of normal vectors
+ std::vector<aiVector3D> mNormals;
+};
+
+// ---------------------------------------------------------------------------
+/** Computes normal vectors for the mesh
+ */
+template <class T>
+void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh);
+
+
+// include implementations
+#include "SmoothingGroups.inl"
+
+#endif // !! AI_SMOOTHINGGROUPS_H_INC
diff --git a/thirdparty/assimp/include/assimp/SmoothingGroups.inl b/thirdparty/assimp/include/assimp/SmoothingGroups.inl
new file mode 100644
index 0000000000..84ea4a1b00
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/SmoothingGroups.inl
@@ -0,0 +1,138 @@
+/*
+---------------------------------------------------------------------------
+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 Generation of normal vectors basing on smoothing groups */
+
+#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED
+#define AI_SMOOTHINGGROUPS_INL_INCLUDED
+
+// internal headers
+#include <assimp/SGSpatialSort.h>
+
+// CRT header
+#include <algorithm>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
+{
+ // First generate face normals
+ sMesh.mNormals.resize(sMesh.mPositions.size(),aiVector3D());
+ for( unsigned int a = 0; a < sMesh.mFaces.size(); a++)
+ {
+ T& face = sMesh.mFaces[a];
+
+ aiVector3D* pV1 = &sMesh.mPositions[face.mIndices[0]];
+ aiVector3D* pV2 = &sMesh.mPositions[face.mIndices[1]];
+ aiVector3D* pV3 = &sMesh.mPositions[face.mIndices[2]];
+
+ aiVector3D pDelta1 = *pV2 - *pV1;
+ aiVector3D pDelta2 = *pV3 - *pV1;
+ aiVector3D vNor = pDelta1 ^ pDelta2;
+
+ for (unsigned int c = 0; c < 3;++c)
+ sMesh.mNormals[face.mIndices[c]] = vNor;
+ }
+
+ // calculate the position bounds so we have a reliable epsilon to check position differences against
+ aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
+ for( unsigned int a = 0; a < sMesh.mPositions.size(); a++)
+ {
+ minVec.x = std::min( minVec.x, sMesh.mPositions[a].x);
+ minVec.y = std::min( minVec.y, sMesh.mPositions[a].y);
+ minVec.z = std::min( minVec.z, sMesh.mPositions[a].z);
+ maxVec.x = std::max( maxVec.x, sMesh.mPositions[a].x);
+ maxVec.y = std::max( maxVec.y, sMesh.mPositions[a].y);
+ maxVec.z = std::max( maxVec.z, sMesh.mPositions[a].z);
+ }
+ const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
+ std::vector<aiVector3D> avNormals;
+ avNormals.resize(sMesh.mNormals.size());
+
+ // now generate the spatial sort tree
+ SGSpatialSort sSort;
+ for( typename std::vector<T>::iterator i = sMesh.mFaces.begin();
+ i != sMesh.mFaces.end();++i)
+ {
+ for (unsigned int c = 0; c < 3;++c)
+ sSort.Add(sMesh.mPositions[(*i).mIndices[c]],(*i).mIndices[c],(*i).iSmoothGroup);
+ }
+ sSort.Prepare();
+
+ std::vector<bool> vertexDone(sMesh.mPositions.size(),false);
+ for( typename std::vector<T>::iterator i = sMesh.mFaces.begin();
+ i != sMesh.mFaces.end();++i)
+ {
+ std::vector<unsigned int> poResult;
+ for (unsigned int c = 0; c < 3;++c)
+ {
+ unsigned int idx = (*i).mIndices[c];
+ if (vertexDone[idx])continue;
+
+ sSort.FindPositions(sMesh.mPositions[idx],(*i).iSmoothGroup,
+ posEpsilon,poResult);
+
+ aiVector3D vNormals;
+ for (std::vector<unsigned int>::const_iterator
+ a = poResult.begin();
+ a != poResult.end();++a)
+ {
+ vNormals += sMesh.mNormals[(*a)];
+ }
+ vNormals.NormalizeSafe();
+
+ // write back into all affected normals
+ for (std::vector<unsigned int>::const_iterator
+ a = poResult.begin();
+ a != poResult.end();++a)
+ {
+ idx = *a;
+ avNormals [idx] = vNormals;
+ vertexDone[idx] = true;
+ }
+ }
+ }
+ sMesh.mNormals = avNormals;
+}
+
+#endif // !! AI_SMOOTHINGGROUPS_INL_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/SpatialSort.h b/thirdparty/assimp/include/assimp/SpatialSort.h
new file mode 100644
index 0000000000..61b345bcbf
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/SpatialSort.h
@@ -0,0 +1,174 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+/** Small helper classes to optimise finding vertizes close to a given location */
+#ifndef AI_SPATIALSORT_H_INC
+#define AI_SPATIALSORT_H_INC
+
+#include <vector>
+#include <assimp/types.h>
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+/** A little helper class to quickly find all vertices in the epsilon environment of a given
+ * position. Construct an instance with an array of positions. The class stores the given positions
+ * by their indices and sorts them by their distance to an arbitrary chosen plane.
+ * You can then query the instance for all vertices close to a given position in an average O(log n)
+ * time, with O(n) worst case complexity when all vertices lay on the plane. The plane is chosen
+ * so that it avoids common planes in usual data sets. */
+// ------------------------------------------------------------------------------------------------
+class ASSIMP_API SpatialSort
+{
+public:
+
+ SpatialSort();
+
+ // ------------------------------------------------------------------------------------
+ /** Constructs a spatially sorted representation from the given position array.
+ * Supply the positions in its layout in memory, the class will only refer to them
+ * by index.
+ * @param pPositions Pointer to the first position vector of the array.
+ * @param pNumPositions Number of vectors to expect in that array.
+ * @param pElementOffset Offset in bytes from the beginning of one vector in memory
+ * to the beginning of the next vector. */
+ SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset);
+
+ /** Destructor */
+ ~SpatialSort();
+
+public:
+
+ // ------------------------------------------------------------------------------------
+ /** Sets the input data for the SpatialSort. This replaces existing data, if any.
+ * The new data receives new indices in ascending order.
+ *
+ * @param pPositions Pointer to the first position vector of the array.
+ * @param pNumPositions Number of vectors to expect in that array.
+ * @param pElementOffset Offset in bytes from the beginning of one vector in memory
+ * to the beginning of the next vector.
+ * @param pFinalize Specifies whether the SpatialSort's internal representation
+ * is finalized after the new data has been added. Finalization is
+ * required in order to use #FindPosition() or #GenerateMappingTable().
+ * If you don't finalize yet, you can use #Append() to add data from
+ * other sources.*/
+ void Fill( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset,
+ bool pFinalize = true);
+
+
+ // ------------------------------------------------------------------------------------
+ /** Same as #Fill(), except the method appends to existing data in the #SpatialSort. */
+ void Append( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset,
+ bool pFinalize = true);
+
+
+ // ------------------------------------------------------------------------------------
+ /** Finalize the spatial hash data structure. This can be useful after
+ * multiple calls to #Append() with the pFinalize parameter set to false.
+ * This is finally required before one of #FindPositions() and #GenerateMappingTable()
+ * can be called to query the spatial sort.*/
+ void Finalize();
+
+ // ------------------------------------------------------------------------------------
+ /** Returns an iterator for all positions close to the given position.
+ * @param pPosition The position to look for vertices.
+ * @param pRadius Maximal distance from the position a vertex may have to be counted in.
+ * @param poResults The container to store the indices of the found positions.
+ * Will be emptied by the call so it may contain anything.
+ * @return An iterator to iterate over all vertices in the given area.*/
+ void FindPositions( const aiVector3D& pPosition, ai_real pRadius,
+ std::vector<unsigned int>& poResults) const;
+
+ // ------------------------------------------------------------------------------------
+ /** 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.
+ * @param pPosition The position to look for vertices.
+ * @param poResults The container to store the indices of the found positions.
+ * Will be emptied by the call so it may contain anything.*/
+ void FindIdenticalPositions( const aiVector3D& pPosition,
+ std::vector<unsigned int>& poResults) const;
+
+ // ------------------------------------------------------------------------------------
+ /** Compute a table that maps each vertex ID referring to a spatially close
+ * enough position to the same output ID. Output IDs are assigned in ascending order
+ * from 0...n.
+ * @param fill Will be filled with numPositions entries.
+ * @param pRadius Maximal distance from the position a vertex may have to
+ * be counted in.
+ * @return Number of unique vertices (n). */
+ unsigned int GenerateMappingTable(std::vector<unsigned int>& fill,
+ ai_real pRadius) const;
+
+protected:
+ /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
+ aiVector3D mPlaneNormal;
+
+ /** An entry in a spatially sorted position array. Consists of a vertex index,
+ * its position and its pre-calculated distance from the reference plane */
+ struct Entry {
+ unsigned int mIndex; ///< The vertex referred by this entry
+ aiVector3D mPosition; ///< Position
+ ai_real mDistance; ///< Distance of this vertex to the sorting plane
+
+ Entry() AI_NO_EXCEPT
+ : mIndex( 999999999 ), mPosition(), mDistance( 99999. ) {
+ // empty
+ }
+ Entry( unsigned int pIndex, const aiVector3D& pPosition, ai_real pDistance)
+ : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance) {
+ // empty
+ }
+
+ bool operator < (const Entry& e) const { return mDistance < e.mDistance; }
+ };
+
+ // all positions, sorted by distance to the sorting plane
+ std::vector<Entry> mPositions;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SPATIALSORT_H_INC
diff --git a/thirdparty/assimp/include/assimp/StandardShapes.h b/thirdparty/assimp/include/assimp/StandardShapes.h
new file mode 100644
index 0000000000..3791569b83
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/StandardShapes.h
@@ -0,0 +1,200 @@
+/*
+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 Declares a helper class, "StandardShapes" which generates
+ * vertices for standard shapes, such as cylnders, cones, spheres ..
+ */
+#ifndef AI_STANDARD_SHAPES_H_INC
+#define AI_STANDARD_SHAPES_H_INC
+
+#include <assimp/vector3.h>
+#include <vector>
+
+struct aiMesh;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** \brief Helper class to generate vertex buffers for standard geometric
+ * shapes, such as cylinders, cones, boxes, spheres, elipsoids ... .
+ */
+class ASSIMP_API StandardShapes
+{
+ // class cannot be instanced
+ StandardShapes() {}
+
+public:
+
+
+ // ----------------------------------------------------------------
+ /** Generates a mesh from an array of vertex positions.
+ *
+ * @param positions List of vertex positions
+ * @param numIndices Number of indices per primitive
+ * @return Output mesh
+ */
+ static aiMesh* MakeMesh(const std::vector<aiVector3D>& positions,
+ unsigned int numIndices);
+
+
+ static aiMesh* MakeMesh ( unsigned int (*GenerateFunc)
+ (std::vector<aiVector3D>&));
+
+ static aiMesh* MakeMesh ( unsigned int (*GenerateFunc)
+ (std::vector<aiVector3D>&, bool));
+
+ static aiMesh* MakeMesh ( unsigned int n, void (*GenerateFunc)
+ (unsigned int,std::vector<aiVector3D>&));
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a hexahedron (cube)
+ *
+ * Hexahedrons can be scaled on all axes.
+ * @param positions Receives output triangles.
+ * @param polygons If you pass true here quads will be returned
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeHexahedron(
+ std::vector<aiVector3D>& positions,
+ bool polygons = false);
+
+ // ----------------------------------------------------------------
+ /** @brief Generates an icosahedron
+ *
+ * @param positions Receives output triangles.
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeIcosahedron(
+ std::vector<aiVector3D>& positions);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a dodecahedron
+ *
+ * @param positions Receives output triangles
+ * @param polygons If you pass true here pentagons will be returned
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeDodecahedron(
+ std::vector<aiVector3D>& positions,
+ bool polygons = false);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates an octahedron
+ *
+ * @param positions Receives output triangles.
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeOctahedron(
+ std::vector<aiVector3D>& positions);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a tetrahedron
+ *
+ * @param positions Receives output triangles.
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeTetrahedron(
+ std::vector<aiVector3D>& positions);
+
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a sphere
+ *
+ * @param tess Number of subdivions - 0 generates a octahedron
+ * @param positions Receives output triangles.
+ */
+ static void MakeSphere(unsigned int tess,
+ std::vector<aiVector3D>& positions);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a cone or a cylinder, either open or closed.
+ *
+ * @code
+ *
+ * |-----| <- radius 1
+ *
+ * __x__ <- ] ^
+ * / \ | height |
+ * / \ | Y
+ * / \ |
+ * / \ |
+ * /______x______\ <- ] <- end cap
+ *
+ * |-------------| <- radius 2
+ *
+ * @endcode
+ *
+ * @param height Height of the cone
+ * @param radius1 First radius
+ * @param radius2 Second radius
+ * @param tess Number of triangles.
+ * @param bOpened true for an open cone/cylinder. An open shape has
+ * no 'end caps'
+ * @param positions Receives output triangles
+ */
+ static void MakeCone(ai_real height,ai_real radius1,
+ ai_real radius2,unsigned int tess,
+ std::vector<aiVector3D>& positions,bool bOpen= false);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a flat circle
+ *
+ * The circle is constructed in the planned formed by the x,z
+ * axes of the cartesian coordinate system.
+ *
+ * @param radius Radius of the circle
+ * @param tess Number of segments.
+ * @param positions Receives output triangles.
+ */
+ static void MakeCircle(ai_real radius, unsigned int tess,
+ std::vector<aiVector3D>& positions);
+
+};
+} // ! Assimp
+
+#endif // !! AI_STANDARD_SHAPES_H_INC
diff --git a/thirdparty/assimp/include/assimp/StreamReader.h b/thirdparty/assimp/include/assimp/StreamReader.h
new file mode 100644
index 0000000000..9116c14261
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/StreamReader.h
@@ -0,0 +1,343 @@
+/*
+---------------------------------------------------------------------------
+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 the StreamReader class which reads data from
+ * a binary stream with a well-defined endianness.
+ */
+
+#ifndef AI_STREAMREADER_H_INCLUDED
+#define AI_STREAMREADER_H_INCLUDED
+
+#include <assimp/IOStream.hpp>
+#include <assimp/Defines.h>
+
+#include "ByteSwapper.h"
+#include "Exceptional.h"
+#include <memory>
+
+namespace Assimp {
+
+// --------------------------------------------------------------------------------------------
+/** Wrapper class around IOStream to allow for consistent reading of binary data in both
+ * little and big endian format. Don't attempt to instance the template directly. Use
+ * StreamReaderLE to read from a little-endian stream and StreamReaderBE to read from a
+ * BE stream. The class expects that the endianness of any input data is known at
+ * compile-time, which should usually be true (#BaseImporter::ConvertToUTF8 implements
+ * runtime endianness conversions for text files).
+ *
+ * XXX switch from unsigned int for size types to size_t? or ptrdiff_t?*/
+// --------------------------------------------------------------------------------------------
+template <bool SwapEndianess = false, bool RuntimeSwitch = false>
+class StreamReader {
+public:
+ // FIXME: use these data types throughout the whole library,
+ // then change them to 64 bit values :-)
+ using diff = int;
+ using pos = unsigned int;
+
+ // ---------------------------------------------------------------------
+ /** Construction from a given stream with a well-defined endianness.
+ *
+ * The StreamReader holds a permanent strong reference to the
+ * stream, which is released upon destruction.
+ * @param stream Input stream. The stream is not restarted if
+ * its file pointer is not at 0. Instead, the stream reader
+ * reads from the current position to the end of the stream.
+ * @param le If @c RuntimeSwitch is true: specifies whether the
+ * stream is in little endian byte order. Otherwise the
+ * endianness information is contained in the @c SwapEndianess
+ * template parameter and this parameter is meaningless. */
+ StreamReader(std::shared_ptr<IOStream> stream, bool le = false)
+ : stream(stream)
+ , le(le)
+ {
+ ai_assert(stream);
+ InternBegin();
+ }
+
+ // ---------------------------------------------------------------------
+ StreamReader(IOStream* stream, bool le = false)
+ : stream(std::shared_ptr<IOStream>(stream))
+ , le(le)
+ {
+ ai_assert(stream);
+ InternBegin();
+ }
+
+ // ---------------------------------------------------------------------
+ ~StreamReader() {
+ delete[] buffer;
+ }
+
+ // deprecated, use overloaded operator>> instead
+
+ // ---------------------------------------------------------------------
+ /** Read a float from the stream */
+ float GetF4()
+ {
+ return Get<float>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a double from the stream */
+ double GetF8() {
+ return Get<double>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a signed 16 bit integer from the stream */
+ int16_t GetI2() {
+ return Get<int16_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a signed 8 bit integer from the stream */
+ int8_t GetI1() {
+ return Get<int8_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read an signed 32 bit integer from the stream */
+ int32_t GetI4() {
+ return Get<int32_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a signed 64 bit integer from the stream */
+ int64_t GetI8() {
+ return Get<int64_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a unsigned 16 bit integer from the stream */
+ uint16_t GetU2() {
+ return Get<uint16_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a unsigned 8 bit integer from the stream */
+ uint8_t GetU1() {
+ return Get<uint8_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read an unsigned 32 bit integer from the stream */
+ uint32_t GetU4() {
+ return Get<uint32_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a unsigned 64 bit integer from the stream */
+ uint64_t GetU8() {
+ return Get<uint64_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the remaining stream size (to the end of the stream) */
+ unsigned int GetRemainingSize() const {
+ return (unsigned int)(end - current);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the remaining stream size (to the current read limit). The
+ * return value is the remaining size of the stream if no custom
+ * read limit has been set. */
+ unsigned int GetRemainingSizeToLimit() const {
+ return (unsigned int)(limit - current);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Increase the file pointer (relative seeking) */
+ void IncPtr(intptr_t plus) {
+ current += plus;
+ if (current > limit) {
+ throw DeadlyImportError("End of file or read limit was reached");
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the current file pointer */
+ int8_t* GetPtr() const {
+ return current;
+ }
+
+ // ---------------------------------------------------------------------
+ /** Set current file pointer (Get it from #GetPtr). This is if you
+ * prefer to do pointer arithmetics on your own or want to copy
+ * large chunks of data at once.
+ * @param p The new pointer, which is validated against the size
+ * limit and buffer boundaries. */
+ void SetPtr(int8_t* p) {
+ current = p;
+ if (current > limit || current < buffer) {
+ throw DeadlyImportError("End of file or read limit was reached");
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ /** Copy n bytes to an external buffer
+ * @param out Destination for copying
+ * @param bytes Number of bytes to copy */
+ void CopyAndAdvance(void* out, size_t bytes) {
+ int8_t* ur = GetPtr();
+ SetPtr(ur+bytes); // fire exception if eof
+
+ ::memcpy(out,ur,bytes);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the current offset from the beginning of the file */
+ int GetCurrentPos() const {
+ return (unsigned int)(current - buffer);
+ }
+
+ void SetCurrentPos(size_t pos) {
+ SetPtr(buffer + pos);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Setup a temporary read limit
+ *
+ * @param limit Maximum number of bytes to be read from
+ * the beginning of the file. Specifying UINT_MAX
+ * resets the limit to the original end of the stream.
+ * Returns the previously set limit. */
+ unsigned int SetReadLimit(unsigned int _limit) {
+ unsigned int prev = GetReadLimit();
+ if (UINT_MAX == _limit) {
+ limit = end;
+ return prev;
+ }
+
+ limit = buffer + _limit;
+ if (limit > end) {
+ throw DeadlyImportError("StreamReader: Invalid read limit");
+ }
+ return prev;
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the current read limit in bytes. Reading over this limit
+ * accidentally raises an exception. */
+ unsigned int GetReadLimit() const {
+ return (unsigned int)(limit - buffer);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Skip to the read limit in bytes. Reading over this limit
+ * accidentally raises an exception. */
+ void SkipToReadLimit() {
+ current = limit;
+ }
+
+ // ---------------------------------------------------------------------
+ /** overload operator>> and allow chaining of >> ops. */
+ template <typename T>
+ StreamReader& operator >> (T& f) {
+ f = Get<T>();
+ return *this;
+ }
+
+ // ---------------------------------------------------------------------
+ /** Generic read method. ByteSwap::Swap(T*) *must* be defined */
+ template <typename T>
+ T Get() {
+ if ( current + sizeof(T) > limit) {
+ throw DeadlyImportError("End of file or stream limit was reached");
+ }
+
+ T f;
+ ::memcpy (&f, current, sizeof(T));
+ Intern::Getter<SwapEndianess,T,RuntimeSwitch>() (&f,le);
+ current += sizeof(T);
+
+ return f;
+ }
+
+private:
+ // ---------------------------------------------------------------------
+ void InternBegin() {
+ if (!stream) {
+ // in case someone wonders: StreamReader is frequently invoked with
+ // no prior validation whether the input stream is valid. Since
+ // no one bothers changing the error message, this message here
+ // is passed down to the caller and 'unable to open file'
+ // simply describes best what happened.
+ throw DeadlyImportError("StreamReader: Unable to open file");
+ }
+
+ const size_t s = stream->FileSize() - stream->Tell();
+ if (!s) {
+ throw DeadlyImportError("StreamReader: File is empty or EOF is already reached");
+ }
+
+ current = buffer = new int8_t[s];
+ const size_t read = stream->Read(current,1,s);
+ // (read < s) can only happen if the stream was opened in text mode, in which case FileSize() is not reliable
+ ai_assert(read <= s);
+ end = limit = &buffer[read-1] + 1;
+ }
+
+private:
+ std::shared_ptr<IOStream> stream;
+ int8_t *buffer, *current, *end, *limit;
+ bool le;
+};
+
+// --------------------------------------------------------------------------------------------
+// `static` StreamReaders. Their byte order is fixed and they might be a little bit faster.
+#ifdef AI_BUILD_BIG_ENDIAN
+ typedef StreamReader<true> StreamReaderLE;
+ typedef StreamReader<false> StreamReaderBE;
+#else
+ typedef StreamReader<true> StreamReaderBE;
+ typedef StreamReader<false> StreamReaderLE;
+#endif
+
+// `dynamic` StreamReader. The byte order of the input data is specified in the
+// c'tor. This involves runtime branching and might be a little bit slower.
+typedef StreamReader<true,true> StreamReaderAny;
+
+} // end namespace Assimp
+
+#endif // !! AI_STREAMREADER_H_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/StreamWriter.h b/thirdparty/assimp/include/assimp/StreamWriter.h
new file mode 100644
index 0000000000..c7cf6c0d74
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/StreamWriter.h
@@ -0,0 +1,303 @@
+/*
+---------------------------------------------------------------------------
+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 the StreamWriter class which writes data to
+ * a binary stream with a well-defined endianness. */
+
+#ifndef AI_STREAMWRITER_H_INCLUDED
+#define AI_STREAMWRITER_H_INCLUDED
+
+#include "ByteSwapper.h"
+#include <assimp/IOStream.hpp>
+
+#include <memory>
+#include <vector>
+
+namespace Assimp {
+
+// --------------------------------------------------------------------------------------------
+/** Wrapper class around IOStream to allow for consistent writing of binary data in both
+ * little and big endian format. Don't attempt to instance the template directly. Use
+ * StreamWriterLE to write to a little-endian stream and StreamWriterBE to write to a
+ * BE stream. Alternatively, there is StreamWriterAny if the endianness of the output
+ * stream is to be determined at runtime.
+ */
+// --------------------------------------------------------------------------------------------
+template <bool SwapEndianess = false, bool RuntimeSwitch = false>
+class StreamWriter
+{
+ enum {
+ INITIAL_CAPACITY = 1024
+ };
+
+public:
+
+ // ---------------------------------------------------------------------
+ /** Construction from a given stream with a well-defined endianness.
+ *
+ * The StreamReader holds a permanent strong reference to the
+ * stream, which is released upon destruction.
+ * @param stream Input stream. The stream is not re-seeked and writing
+ continues at the current position of the stream cursor.
+ * @param le If @c RuntimeSwitch is true: specifies whether the
+ * stream is in little endian byte order. Otherwise the
+ * endianness information is defined by the @c SwapEndianess
+ * template parameter and this parameter is meaningless. */
+ StreamWriter(std::shared_ptr<IOStream> stream, bool le = false)
+ : stream(stream)
+ , le(le)
+ , cursor()
+ {
+ ai_assert(stream);
+ buffer.reserve(INITIAL_CAPACITY);
+ }
+
+ // ---------------------------------------------------------------------
+ StreamWriter(IOStream* stream, bool le = false)
+ : stream(std::shared_ptr<IOStream>(stream))
+ , le(le)
+ , cursor()
+ {
+ ai_assert(stream);
+ buffer.reserve(INITIAL_CAPACITY);
+ }
+
+ // ---------------------------------------------------------------------
+ ~StreamWriter() {
+ stream->Write(buffer.data(), 1, buffer.size());
+ stream->Flush();
+ }
+
+public:
+
+ // ---------------------------------------------------------------------
+ /** Flush the contents of the internal buffer, and the output IOStream */
+ void Flush()
+ {
+ stream->Write(buffer.data(), 1, buffer.size());
+ stream->Flush();
+ buffer.clear();
+ cursor = 0;
+ }
+
+ // ---------------------------------------------------------------------
+ /** Seek to the given offset / origin in the output IOStream.
+ *
+ * Flushes the internal buffer and the output IOStream prior to seeking. */
+ aiReturn Seek(size_t pOffset, aiOrigin pOrigin=aiOrigin_SET)
+ {
+ Flush();
+ return stream->Seek(pOffset, pOrigin);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Tell the current position in the output IOStream.
+ *
+ * First flushes the internal buffer and the output IOStream. */
+ size_t Tell()
+ {
+ Flush();
+ return stream->Tell();
+ }
+
+public:
+
+ // ---------------------------------------------------------------------
+ /** Write a float to the stream */
+ void PutF4(float f)
+ {
+ Put(f);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a double to the stream */
+ void PutF8(double d) {
+ Put(d);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a signed 16 bit integer to the stream */
+ void PutI2(int16_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a signed 8 bit integer to the stream */
+ void PutI1(int8_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write an signed 32 bit integer to the stream */
+ void PutI4(int32_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a signed 64 bit integer to the stream */
+ void PutI8(int64_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a unsigned 16 bit integer to the stream */
+ void PutU2(uint16_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a unsigned 8 bit integer to the stream */
+ void PutU1(uint8_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write an unsigned 32 bit integer to the stream */
+ void PutU4(uint32_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a unsigned 64 bit integer to the stream */
+ void PutU8(uint64_t n) {
+ Put(n);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a single character to the stream */
+ void PutChar(char c) {
+ Put(c);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write an aiString to the stream */
+ void PutString(const aiString& s)
+ {
+ // as Put(T f) below
+ if (cursor + s.length >= buffer.size()) {
+ buffer.resize(cursor + s.length);
+ }
+ void* dest = &buffer[cursor];
+ ::memcpy(dest, s.C_Str(), s.length);
+ cursor += s.length;
+ }
+
+ // ---------------------------------------------------------------------
+ /** Write a std::string to the stream */
+ void PutString(const std::string& s)
+ {
+ // as Put(T f) below
+ if (cursor + s.size() >= buffer.size()) {
+ buffer.resize(cursor + s.size());
+ }
+ void* dest = &buffer[cursor];
+ ::memcpy(dest, s.c_str(), s.size());
+ cursor += s.size();
+ }
+
+public:
+
+ // ---------------------------------------------------------------------
+ /** overload operator<< and allow chaining of MM ops. */
+ template <typename T>
+ StreamWriter& operator << (T f) {
+ Put(f);
+ return *this;
+ }
+
+ // ---------------------------------------------------------------------
+ std::size_t GetCurrentPos() const {
+ return cursor;
+ }
+
+ // ---------------------------------------------------------------------
+ void SetCurrentPos(std::size_t new_cursor) {
+ cursor = new_cursor;
+ }
+
+ // ---------------------------------------------------------------------
+ /** Generic write method. ByteSwap::Swap(T*) *must* be defined */
+ template <typename T>
+ void Put(T f) {
+ Intern :: Getter<SwapEndianess,T,RuntimeSwitch>() (&f, le);
+
+ if (cursor + sizeof(T) >= buffer.size()) {
+ buffer.resize(cursor + sizeof(T));
+ }
+
+ void* dest = &buffer[cursor];
+
+ // reinterpret_cast + assignment breaks strict aliasing rules
+ // and generally causes trouble on platforms such as ARM that
+ // do not silently ignore alignment faults.
+ ::memcpy(dest, &f, sizeof(T));
+ cursor += sizeof(T);
+ }
+
+private:
+
+ std::shared_ptr<IOStream> stream;
+ bool le;
+
+ std::vector<uint8_t> buffer;
+ std::size_t cursor;
+};
+
+
+// --------------------------------------------------------------------------------------------
+// `static` StreamWriter. Their byte order is fixed and they might be a little bit faster.
+#ifdef AI_BUILD_BIG_ENDIAN
+ typedef StreamWriter<true> StreamWriterLE;
+ typedef StreamWriter<false> StreamWriterBE;
+#else
+ typedef StreamWriter<true> StreamWriterBE;
+ typedef StreamWriter<false> StreamWriterLE;
+#endif
+
+// `dynamic` StreamWriter. The byte order of the input data is specified in the
+// c'tor. This involves runtime branching and might be a little bit slower.
+typedef StreamWriter<true,true> StreamWriterAny;
+
+} // end namespace Assimp
+
+#endif // !! AI_STREAMWriter_H_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/StringComparison.h b/thirdparty/assimp/include/assimp/StringComparison.h
new file mode 100644
index 0000000000..8acef277b9
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/StringComparison.h
@@ -0,0 +1,233 @@
+/*
+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 Definition of platform independent string workers:
+
+ ASSIMP_itoa10
+ ASSIMP_stricmp
+ ASSIMP_strincmp
+
+ These functions are not consistently available on all platforms,
+ or the provided implementations behave too differently.
+*/
+#ifndef INCLUDED_AI_STRING_WORKERS_H
+#define INCLUDED_AI_STRING_WORKERS_H
+
+#include <assimp/ai_assert.h>
+#include <assimp/defs.h>
+#include "StringComparison.h"
+
+#include <string.h>
+#include <stdint.h>
+#include <string>
+
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+/** @brief itoa with a fixed base 10
+ * 'itoa' is not consistently available on all platforms so it is quite useful
+ * to have a small replacement function here. No need to use a full sprintf()
+ * if we just want to print a number ...
+ * @param out Output buffer
+ * @param max Maximum number of characters to be written, including '\0'.
+ * This parameter may not be 0.
+ * @param number Number to be written
+ * @return Length of the output string, excluding the '\0'
+ */
+AI_FORCE_INLINE
+unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) {
+ ai_assert(NULL != out);
+
+ // write the unary minus to indicate we have a negative number
+ unsigned int written = 1u;
+ if (number < 0 && written < max) {
+ *out++ = '-';
+ ++written;
+ number = -number;
+ }
+
+ // We begin with the largest number that is not zero.
+ int32_t cur = 1000000000; // 2147483648
+ bool mustPrint = false;
+ while (written < max) {
+
+ const unsigned int digit = number / cur;
+ if (mustPrint || digit > 0 || 1 == cur) {
+ // print all future zeroe's from now
+ mustPrint = true;
+
+ *out++ = '0'+static_cast<char>(digit);
+
+ ++written;
+ number -= digit*cur;
+ if (1 == cur) {
+ break;
+ }
+ }
+ cur /= 10;
+ }
+
+ // append a terminal zero
+ *out++ = '\0';
+ return written-1;
+}
+
+// -------------------------------------------------------------------------------
+/** @brief itoa with a fixed base 10 (Secure template overload)
+ * The compiler should choose this function if he or she is able to determine the
+ * size of the array automatically.
+ */
+template <size_t length>
+AI_FORCE_INLINE
+unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) {
+ return ASSIMP_itoa10(out,length,number);
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Helper function to do platform independent string comparison.
+ *
+ * This is required since stricmp() is not consistently available on
+ * all platforms. Some platforms use the '_' prefix, others don't even
+ * have such a function.
+ *
+ * @param s1 First input string
+ * @param s2 Second input string
+ * @return 0 if the given strings are identical
+ */
+AI_FORCE_INLINE
+int ASSIMP_stricmp(const char *s1, const char *s2) {
+ ai_assert( NULL != s1 );
+ ai_assert( NULL != s2 );
+
+#if (defined _MSC_VER)
+
+ return ::_stricmp(s1,s2);
+#elif defined( __GNUC__ )
+
+ return ::strcasecmp(s1,s2);
+#else
+
+ char c1, c2;
+ do {
+ c1 = tolower(*s1++);
+ c2 = tolower(*s2++);
+ }
+ while ( c1 && (c1 == c2) );
+ return c1 - c2;
+#endif
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Case independent comparison of two std::strings
+ *
+ * @param a First string
+ * @param b Second string
+ * @return 0 if a == b
+ */
+AI_FORCE_INLINE
+int ASSIMP_stricmp(const std::string& a, const std::string& b) {
+ int i = (int)b.length()-(int)a.length();
+ return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str()));
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Helper function to do platform independent string comparison.
+ *
+ * This is required since strincmp() is not consistently available on
+ * all platforms. Some platforms use the '_' prefix, others don't even
+ * have such a function.
+ *
+ * @param s1 First input string
+ * @param s2 Second input string
+ * @param n Macimum number of characters to compare
+ * @return 0 if the given strings are identical
+ */
+AI_FORCE_INLINE
+int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) {
+ ai_assert( NULL != s1 );
+ ai_assert( NULL != s2 );
+ if ( !n ) {
+ return 0;
+ }
+
+#if (defined _MSC_VER)
+
+ return ::_strnicmp(s1,s2,n);
+
+#elif defined( __GNUC__ )
+
+ return ::strncasecmp(s1,s2, n);
+
+#else
+ char c1, c2;
+ unsigned int p = 0;
+ do
+ {
+ if (p++ >= n)return 0;
+ c1 = tolower(*s1++);
+ c2 = tolower(*s2++);
+ }
+ while ( c1 && (c1 == c2) );
+
+ return c1 - c2;
+#endif
+}
+
+
+// -------------------------------------------------------------------------------
+/** @brief Evaluates an integer power
+ *
+ * todo: move somewhere where it fits better in than here
+ */
+AI_FORCE_INLINE
+unsigned int integer_pow( unsigned int base, unsigned int power ) {
+ unsigned int res = 1;
+ for ( unsigned int i = 0; i < power; ++i ) {
+ res *= base;
+ }
+
+ return res;
+}
+
+} // end of namespace
+
+#endif // ! AI_STRINGCOMPARISON_H_INC
diff --git a/thirdparty/assimp/include/assimp/StringUtils.h b/thirdparty/assimp/include/assimp/StringUtils.h
new file mode 100644
index 0000000000..d68b7fa479
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/StringUtils.h
@@ -0,0 +1,143 @@
+/*
+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 INCLUDED_AI_STRINGUTILS_H
+#define INCLUDED_AI_STRINGUTILS_H
+
+#include <assimp/defs.h>
+
+#include <sstream>
+#include <stdarg.h>
+#include <cstdlib>
+
+/// @fn ai_snprintf
+/// @brief The portable version of the function snprintf ( C99 standard ), which works on visual studio compilers 2013 and earlier.
+/// @param outBuf The buffer to write in
+/// @param size The buffer size
+/// @param format The format string
+/// @param ap The additional arguments.
+/// @return The number of written characters if the buffer size was big enough. If an encoding error occurs, a negative number is returned.
+#if defined(_MSC_VER) && _MSC_VER < 1900
+
+ AI_FORCE_INLINE
+ int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
+ int count(-1);
+ if (0 != size) {
+ count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+ }
+ if (count == -1) {
+ count = _vscprintf(format, ap);
+ }
+
+ return count;
+ }
+
+ AI_FORCE_INLINE
+ int ai_snprintf(char *outBuf, size_t size, const char *format, ...) {
+ int count;
+ va_list ap;
+
+ va_start(ap, format);
+ count = c99_ai_vsnprintf(outBuf, size, format, ap);
+ va_end(ap);
+
+ return count;
+ }
+
+#else
+# define ai_snprintf snprintf
+#endif
+
+/// @fn to_string
+/// @brief The portable version of to_string ( some gcc-versions on embedded devices are not supporting this).
+/// @param value The value to write into the std::string.
+/// @return The value as a std::string
+template <typename T>
+AI_FORCE_INLINE
+std::string to_string( T value ) {
+ std::ostringstream os;
+ os << value;
+
+ return os.str();
+}
+
+/// @fn ai_strtof
+/// @brief The portable version of strtof.
+/// @param begin The first character of the string.
+/// @param end The last character
+/// @return The float value, 0.0f in cas of an error.
+AI_FORCE_INLINE
+float ai_strtof( const char *begin, const char *end ) {
+ if ( nullptr == begin ) {
+ return 0.0f;
+ }
+ float val( 0.0f );
+ if ( nullptr == end ) {
+ val = static_cast< float >( ::atof( begin ) );
+ } else {
+ std::string::size_type len( end - begin );
+ std::string token( begin, len );
+ val = static_cast< float >( ::atof( token.c_str() ) );
+ }
+
+ return val;
+}
+
+/// @fn DecimalToHexa
+/// @brief The portable to convert a decimal value into a hexadecimal string.
+/// @param toConvert Value to convert
+/// @return The hexadecimal string, is empty in case of an error.
+template<class T>
+AI_FORCE_INLINE
+std::string DecimalToHexa( T toConvert ) {
+ std::string result;
+ std::stringstream ss;
+ ss << std::hex << toConvert;
+ ss >> result;
+
+ for ( size_t i = 0; i < result.size(); ++i ) {
+ result[ i ] = toupper( result[ i ] );
+ }
+
+ return result;
+}
+
+#endif // INCLUDED_AI_STRINGUTILS_H
diff --git a/thirdparty/assimp/include/assimp/Subdivision.h b/thirdparty/assimp/include/assimp/Subdivision.h
new file mode 100644
index 0000000000..43feb73b30
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Subdivision.h
@@ -0,0 +1,131 @@
+/*
+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 evaluate subdivision surfaces.*/
+#pragma once
+#ifndef AI_SUBDISIVION_H_INC
+#define AI_SUBDISIVION_H_INC
+
+#include <cstddef>
+#include <assimp/types.h>
+
+struct aiMesh;
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------
+/** Helper class to evaluate subdivision surfaces. Different algorithms
+ * are provided for choice. */
+// ------------------------------------------------------------------------------
+class ASSIMP_API Subdivider {
+public:
+
+ /** Enumerates all supported subvidision algorithms */
+ enum Algorithm {
+ CATMULL_CLARKE = 0x1
+ };
+
+ virtual ~Subdivider();
+
+ // ---------------------------------------------------------------
+ /** Create a subdivider of a specific type
+ *
+ * @param algo Algorithm to be used for subdivision
+ * @return Subdivider instance. */
+ static Subdivider* Create (Algorithm algo);
+
+ // ---------------------------------------------------------------
+ /** Subdivide a mesh using the selected algorithm
+ *
+ * @param mesh First mesh to be subdivided. Must be in verbose
+ * format.
+ * @param out Receives the output mesh, allocated by me.
+ * @param num Number of subdivisions to perform.
+ * @param discard_input If true is passed, the input mesh is
+ * deleted after the subdivision is complete. This can
+ * improve performance because it allows the optimization
+ * to reuse the existing mesh for intermediate results.
+ * @pre out!=mesh*/
+ virtual void Subdivide ( aiMesh* mesh,
+ aiMesh*& out, unsigned int num,
+ bool discard_input = false) = 0;
+
+ // ---------------------------------------------------------------
+ /** Subdivide multiple meshes using the selected algorithm. This
+ * avoids erroneous smoothing on objects consisting of multiple
+ * per-material meshes. Usually, most 3d modellers smooth on a
+ * per-object base, regardless the materials assigned to the
+ * meshes.
+ *
+ * @param smesh Array of meshes to be subdivided. Must be in
+ * verbose format.
+ * @param nmesh Number of meshes in smesh.
+ * @param out Receives the output meshes. The array must be
+ * sufficiently large (at least @c nmesh elements) and may not
+ * overlap the input array. Output meshes map one-to-one to
+ * their corresponding input meshes. The meshes are allocated
+ * by the function.
+ * @param discard_input If true is passed, input meshes are
+ * deleted after the subdivision is complete. This can
+ * improve performance because it allows the optimization
+ * of reusing existing meshes for intermediate results.
+ * @param num Number of subdivisions to perform.
+ * @pre nmesh != 0, smesh and out may not overlap*/
+ virtual void Subdivide (
+ aiMesh** smesh,
+ size_t nmesh,
+ aiMesh** out,
+ unsigned int num,
+ bool discard_input = false) = 0;
+
+};
+
+inline
+Subdivider::~Subdivider() {
+ // empty
+}
+
+} // end namespace Assimp
+
+
+#endif // !! AI_SUBDISIVION_H_INC
+
diff --git a/thirdparty/assimp/include/assimp/TinyFormatter.h b/thirdparty/assimp/include/assimp/TinyFormatter.h
new file mode 100644
index 0000000000..1226b482e6
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/TinyFormatter.h
@@ -0,0 +1,166 @@
+/*
+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 TinyFormatter.h
+ * @brief Utility to format log messages more easily. Introduced
+ * to get rid of the boost::format dependency. Much slinker,
+ * basically just extends stringstream.
+ */
+#ifndef INCLUDED_TINY_FORMATTER_H
+#define INCLUDED_TINY_FORMATTER_H
+
+#include <sstream>
+
+namespace Assimp {
+namespace Formatter {
+
+// ------------------------------------------------------------------------------------------------
+/** stringstream utility. Usage:
+ * @code
+ * void writelog(const std::string&s);
+ * void writelog(const std::wstring&s);
+ * ...
+ * writelog(format()<< "hi! this is a number: " << 4);
+ * writelog(wformat()<< L"hi! this is a number: " << 4);
+ *
+ * @endcode */
+template < typename T,
+ typename CharTraits = std::char_traits<T>,
+ typename Allocator = std::allocator<T>
+>
+class basic_formatter
+{
+
+public:
+
+ typedef class std::basic_string<
+ T,CharTraits,Allocator
+ > string;
+
+ typedef class std::basic_ostringstream<
+ T,CharTraits,Allocator
+ > stringstream;
+
+public:
+
+ basic_formatter() {}
+
+ /* Allow basic_formatter<T>'s to be used almost interchangeably
+ * with std::(w)string or const (w)char* arguments because the
+ * conversion c'tor is called implicitly. */
+ template <typename TT>
+ basic_formatter(const TT& sin) {
+ underlying << sin;
+ }
+
+
+ // The problem described here:
+ // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462
+ // can also cause trouble here. Apparently, older gcc versions sometimes copy temporaries
+ // being bound to const ref& function parameters. Copying streams is not permitted, though.
+ // This workaround avoids this by manually specifying a copy ctor.
+#if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+ explicit basic_formatter(const basic_formatter& other) {
+ underlying << (string)other;
+ }
+#endif
+
+
+public:
+
+ operator string () const {
+ return underlying.str();
+ }
+
+
+ /* note - this is declared const because binding temporaries does only
+ * work for const references, so many function prototypes will
+ * include const basic_formatter<T>& s but might still want to
+ * modify the formatted string without the need for a full copy.*/
+ template <typename TToken>
+ const basic_formatter& operator << (const TToken& s) const {
+ underlying << s;
+ return *this;
+ }
+
+ template <typename TToken>
+ basic_formatter& operator << (const TToken& s) {
+ underlying << s;
+ return *this;
+ }
+
+
+ // comma operator overloaded as well, choose your preferred way.
+ template <typename TToken>
+ const basic_formatter& operator, (const TToken& s) const {
+ underlying << s;
+ return *this;
+ }
+
+ template <typename TToken>
+ basic_formatter& operator, (const TToken& s) {
+ underlying << s;
+ return *this;
+ }
+
+ // Fix for MSVC8
+ // See https://sourceforge.net/projects/assimp/forums/forum/817654/topic/4372824
+ template <typename TToken>
+ basic_formatter& operator, (TToken& s) {
+ underlying << s;
+ return *this;
+ }
+
+
+private:
+ mutable stringstream underlying;
+};
+
+
+typedef basic_formatter< char > format;
+typedef basic_formatter< wchar_t > wformat;
+
+} // ! namespace Formatter
+
+} // ! namespace Assimp
+
+#endif
diff --git a/thirdparty/assimp/include/assimp/Vertex.h b/thirdparty/assimp/include/assimp/Vertex.h
new file mode 100644
index 0000000000..2a7f0256ad
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/Vertex.h
@@ -0,0 +1,348 @@
+/*
+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 represent an interleaved vertex
+ along with arithmetic operations to support vertex operations
+ such as subdivision, smoothing etc.
+
+ While the code is kept as general as possible, arithmetic operations
+ that are not currently well-defined (and would cause compile errors
+ due to missing operators in the math library), are commented.
+ */
+#ifndef AI_VERTEX_H_INC
+#define AI_VERTEX_H_INC
+
+#include <assimp/vector3.h>
+#include <assimp/mesh.h>
+#include <assimp/ai_assert.h>
+#include <functional>
+
+namespace Assimp {
+
+ ///////////////////////////////////////////////////////////////////////////
+ // std::plus-family operates on operands with identical types - we need to
+ // support all the (vectype op float) combinations in vector maths.
+ // Providing T(float) would open the way to endless implicit conversions.
+ ///////////////////////////////////////////////////////////////////////////
+ namespace Intern {
+ template <typename T0, typename T1, typename TRES = T0> struct plus {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0+t1;
+ }
+ };
+ template <typename T0, typename T1, typename TRES = T0> struct minus {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0-t1;
+ }
+ };
+ template <typename T0, typename T1, typename TRES = T0> struct multiplies {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0*t1;
+ }
+ };
+ template <typename T0, typename T1, typename TRES = T0> struct divides {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0/t1;
+ }
+ };
+ }
+
+// ------------------------------------------------------------------------------------------------
+/** Intermediate description a vertex with all possible components. Defines a full set of
+ * operators, so you may use such a 'Vertex' in basic arithmetics. All operators are applied
+ * to *all* vertex components equally. This is useful for stuff like interpolation
+ * or subdivision, but won't work if special handling is required for some vertex components. */
+// ------------------------------------------------------------------------------------------------
+class Vertex
+{
+ friend Vertex operator + (const Vertex&,const Vertex&);
+ friend Vertex operator - (const Vertex&,const Vertex&);
+
+// friend Vertex operator + (const Vertex&,ai_real);
+// friend Vertex operator - (const Vertex&,ai_real);
+ friend Vertex operator * (const Vertex&,ai_real);
+ friend Vertex operator / (const Vertex&,ai_real);
+
+// friend Vertex operator + (ai_real, const Vertex&);
+// friend Vertex operator - (ai_real, const Vertex&);
+ friend Vertex operator * (ai_real, const Vertex&);
+// friend Vertex operator / (ai_real, const Vertex&);
+
+public:
+
+ Vertex() {}
+
+ // ----------------------------------------------------------------------------
+ /** Extract a particular vertex from a mesh and interleave all components */
+ explicit Vertex(const aiMesh* msh, unsigned int idx) {
+ ai_assert(idx < msh->mNumVertices);
+ position = msh->mVertices[idx];
+
+ if (msh->HasNormals()) {
+ normal = msh->mNormals[idx];
+ }
+
+ if (msh->HasTangentsAndBitangents()) {
+ tangent = msh->mTangents[idx];
+ bitangent = msh->mBitangents[idx];
+ }
+
+ for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) {
+ texcoords[i] = msh->mTextureCoords[i][idx];
+ }
+
+ for (unsigned int i = 0; msh->HasVertexColors(i); ++i) {
+ colors[i] = msh->mColors[i][idx];
+ }
+ }
+
+ // ----------------------------------------------------------------------------
+ /** Extract a particular vertex from a anim mesh and interleave all components */
+ explicit Vertex(const aiAnimMesh* msh, unsigned int idx) {
+ ai_assert(idx < msh->mNumVertices);
+ position = msh->mVertices[idx];
+
+ if (msh->HasNormals()) {
+ normal = msh->mNormals[idx];
+ }
+
+ if (msh->HasTangentsAndBitangents()) {
+ tangent = msh->mTangents[idx];
+ bitangent = msh->mBitangents[idx];
+ }
+
+ for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) {
+ texcoords[i] = msh->mTextureCoords[i][idx];
+ }
+
+ for (unsigned int i = 0; msh->HasVertexColors(i); ++i) {
+ colors[i] = msh->mColors[i][idx];
+ }
+ }
+
+public:
+
+ Vertex& operator += (const Vertex& v) {
+ *this = *this+v;
+ return *this;
+ }
+
+ Vertex& operator -= (const Vertex& v) {
+ *this = *this-v;
+ return *this;
+ }
+
+
+/*
+ Vertex& operator += (ai_real v) {
+ *this = *this+v;
+ return *this;
+ }
+
+ Vertex& operator -= (ai_real v) {
+ *this = *this-v;
+ return *this;
+ }
+*/
+ Vertex& operator *= (ai_real v) {
+ *this = *this*v;
+ return *this;
+ }
+
+ Vertex& operator /= (ai_real v) {
+ *this = *this/v;
+ return *this;
+ }
+
+public:
+
+ // ----------------------------------------------------------------------------
+ /** Convert back to non-interleaved storage */
+ void SortBack(aiMesh* out, unsigned int idx) const {
+
+ ai_assert(idx<out->mNumVertices);
+ out->mVertices[idx] = position;
+
+ if (out->HasNormals()) {
+ out->mNormals[idx] = normal;
+ }
+
+ if (out->HasTangentsAndBitangents()) {
+ out->mTangents[idx] = tangent;
+ out->mBitangents[idx] = bitangent;
+ }
+
+ for(unsigned int i = 0; out->HasTextureCoords(i); ++i) {
+ out->mTextureCoords[i][idx] = texcoords[i];
+ }
+
+ for(unsigned int i = 0; out->HasVertexColors(i); ++i) {
+ out->mColors[i][idx] = colors[i];
+ }
+ }
+
+private:
+
+ // ----------------------------------------------------------------------------
+ /** Construct from two operands and a binary operation to combine them */
+ template <template <typename t> class op> static Vertex BinaryOp(const Vertex& v0, const Vertex& v1) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<aiVector3D>()(v0.position,v1.position);
+ res.normal = op<aiVector3D>()(v0.normal,v1.normal);
+ res.tangent = op<aiVector3D>()(v0.tangent,v1.tangent);
+ res.bitangent = op<aiVector3D>()(v0.bitangent,v1.bitangent);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<aiVector3D>()(v0.texcoords[i],v1.texcoords[i]);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<aiColor4D>()(v0.colors[i],v1.colors[i]);
+ }
+ return res;
+ }
+
+ // ----------------------------------------------------------------------------
+ /** This time binary arithmetics of v0 with a floating-point number */
+ template <template <typename, typename, typename> class op> static Vertex BinaryOp(const Vertex& v0, ai_real f) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<aiVector3D,ai_real,aiVector3D>()(v0.position,f);
+ res.normal = op<aiVector3D,ai_real,aiVector3D>()(v0.normal,f);
+ res.tangent = op<aiVector3D,ai_real,aiVector3D>()(v0.tangent,f);
+ res.bitangent = op<aiVector3D,ai_real,aiVector3D>()(v0.bitangent,f);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<aiVector3D,ai_real,aiVector3D>()(v0.texcoords[i],f);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<aiColor4D,ai_real,aiColor4D>()(v0.colors[i],f);
+ }
+ return res;
+ }
+
+ // ----------------------------------------------------------------------------
+ /** This time binary arithmetics of v0 with a floating-point number */
+ template <template <typename, typename, typename> class op> static Vertex BinaryOp(ai_real f, const Vertex& v0) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<ai_real,aiVector3D,aiVector3D>()(f,v0.position);
+ res.normal = op<ai_real,aiVector3D,aiVector3D>()(f,v0.normal);
+ res.tangent = op<ai_real,aiVector3D,aiVector3D>()(f,v0.tangent);
+ res.bitangent = op<ai_real,aiVector3D,aiVector3D>()(f,v0.bitangent);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<ai_real,aiVector3D,aiVector3D>()(f,v0.texcoords[i]);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<ai_real,aiColor4D,aiColor4D>()(f,v0.colors[i]);
+ }
+ return res;
+ }
+
+public:
+
+ aiVector3D position;
+ aiVector3D normal;
+ aiVector3D tangent, bitangent;
+
+ aiVector3D texcoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ aiColor4D colors[AI_MAX_NUMBER_OF_COLOR_SETS];
+};
+
+
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE Vertex operator + (const Vertex& v0,const Vertex& v1) {
+ return Vertex::BinaryOp<std::plus>(v0,v1);
+}
+
+AI_FORCE_INLINE Vertex operator - (const Vertex& v0,const Vertex& v1) {
+ return Vertex::BinaryOp<std::minus>(v0,v1);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+/*
+AI_FORCE_INLINE Vertex operator + (const Vertex& v0,ai_real f) {
+ return Vertex::BinaryOp<Intern::plus>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator - (const Vertex& v0,ai_real f) {
+ return Vertex::BinaryOp<Intern::minus>(v0,f);
+}
+
+*/
+
+AI_FORCE_INLINE Vertex operator * (const Vertex& v0,ai_real f) {
+ return Vertex::BinaryOp<Intern::multiplies>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator / (const Vertex& v0,ai_real f) {
+ return Vertex::BinaryOp<Intern::multiplies>(v0,1.f/f);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*
+AI_FORCE_INLINE Vertex operator + (ai_real f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::plus>(f,v0);
+}
+
+AI_FORCE_INLINE Vertex operator - (ai_real f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::minus>(f,v0);
+}
+*/
+
+AI_FORCE_INLINE Vertex operator * (ai_real f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::multiplies>(f,v0);
+}
+
+/*
+AI_FORCE_INLINE Vertex operator / (ai_real f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::divides>(f,v0);
+}
+*/
+
+}
+#endif
diff --git a/thirdparty/assimp/include/assimp/XMLTools.h b/thirdparty/assimp/include/assimp/XMLTools.h
new file mode 100644
index 0000000000..b0d3276873
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/XMLTools.h
@@ -0,0 +1,83 @@
+/*
+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 INCLUDED_ASSIMP_XML_TOOLS_H
+#define INCLUDED_ASSIMP_XML_TOOLS_H
+
+#include <string>
+
+namespace Assimp {
+ // XML escape the 5 XML special characters (",',<,> and &) in |data|
+ // Based on http://stackoverflow.com/questions/5665231
+ std::string XMLEscape(const std::string& data) {
+ std::string buffer;
+
+ const size_t size = data.size();
+ buffer.reserve(size + size / 8);
+ for(size_t i = 0; i < size; ++i) {
+ const char c = data[i];
+ switch(c) {
+ case '&' :
+ buffer.append("&amp;");
+ break;
+ case '\"':
+ buffer.append("&quot;");
+ break;
+ case '\'':
+ buffer.append("&apos;");
+ break;
+ case '<' :
+ buffer.append("&lt;");
+ break;
+ case '>' :
+ buffer.append("&gt;");
+ break;
+ default:
+ buffer.append(&c, 1);
+ break;
+ }
+ }
+ return buffer;
+ }
+}
+
+#endif // INCLUDED_ASSIMP_XML_TOOLS_H
diff --git a/thirdparty/assimp/include/assimp/ai_assert.h b/thirdparty/assimp/include/assimp/ai_assert.h
new file mode 100644
index 0000000000..e5de5d3f36
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/ai_assert.h
@@ -0,0 +1,57 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+#pragma once
+#ifndef AI_ASSERT_H_INC
+#define AI_ASSERT_H_INC
+
+#ifdef ASSIMP_BUILD_DEBUG
+# include <assert.h>
+# define ai_assert(expression) assert( expression )
+# define ai_assert_entry() assert( false )
+#else
+# define ai_assert(expression)
+# define ai_assert_entry()
+#endif // ASSIMP_BUILD_DEBUG
+
+#endif // AI_ASSERT_H_INC
+
diff --git a/thirdparty/assimp/include/assimp/anim.h b/thirdparty/assimp/include/assimp/anim.h
new file mode 100644
index 0000000000..02e92739ec
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/anim.h
@@ -0,0 +1,577 @@
+/*
+---------------------------------------------------------------------------
+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 anim.h
+ * @brief Defines the data structures in which the imported animations
+ * are returned.
+ */
+#pragma once
+#ifndef AI_ANIM_H_INC
+#define AI_ANIM_H_INC
+
+#include <assimp/types.h>
+#include <assimp/quaternion.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** A time-value pair specifying a certain 3D vector for the given time. */
+struct aiVectorKey
+{
+ /** The time of this key */
+ double mTime;
+
+ /** The value of this key */
+ C_STRUCT aiVector3D mValue;
+
+#ifdef __cplusplus
+
+ /// @brief The default constructor.
+ aiVectorKey() AI_NO_EXCEPT
+ : mTime( 0.0 )
+ , mValue() {
+ // empty
+ }
+
+ /// @brief Construction from a given time and key value.
+
+ aiVectorKey(double time, const aiVector3D& value)
+ : mTime( time )
+ , mValue( value ) {
+ // empty
+ }
+
+ typedef aiVector3D elem_type;
+
+ // Comparison operators. For use with std::find();
+ bool operator == (const aiVectorKey& rhs) const {
+ return rhs.mValue == this->mValue;
+ }
+ bool operator != (const aiVectorKey& rhs ) const {
+ return rhs.mValue != this->mValue;
+ }
+
+ // Relational operators. For use with std::sort();
+ bool operator < (const aiVectorKey& rhs ) const {
+ return mTime < rhs.mTime;
+ }
+ bool operator > (const aiVectorKey& rhs ) const {
+ return mTime > rhs.mTime;
+ }
+#endif // __cplusplus
+};
+
+// ---------------------------------------------------------------------------
+/** A time-value pair specifying a rotation for the given time.
+ * Rotations are expressed with quaternions. */
+struct aiQuatKey
+{
+ /** The time of this key */
+ double mTime;
+
+ /** The value of this key */
+ C_STRUCT aiQuaternion mValue;
+
+#ifdef __cplusplus
+ aiQuatKey() AI_NO_EXCEPT
+ : mTime( 0.0 )
+ , mValue() {
+ // empty
+ }
+
+ /** Construction from a given time and key value */
+ aiQuatKey(double time, const aiQuaternion& value)
+ : mTime (time)
+ , mValue (value)
+ {}
+
+ typedef aiQuaternion elem_type;
+
+ // Comparison operators. For use with std::find();
+ bool operator == (const aiQuatKey& rhs ) const {
+ return rhs.mValue == this->mValue;
+ }
+ bool operator != (const aiQuatKey& rhs ) const {
+ return rhs.mValue != this->mValue;
+ }
+
+ // Relational operators. For use with std::sort();
+ bool operator < (const aiQuatKey& rhs ) const {
+ return mTime < rhs.mTime;
+ }
+ bool operator > (const aiQuatKey& rhs ) const {
+ return mTime > rhs.mTime;
+ }
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Binds a anim-mesh to a specific point in time. */
+struct aiMeshKey
+{
+ /** The time of this key */
+ double mTime;
+
+ /** Index into the aiMesh::mAnimMeshes array of the
+ * mesh corresponding to the #aiMeshAnim hosting this
+ * key frame. The referenced anim mesh is evaluated
+ * according to the rules defined in the docs for #aiAnimMesh.*/
+ unsigned int mValue;
+
+#ifdef __cplusplus
+
+ aiMeshKey() AI_NO_EXCEPT
+ : mTime(0.0)
+ , mValue(0)
+ {
+ }
+
+ /** Construction from a given time and key value */
+ aiMeshKey(double time, const unsigned int value)
+ : mTime (time)
+ , mValue (value)
+ {}
+
+ typedef unsigned int elem_type;
+
+ // Comparison operators. For use with std::find();
+ bool operator == (const aiMeshKey& o) const {
+ return o.mValue == this->mValue;
+ }
+ bool operator != (const aiMeshKey& o) const {
+ return o.mValue != this->mValue;
+ }
+
+ // Relational operators. For use with std::sort();
+ bool operator < (const aiMeshKey& o) const {
+ return mTime < o.mTime;
+ }
+ bool operator > (const aiMeshKey& o) const {
+ return mTime > o.mTime;
+ }
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Binds a morph anim mesh to a specific point in time. */
+struct aiMeshMorphKey
+{
+ /** The time of this key */
+ double mTime;
+
+ /** The values and weights at the time of this key */
+ unsigned int *mValues;
+ double *mWeights;
+
+ /** The number of values and weights */
+ unsigned int mNumValuesAndWeights;
+#ifdef __cplusplus
+ aiMeshMorphKey() AI_NO_EXCEPT
+ : mTime(0.0)
+ , mValues(nullptr)
+ , mWeights(nullptr)
+ , mNumValuesAndWeights(0)
+ {
+
+ }
+
+ ~aiMeshMorphKey()
+ {
+ if (mNumValuesAndWeights && mValues && mWeights) {
+ delete [] mValues;
+ delete [] mWeights;
+ }
+ }
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Defines how an animation channel behaves outside the defined time
+ * range. This corresponds to aiNodeAnim::mPreState and
+ * aiNodeAnim::mPostState.*/
+enum aiAnimBehaviour
+{
+ /** The value from the default node transformation is taken*/
+ aiAnimBehaviour_DEFAULT = 0x0,
+
+ /** The nearest key value is used without interpolation */
+ aiAnimBehaviour_CONSTANT = 0x1,
+
+ /** The value of the nearest two keys is linearly
+ * extrapolated for the current time value.*/
+ aiAnimBehaviour_LINEAR = 0x2,
+
+ /** The animation is repeated.
+ *
+ * If the animation key go from n to m and the current
+ * time is t, use the value at (t-n) % (|m-n|).*/
+ aiAnimBehaviour_REPEAT = 0x3,
+
+ /** This value is not used, it is just here to force the
+ * the compiler to map this enum to a 32 Bit integer */
+#ifndef SWIG
+ _aiAnimBehaviour_Force32Bit = INT_MAX
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Describes the animation of a single node. The name specifies the
+ * bone/node which is affected by this animation channel. The keyframes
+ * are given in three separate series of values, one each for position,
+ * rotation and scaling. The transformation matrix computed from these
+ * values replaces the node's original transformation matrix at a
+ * specific time.
+ * This means all keys are absolute and not relative to the bone default pose.
+ * The order in which the transformations are applied is
+ * - as usual - scaling, rotation, translation.
+ *
+ * @note All keys are returned in their correct, chronological order.
+ * Duplicate keys don't pass the validation step. Most likely there
+ * will be no negative time values, but they are not forbidden also ( so
+ * implementations need to cope with them! ) */
+struct aiNodeAnim {
+ /** The name of the node affected by this animation. The node
+ * must exist and it must be unique.*/
+ C_STRUCT aiString mNodeName;
+
+ /** The number of position keys */
+ unsigned int mNumPositionKeys;
+
+ /** The position keys of this animation channel. Positions are
+ * specified as 3D vector. The array is mNumPositionKeys in size.
+ *
+ * If there are position keys, there will also be at least one
+ * scaling and one rotation key.*/
+ C_STRUCT aiVectorKey* mPositionKeys;
+
+ /** The number of rotation keys */
+ unsigned int mNumRotationKeys;
+
+ /** The rotation keys of this animation channel. Rotations are
+ * given as quaternions, which are 4D vectors. The array is
+ * mNumRotationKeys in size.
+ *
+ * If there are rotation keys, there will also be at least one
+ * scaling and one position key. */
+ C_STRUCT aiQuatKey* mRotationKeys;
+
+ /** The number of scaling keys */
+ unsigned int mNumScalingKeys;
+
+ /** The scaling keys of this animation channel. Scalings are
+ * specified as 3D vector. The array is mNumScalingKeys in size.
+ *
+ * If there are scaling keys, there will also be at least one
+ * position and one rotation key.*/
+ C_STRUCT aiVectorKey* mScalingKeys;
+
+ /** Defines how the animation behaves before the first
+ * key is encountered.
+ *
+ * The default value is aiAnimBehaviour_DEFAULT (the original
+ * transformation matrix of the affected node is used).*/
+ C_ENUM aiAnimBehaviour mPreState;
+
+ /** Defines how the animation behaves after the last
+ * key was processed.
+ *
+ * The default value is aiAnimBehaviour_DEFAULT (the original
+ * transformation matrix of the affected node is taken).*/
+ C_ENUM aiAnimBehaviour mPostState;
+
+#ifdef __cplusplus
+ aiNodeAnim() AI_NO_EXCEPT
+ : mNumPositionKeys( 0 )
+ , mPositionKeys( nullptr )
+ , mNumRotationKeys( 0 )
+ , mRotationKeys( nullptr )
+ , mNumScalingKeys( 0 )
+ , mScalingKeys( nullptr )
+ , mPreState( aiAnimBehaviour_DEFAULT )
+ , mPostState( aiAnimBehaviour_DEFAULT ) {
+ // empty
+ }
+
+ ~aiNodeAnim() {
+ delete [] mPositionKeys;
+ delete [] mRotationKeys;
+ delete [] mScalingKeys;
+ }
+#endif // __cplusplus
+};
+
+// ---------------------------------------------------------------------------
+/** Describes vertex-based animations for a single mesh or a group of
+ * meshes. Meshes carry the animation data for each frame in their
+ * aiMesh::mAnimMeshes array. The purpose of aiMeshAnim is to
+ * define keyframes linking each mesh attachment to a particular
+ * point in time. */
+struct aiMeshAnim
+{
+ /** Name of the mesh to be animated. An empty string is not allowed,
+ * animated meshes need to be named (not necessarily uniquely,
+ * the name can basically serve as wild-card to select a group
+ * of meshes with similar animation setup)*/
+ C_STRUCT aiString mName;
+
+ /** Size of the #mKeys array. Must be 1, at least. */
+ unsigned int mNumKeys;
+
+ /** Key frames of the animation. May not be NULL. */
+ C_STRUCT aiMeshKey* mKeys;
+
+#ifdef __cplusplus
+
+ aiMeshAnim() AI_NO_EXCEPT
+ : mNumKeys()
+ , mKeys()
+ {}
+
+ ~aiMeshAnim()
+ {
+ delete[] mKeys;
+ }
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Describes a morphing animation of a given mesh. */
+struct aiMeshMorphAnim
+{
+ /** Name of the mesh to be animated. An empty string is not allowed,
+ * animated meshes need to be named (not necessarily uniquely,
+ * the name can basically serve as wildcard to select a group
+ * of meshes with similar animation setup)*/
+ C_STRUCT aiString mName;
+
+ /** Size of the #mKeys array. Must be 1, at least. */
+ unsigned int mNumKeys;
+
+ /** Key frames of the animation. May not be NULL. */
+ C_STRUCT aiMeshMorphKey* mKeys;
+
+#ifdef __cplusplus
+
+ aiMeshMorphAnim() AI_NO_EXCEPT
+ : mNumKeys()
+ , mKeys()
+ {}
+
+ ~aiMeshMorphAnim()
+ {
+ delete[] mKeys;
+ }
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** An animation consists of key-frame data for a number of nodes. For
+ * each node affected by the animation a separate series of data is given.*/
+struct aiAnimation {
+ /** The name of the animation. If the modeling package this data was
+ * exported from does support only a single animation channel, this
+ * name is usually empty (length is zero). */
+ C_STRUCT aiString mName;
+
+ /** Duration of the animation in ticks. */
+ double mDuration;
+
+ /** Ticks per second. 0 if not specified in the imported file */
+ double mTicksPerSecond;
+
+ /** The number of bone animation channels. Each channel affects
+ * a single node. */
+ unsigned int mNumChannels;
+
+ /** The node animation channels. Each channel affects a single node.
+ * The array is mNumChannels in size. */
+ C_STRUCT aiNodeAnim** mChannels;
+
+
+ /** The number of mesh animation channels. Each channel affects
+ * a single mesh and defines vertex-based animation. */
+ unsigned int mNumMeshChannels;
+
+ /** The mesh animation channels. Each channel affects a single mesh.
+ * The array is mNumMeshChannels in size. */
+ C_STRUCT aiMeshAnim** mMeshChannels;
+
+ /** The number of mesh animation channels. Each channel affects
+ * a single mesh and defines morphing animation. */
+ unsigned int mNumMorphMeshChannels;
+
+ /** The morph mesh animation channels. Each channel affects a single mesh.
+ * The array is mNumMorphMeshChannels in size. */
+ C_STRUCT aiMeshMorphAnim **mMorphMeshChannels;
+
+#ifdef __cplusplus
+ aiAnimation() AI_NO_EXCEPT
+ : mDuration(-1.)
+ , mTicksPerSecond(0.)
+ , mNumChannels(0)
+ , mChannels(nullptr)
+ , mNumMeshChannels(0)
+ , mMeshChannels(nullptr)
+ , mNumMorphMeshChannels(0)
+ , mMorphMeshChannels(nullptr) {
+ // empty
+ }
+
+ ~aiAnimation() {
+ // DO NOT REMOVE THIS ADDITIONAL CHECK
+ if ( mNumChannels && mChannels ) {
+ for( unsigned int a = 0; a < mNumChannels; a++) {
+ delete mChannels[ a ];
+ }
+
+ delete [] mChannels;
+ }
+ if (mNumMeshChannels && mMeshChannels) {
+ for( unsigned int a = 0; a < mNumMeshChannels; a++) {
+ delete mMeshChannels[a];
+ }
+
+ delete [] mMeshChannels;
+ }
+ if (mNumMorphMeshChannels && mMorphMeshChannels) {
+ for( unsigned int a = 0; a < mNumMorphMeshChannels; a++) {
+ delete mMorphMeshChannels[a];
+ }
+
+ delete [] mMorphMeshChannels;
+ }
+ }
+#endif // __cplusplus
+};
+
+#ifdef __cplusplus
+
+}
+
+/// @brief Some C++ utilities for inter- and extrapolation
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/**
+ * @brief CPP-API: Utility class to simplify interpolations of various data types.
+ *
+ * The type of interpolation is chosen automatically depending on the
+ * types of the arguments.
+ */
+template <typename T>
+struct Interpolator
+{
+ // ------------------------------------------------------------------
+ /** @brief Get the result of the interpolation between a,b.
+ *
+ * The interpolation algorithm depends on the type of the operands.
+ * aiQuaternion's and aiQuatKey's SLERP, the rest does a simple
+ * linear interpolation. */
+ void operator () (T& out,const T& a, const T& b, ai_real d) const {
+ out = a + (b-a)*d;
+ }
+}; // ! Interpolator <T>
+
+//! @cond Never
+
+template <>
+struct Interpolator <aiQuaternion> {
+ void operator () (aiQuaternion& out,const aiQuaternion& a,
+ const aiQuaternion& b, ai_real d) const
+ {
+ aiQuaternion::Interpolate(out,a,b,d);
+ }
+}; // ! Interpolator <aiQuaternion>
+
+template <>
+struct Interpolator <unsigned int> {
+ void operator () (unsigned int& out,unsigned int a,
+ unsigned int b, ai_real d) const
+ {
+ out = d>0.5f ? b : a;
+ }
+}; // ! Interpolator <aiQuaternion>
+
+template <>
+struct Interpolator<aiVectorKey> {
+ void operator () (aiVector3D& out,const aiVectorKey& a,
+ const aiVectorKey& b, ai_real d) const
+ {
+ Interpolator<aiVector3D> ipl;
+ ipl(out,a.mValue,b.mValue,d);
+ }
+}; // ! Interpolator <aiVectorKey>
+
+template <>
+struct Interpolator<aiQuatKey> {
+ void operator () (aiQuaternion& out, const aiQuatKey& a,
+ const aiQuatKey& b, ai_real d) const
+ {
+ Interpolator<aiQuaternion> ipl;
+ ipl(out,a.mValue,b.mValue,d);
+ }
+}; // ! Interpolator <aiQuatKey>
+
+template <>
+struct Interpolator<aiMeshKey> {
+ void operator () (unsigned int& out, const aiMeshKey& a,
+ const aiMeshKey& b, ai_real d) const
+ {
+ Interpolator<unsigned int> ipl;
+ ipl(out,a.mValue,b.mValue,d);
+ }
+}; // ! Interpolator <aiQuatKey>
+
+//! @endcond
+
+} // ! end namespace Assimp
+
+#endif // __cplusplus
+
+#endif // AI_ANIM_H_INC
diff --git a/thirdparty/assimp/include/assimp/camera.h b/thirdparty/assimp/include/assimp/camera.h
new file mode 100644
index 0000000000..99daf69934
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/camera.h
@@ -0,0 +1,226 @@
+/*
+---------------------------------------------------------------------------
+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 camera.h
+ * @brief Defines the aiCamera data structure
+ */
+
+#pragma once
+#ifndef AI_CAMERA_H_INC
+#define AI_CAMERA_H_INC
+
+#include "types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** Helper structure to describe a virtual camera.
+ *
+ * Cameras have a representation in the node graph and can be animated.
+ * An important aspect is that the camera itself is also part of the
+ * scenegraph. This means, any values such as the look-at vector are not
+ * *absolute*, they're <b>relative</b> to the coordinate system defined
+ * by the node which corresponds to the camera. This allows for camera
+ * animations. For static cameras parameters like the 'look-at' or 'up' vectors
+ * are usually specified directly in aiCamera, but beware, they could also
+ * be encoded in the node transformation. The following (pseudo)code sample
+ * shows how to do it: <br><br>
+ * @code
+ * // Get the camera matrix for a camera at a specific time
+ * // if the node hierarchy for the camera does not contain
+ * // at least one animated node this is a static computation
+ * get-camera-matrix (node sceneRoot, camera cam) : matrix
+ * {
+ * node cnd = find-node-for-camera(cam)
+ * matrix cmt = identity()
+ *
+ * // as usual - get the absolute camera transformation for this frame
+ * for each node nd in hierarchy from sceneRoot to cnd
+ * matrix cur
+ * if (is-animated(nd))
+ * cur = eval-animation(nd)
+ * else cur = nd->mTransformation;
+ * cmt = mult-matrices( cmt, cur )
+ * end for
+ *
+ * // now multiply with the camera's own local transform
+ * cam = mult-matrices (cam, get-camera-matrix(cmt) )
+ * }
+ * @endcode
+ *
+ * @note some file formats (such as 3DS, ASE) export a "target point" -
+ * the point the camera is looking at (it can even be animated). Assimp
+ * writes the target point as a subnode of the camera's main node,
+ * called "<camName>.Target". However this is just additional information
+ * then the transformation tracks of the camera main node make the
+ * camera already look in the right direction.
+ *
+*/
+struct aiCamera
+{
+ /** The name of the camera.
+ *
+ * There must be a node in the scenegraph with the same name.
+ * This node specifies the position of the camera in the scene
+ * hierarchy and can be animated.
+ */
+ C_STRUCT aiString mName;
+
+ /** Position of the camera relative to the coordinate space
+ * defined by the corresponding node.
+ *
+ * The default value is 0|0|0.
+ */
+ C_STRUCT aiVector3D mPosition;
+
+
+ /** 'Up' - vector of the camera coordinate system relative to
+ * the coordinate space defined by the corresponding node.
+ *
+ * The 'right' vector of the camera coordinate system is
+ * the cross product of the up and lookAt vectors.
+ * The default value is 0|1|0. The vector
+ * may be normalized, but it needn't.
+ */
+ C_STRUCT aiVector3D mUp;
+
+
+ /** 'LookAt' - vector of the camera coordinate system relative to
+ * the coordinate space defined by the corresponding node.
+ *
+ * This is the viewing direction of the user.
+ * The default value is 0|0|1. The vector
+ * may be normalized, but it needn't.
+ */
+ C_STRUCT aiVector3D mLookAt;
+
+
+ /** Half horizontal field of view angle, in radians.
+ *
+ * The field of view angle is the angle between the center
+ * line of the screen and the left or right border.
+ * The default value is 1/4PI.
+ */
+ float mHorizontalFOV;
+
+ /** Distance of the near clipping plane from the camera.
+ *
+ * The value may not be 0.f (for arithmetic reasons to prevent
+ * a division through zero). The default value is 0.1f.
+ */
+ float mClipPlaneNear;
+
+ /** Distance of the far clipping plane from the camera.
+ *
+ * The far clipping plane must, of course, be further away than the
+ * near clipping plane. The default value is 1000.f. The ratio
+ * between the near and the far plane should not be too
+ * large (between 1000-10000 should be ok) to avoid floating-point
+ * inaccuracies which could lead to z-fighting.
+ */
+ float mClipPlaneFar;
+
+
+ /** Screen aspect ratio.
+ *
+ * This is the ration between the width and the height of the
+ * screen. Typical values are 4/3, 1/2 or 1/1. This value is
+ * 0 if the aspect ratio is not defined in the source file.
+ * 0 is also the default value.
+ */
+ float mAspect;
+
+#ifdef __cplusplus
+
+ aiCamera() AI_NO_EXCEPT
+ : mUp (0.f,1.f,0.f)
+ , mLookAt (0.f,0.f,1.f)
+ , mHorizontalFOV (0.25f * (float)AI_MATH_PI)
+ , mClipPlaneNear (0.1f)
+ , mClipPlaneFar (1000.f)
+ , mAspect (0.f)
+ {}
+
+ /** @brief Get a *right-handed* camera matrix from me
+ * @param out Camera matrix to be filled
+ */
+ void GetCameraMatrix (aiMatrix4x4& out) const
+ {
+ /** todo: test ... should work, but i'm not absolutely sure */
+
+ /** We don't know whether these vectors are already normalized ...*/
+ aiVector3D zaxis = mLookAt; zaxis.Normalize();
+ aiVector3D yaxis = mUp; yaxis.Normalize();
+ aiVector3D xaxis = mUp^mLookAt; xaxis.Normalize();
+
+ out.a4 = -(xaxis * mPosition);
+ out.b4 = -(yaxis * mPosition);
+ out.c4 = -(zaxis * mPosition);
+
+ out.a1 = xaxis.x;
+ out.a2 = xaxis.y;
+ out.a3 = xaxis.z;
+
+ out.b1 = yaxis.x;
+ out.b2 = yaxis.y;
+ out.b3 = yaxis.z;
+
+ out.c1 = zaxis.x;
+ out.c2 = zaxis.y;
+ out.c3 = zaxis.z;
+
+ out.d1 = out.d2 = out.d3 = 0.f;
+ out.d4 = 1.f;
+ }
+
+#endif
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_CAMERA_H_INC
diff --git a/thirdparty/assimp/include/assimp/cexport.h b/thirdparty/assimp/include/assimp/cexport.h
new file mode 100644
index 0000000000..1d62dc26b3
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/cexport.h
@@ -0,0 +1,261 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2011, 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 cexport.h
+* @brief Defines the C-API for the Assimp export interface
+*/
+#pragma once
+#ifndef AI_EXPORT_H_INC
+#define AI_EXPORT_H_INC
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
+// Public ASSIMP data structures
+#include <assimp/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aiScene; // aiScene.h
+struct aiFileIO; // aiFileIO.h
+
+// --------------------------------------------------------------------------------
+/** Describes an file format which Assimp can export to. Use #aiGetExportFormatCount() to
+* learn how many export formats the current Assimp build supports and #aiGetExportFormatDescription()
+* to retrieve a description of an export format option.
+*/
+struct aiExportFormatDesc
+{
+ /// a short string ID to uniquely identify the export format. Use this ID string to
+ /// specify which file format you want to export to when calling #aiExportScene().
+ /// Example: "dae" or "obj"
+ const char* id;
+
+ /// A short description of the file format to present to users. Useful if you want
+ /// to allow the user to select an export format.
+ const char* description;
+
+ /// Recommended file extension for the exported file in lower case.
+ const char* fileExtension;
+};
+
+
+// --------------------------------------------------------------------------------
+/** Returns the number of export file formats available in the current Assimp build.
+ * Use aiGetExportFormatDescription() to retrieve infos of a specific export format.
+ */
+ASSIMP_API size_t aiGetExportFormatCount(void);
+
+// --------------------------------------------------------------------------------
+/** Returns a description of the nth export file format. Use #aiGetExportFormatCount()
+ * to learn how many export formats are supported. The description must be released by
+ * calling aiReleaseExportFormatDescription afterwards.
+ * @param pIndex Index of the export format to retrieve information for. Valid range is
+ * 0 to #aiGetExportFormatCount()
+ * @return A description of that specific export format. NULL if pIndex is out of range.
+ */
+ASSIMP_API const C_STRUCT aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex);
+
+// --------------------------------------------------------------------------------
+/** Release a description of the nth export file format. Must be returned by
+* aiGetExportFormatDescription
+* @param desc Pointer to the description
+*/
+ASSIMP_API void aiReleaseExportFormatDescription( const C_STRUCT aiExportFormatDesc *desc );
+
+// --------------------------------------------------------------------------------
+/** Create a modifiable copy of a scene.
+ * This is useful to import files via Assimp, change their topology and
+ * export them again. Since the scene returned by the various importer functions
+ * is const, a modifiable copy is needed.
+ * @param pIn Valid scene to be copied
+ * @param pOut Receives a modifyable copy of the scene. Use aiFreeScene() to
+ * delete it again.
+ */
+ASSIMP_API void aiCopyScene(const C_STRUCT aiScene* pIn,
+ C_STRUCT aiScene** pOut);
+
+
+// --------------------------------------------------------------------------------
+/** Frees a scene copy created using aiCopyScene() */
+ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn);
+
+// --------------------------------------------------------------------------------
+/** Exports the given scene to a chosen file format and writes the result file(s) to disk.
+* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function.
+* The scene is expected to conform to Assimp's Importer output format as specified
+* in the @link data Data Structures Page @endlink. In short, this means the model data
+* should use a right-handed coordinate systems, face winding should be counter-clockwise
+* and the UV coordinate origin is assumed to be in the upper left. If your input data
+* uses different conventions, have a look at the last parameter.
+* @param pFormatId ID string to specify to which format you want to export to. Use
+* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available.
+* @param pFileName Output file to write
+* @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated
+* flags, but in reality only a subset of them makes sense here. Specifying
+* 'preprocessing' flags is useful if the input scene does not conform to
+* Assimp's default conventions as specified in the @link data Data Structures Page @endlink.
+* In short, this means the geometry data should use a right-handed coordinate systems, face
+* winding should be counter-clockwise and the UV coordinate origin is assumed to be in
+* the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and
+* #aiProcess_FlipWindingOrder flags are used in the import side to allow users
+* to have those defaults automatically adapted to their conventions. Specifying those flags
+* for exporting has the opposite effect, respectively. Some other of the
+* #aiPostProcessSteps enumerated values may be useful as well, but you'll need
+* to try out what their effect on the exported file is. Many formats impose
+* their own restrictions on the structure of the geometry stored therein,
+* so some preprocessing may have little or no effect at all, or may be
+* redundant as exporters would apply them anyhow. A good example
+* is triangulation - whilst you can enforce it by specifying
+* the #aiProcess_Triangulate flag, most export formats support only
+* triangulate data so they would run the step anyway.
+*
+* If assimp detects that the input scene was directly taken from the importer side of
+* the library (i.e. not copied using aiCopyScene and potetially modified afterwards),
+* any postprocessing steps already applied to the scene will not be applied again, unless
+* they show non-idempotent behaviour (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and
+* #aiProcess_FlipWindingOrder).
+* @return a status code indicating the result of the export
+* @note Use aiCopyScene() to get a modifiable copy of a previously
+* imported scene.
+*/
+ASSIMP_API aiReturn aiExportScene( const C_STRUCT aiScene* pScene,
+ const char* pFormatId,
+ const char* pFileName,
+ unsigned int pPreprocessing);
+
+
+// --------------------------------------------------------------------------------
+/** Exports the given scene to a chosen file format using custom IO logic supplied by you.
+* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function.
+* @param pFormatId ID string to specify to which format you want to export to. Use
+* aiGetExportFormatCount() / aiGetExportFormatDescription() to learn which export formats are available.
+* @param pFileName Output file to write
+* @param pIO custom IO implementation to be used. Use this if you use your own storage methods.
+* If none is supplied, a default implementation using standard file IO is used. Note that
+* #aiExportSceneToBlob is provided as convenience function to export to memory buffers.
+* @param pPreprocessing Please see the documentation for #aiExportScene
+* @return a status code indicating the result of the export
+* @note Include <aiFileIO.h> for the definition of #aiFileIO.
+* @note Use aiCopyScene() to get a modifiable copy of a previously
+* imported scene.
+*/
+ASSIMP_API aiReturn aiExportSceneEx( const C_STRUCT aiScene* pScene,
+ const char* pFormatId,
+ const char* pFileName,
+ C_STRUCT aiFileIO* pIO,
+ unsigned int pPreprocessing );
+
+// --------------------------------------------------------------------------------
+/** Describes a blob of exported scene data. Use #aiExportSceneToBlob() to create a blob containing an
+* exported scene. The memory referred by this structure is owned by Assimp.
+* to free its resources. Don't try to free the memory on your side - it will crash for most build configurations
+* due to conflicting heaps.
+*
+* Blobs can be nested - each blob may reference another blob, which may in turn reference another blob and so on.
+* This is used when exporters write more than one output file for a given #aiScene. See the remarks for
+* #aiExportDataBlob::name for more information.
+*/
+struct aiExportDataBlob
+{
+ /// Size of the data in bytes
+ size_t size;
+
+ /// The data.
+ void* data;
+
+ /** Name of the blob. An empty string always
+ indicates the first (and primary) blob,
+ which contains the actual file data.
+ Any other blobs are auxiliary files produced
+ by exporters (i.e. material files). Existence
+ of such files depends on the file format. Most
+ formats don't split assets across multiple files.
+
+ If used, blob names usually contain the file
+ extension that should be used when writing
+ the data to disc.
+ */
+ C_STRUCT aiString name;
+
+ /** Pointer to the next blob in the chain or NULL if there is none. */
+ C_STRUCT aiExportDataBlob * next;
+
+#ifdef __cplusplus
+ /// Default constructor
+ aiExportDataBlob() { size = 0; data = next = NULL; }
+ /// Releases the data
+ ~aiExportDataBlob() { delete [] static_cast<unsigned char*>( data ); delete next; }
+
+private:
+ // no copying
+ aiExportDataBlob(const aiExportDataBlob& );
+ aiExportDataBlob& operator= (const aiExportDataBlob& );
+#endif // __cplusplus
+};
+
+// --------------------------------------------------------------------------------
+/** Exports the given scene to a chosen file format. Returns the exported data as a binary blob which
+* you can write into a file or something. When you're done with the data, use #aiReleaseExportBlob()
+* to free the resources associated with the export.
+* @param pScene The scene to export. Stays in possession of the caller, is not changed by the function.
+* @param pFormatId ID string to specify to which format you want to export to. Use
+* #aiGetExportFormatCount() / #aiGetExportFormatDescription() to learn which export formats are available.
+* @param pPreprocessing Please see the documentation for #aiExportScene
+* @return the exported data or NULL in case of error
+*/
+ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const C_STRUCT aiScene* pScene, const char* pFormatId,
+ unsigned int pPreprocessing );
+
+// --------------------------------------------------------------------------------
+/** Releases the memory associated with the given exported data. Use this function to free a data blob
+* returned by aiExportScene().
+* @param pData the data blob returned by #aiExportSceneToBlob
+*/
+ASSIMP_API void aiReleaseExportBlob( const C_STRUCT aiExportDataBlob* pData );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ASSIMP_BUILD_NO_EXPORT
+#endif // AI_EXPORT_H_INC
diff --git a/thirdparty/assimp/include/assimp/cfileio.h b/thirdparty/assimp/include/assimp/cfileio.h
new file mode 100644
index 0000000000..8f7ca45469
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/cfileio.h
@@ -0,0 +1,138 @@
+/*
+---------------------------------------------------------------------------
+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 cfileio.h
+ * @brief Defines generic C routines to access memory-mapped files
+ */
+#pragma once
+#ifndef AI_FILEIO_H_INC
+#define AI_FILEIO_H_INC
+
+#include <assimp/types.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+struct aiFileIO;
+struct aiFile;
+
+// aiFile callbacks
+typedef size_t (*aiFileWriteProc) (C_STRUCT aiFile*, const char*, size_t, size_t);
+typedef size_t (*aiFileReadProc) (C_STRUCT aiFile*, char*, size_t,size_t);
+typedef size_t (*aiFileTellProc) (C_STRUCT aiFile*);
+typedef void (*aiFileFlushProc) (C_STRUCT aiFile*);
+typedef C_ENUM aiReturn (*aiFileSeek) (C_STRUCT aiFile*, size_t, C_ENUM aiOrigin);
+
+// aiFileIO callbacks
+typedef C_STRUCT aiFile* (*aiFileOpenProc) (C_STRUCT aiFileIO*, const char*, const char*);
+typedef void (*aiFileCloseProc) (C_STRUCT aiFileIO*, C_STRUCT aiFile*);
+
+// Represents user-defined data
+typedef char* aiUserData;
+
+// ----------------------------------------------------------------------------------
+/** @brief C-API: File system callbacks
+ *
+ * Provided are functions to open and close files. Supply a custom structure to
+ * the import function. If you don't, a default implementation is used. Use custom
+ * file systems to enable reading from other sources, such as ZIPs
+ * or memory locations. */
+struct aiFileIO
+{
+ /** Function used to open a new file
+ */
+ aiFileOpenProc OpenProc;
+
+ /** Function used to close an existing file
+ */
+ aiFileCloseProc CloseProc;
+
+ /** User-defined, opaque data */
+ aiUserData UserData;
+};
+
+// ----------------------------------------------------------------------------------
+/** @brief C-API: File callbacks
+ *
+ * Actually, it's a data structure to wrap a set of fXXXX (e.g fopen)
+ * replacement functions.
+ *
+ * The default implementation of the functions utilizes the fXXX functions from
+ * the CRT. However, you can supply a custom implementation to Assimp by
+ * delivering a custom aiFileIO. Use this to enable reading from other sources,
+ * such as ZIP archives or memory locations. */
+struct aiFile
+{
+ /** Callback to read from a file */
+ aiFileReadProc ReadProc;
+
+ /** Callback to write to a file */
+ aiFileWriteProc WriteProc;
+
+ /** Callback to retrieve the current position of
+ * the file cursor (ftell())
+ */
+ aiFileTellProc TellProc;
+
+ /** Callback to retrieve the size of the file,
+ * in bytes
+ */
+ aiFileTellProc FileSizeProc;
+
+ /** Callback to set the current position
+ * of the file cursor (fseek())
+ */
+ aiFileSeek SeekProc;
+
+ /** Callback to flush the file contents
+ */
+ aiFileFlushProc FlushProc;
+
+ /** User-defined, opaque data
+ */
+ aiUserData UserData;
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif // AI_FILEIO_H_INC
diff --git a/thirdparty/assimp/include/assimp/cimport.h b/thirdparty/assimp/include/assimp/cimport.h
new file mode 100644
index 0000000000..dbd10f1370
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/cimport.h
@@ -0,0 +1,565 @@
+/*
+---------------------------------------------------------------------------
+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 cimport.h
+ * @brief Defines the C-API to the Open Asset Import Library.
+ */
+#pragma once
+#ifndef AI_ASSIMP_H_INC
+#define AI_ASSIMP_H_INC
+
+#include <assimp/types.h>
+#include "importerdesc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aiScene; // aiScene.h
+struct aiFileIO; // aiFileIO.h
+typedef void (*aiLogStreamCallback)(const char* /* message */, char* /* user */);
+
+// --------------------------------------------------------------------------------
+/** C-API: Represents a log stream. A log stream receives all log messages and
+ * streams them _somewhere_.
+ * @see aiGetPredefinedLogStream
+ * @see aiAttachLogStream
+ * @see aiDetachLogStream */
+// --------------------------------------------------------------------------------
+struct aiLogStream
+{
+ /** callback to be called */
+ aiLogStreamCallback callback;
+
+ /** user data to be passed to the callback */
+ char* user;
+};
+
+
+// --------------------------------------------------------------------------------
+/** C-API: Represents an opaque set of settings to be used during importing.
+ * @see aiCreatePropertyStore
+ * @see aiReleasePropertyStore
+ * @see aiImportFileExWithProperties
+ * @see aiSetPropertyInteger
+ * @see aiSetPropertyFloat
+ * @see aiSetPropertyString
+ * @see aiSetPropertyMatrix
+ */
+// --------------------------------------------------------------------------------
+struct aiPropertyStore { char sentinel; };
+
+/** Our own C boolean type */
+typedef int aiBool;
+
+#define AI_FALSE 0
+#define AI_TRUE 1
+
+// --------------------------------------------------------------------------------
+/** Reads the given file and returns its content.
+ *
+ * If the call succeeds, the imported data is returned in an aiScene structure.
+ * The data is intended to be read-only, it stays property of the ASSIMP
+ * library and will be stable until aiReleaseImport() is called. After you're
+ * done with it, call aiReleaseImport() to free the resources associated with
+ * this file. If the import fails, NULL is returned instead. Call
+ * aiGetErrorString() to retrieve a human-readable error text.
+ * @param pFile Path and filename of the file to be imported,
+ * expected to be a null-terminated c-string. NULL is not a valid value.
+ * @param pFlags Optional post processing steps to be executed after
+ * a successful import. Provide a bitwise combination of the
+ * #aiPostProcessSteps flags.
+ * @return Pointer to the imported data or NULL if the import failed.
+ */
+ASSIMP_API const C_STRUCT aiScene* aiImportFile(
+ const char* pFile,
+ unsigned int pFlags);
+
+// --------------------------------------------------------------------------------
+/** Reads the given file using user-defined I/O functions and returns
+ * its content.
+ *
+ * If the call succeeds, the imported data is returned in an aiScene structure.
+ * The data is intended to be read-only, it stays property of the ASSIMP
+ * library and will be stable until aiReleaseImport() is called. After you're
+ * done with it, call aiReleaseImport() to free the resources associated with
+ * this file. If the import fails, NULL is returned instead. Call
+ * aiGetErrorString() to retrieve a human-readable error text.
+ * @param pFile Path and filename of the file to be imported,
+ * expected to be a null-terminated c-string. NULL is not a valid value.
+ * @param pFlags Optional post processing steps to be executed after
+ * a successful import. Provide a bitwise combination of the
+ * #aiPostProcessSteps flags.
+ * @param pFS aiFileIO structure. Will be used to open the model file itself
+ * and any other files the loader needs to open. Pass NULL to use the default
+ * implementation.
+ * @return Pointer to the imported data or NULL if the import failed.
+ * @note Include <aiFileIO.h> for the definition of #aiFileIO.
+ */
+ASSIMP_API const C_STRUCT aiScene* aiImportFileEx(
+ const char* pFile,
+ unsigned int pFlags,
+ C_STRUCT aiFileIO* pFS);
+
+// --------------------------------------------------------------------------------
+/** Same as #aiImportFileEx, but adds an extra parameter containing importer settings.
+ *
+ * @param pFile Path and filename of the file to be imported,
+ * expected to be a null-terminated c-string. NULL is not a valid value.
+ * @param pFlags Optional post processing steps to be executed after
+ * a successful import. Provide a bitwise combination of the
+ * #aiPostProcessSteps flags.
+ * @param pFS aiFileIO structure. Will be used to open the model file itself
+ * and any other files the loader needs to open. Pass NULL to use the default
+ * implementation.
+ * @param pProps #aiPropertyStore instance containing import settings.
+ * @return Pointer to the imported data or NULL if the import failed.
+ * @note Include <aiFileIO.h> for the definition of #aiFileIO.
+ * @see aiImportFileEx
+ */
+ASSIMP_API const C_STRUCT aiScene* aiImportFileExWithProperties(
+ const char* pFile,
+ unsigned int pFlags,
+ C_STRUCT aiFileIO* pFS,
+ const C_STRUCT aiPropertyStore* pProps);
+
+// --------------------------------------------------------------------------------
+/** Reads the given file from a given memory buffer,
+ *
+ * If the call succeeds, the contents of the file are returned as a pointer to an
+ * aiScene object. The returned data is intended to be read-only, the importer keeps
+ * ownership of the data and will destroy it upon destruction. If the import fails,
+ * NULL is returned.
+ * A human-readable error description can be retrieved by calling aiGetErrorString().
+ * @param pBuffer Pointer to the file data
+ * @param pLength Length of pBuffer, in bytes
+ * @param pFlags Optional post processing steps to be executed after
+ * a successful import. Provide a bitwise combination of the
+ * #aiPostProcessSteps flags. If you wish to inspect the imported
+ * scene first in order to fine-tune your post-processing setup,
+ * consider to use #aiApplyPostProcessing().
+ * @param pHint An additional hint to the library. If this is a non empty string,
+ * the library looks for a loader to support the file extension specified by pHint
+ * and passes the file to the first matching loader. If this loader is unable to
+ * completely the request, the library continues and tries to determine the file
+ * format on its own, a task that may or may not be successful.
+ * Check the return value, and you'll know ...
+ * @return A pointer to the imported data, NULL if the import failed.
+ *
+ * @note This is a straightforward way to decode models from memory
+ * buffers, but it doesn't handle model formats that spread their
+ * data across multiple files or even directories. Examples include
+ * OBJ or MD3, which outsource parts of their material info into
+ * external scripts. If you need full functionality, provide
+ * a custom IOSystem to make Assimp find these files and use
+ * the regular aiImportFileEx()/aiImportFileExWithProperties() API.
+ */
+ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemory(
+ const char* pBuffer,
+ unsigned int pLength,
+ unsigned int pFlags,
+ const char* pHint);
+
+// --------------------------------------------------------------------------------
+/** Same as #aiImportFileFromMemory, but adds an extra parameter containing importer settings.
+ *
+ * @param pBuffer Pointer to the file data
+ * @param pLength Length of pBuffer, in bytes
+ * @param pFlags Optional post processing steps to be executed after
+ * a successful import. Provide a bitwise combination of the
+ * #aiPostProcessSteps flags. If you wish to inspect the imported
+ * scene first in order to fine-tune your post-processing setup,
+ * consider to use #aiApplyPostProcessing().
+ * @param pHint An additional hint to the library. If this is a non empty string,
+ * the library looks for a loader to support the file extension specified by pHint
+ * and passes the file to the first matching loader. If this loader is unable to
+ * completely the request, the library continues and tries to determine the file
+ * format on its own, a task that may or may not be successful.
+ * Check the return value, and you'll know ...
+ * @param pProps #aiPropertyStore instance containing import settings.
+ * @return A pointer to the imported data, NULL if the import failed.
+ *
+ * @note This is a straightforward way to decode models from memory
+ * buffers, but it doesn't handle model formats that spread their
+ * data across multiple files or even directories. Examples include
+ * OBJ or MD3, which outsource parts of their material info into
+ * external scripts. If you need full functionality, provide
+ * a custom IOSystem to make Assimp find these files and use
+ * the regular aiImportFileEx()/aiImportFileExWithProperties() API.
+ * @see aiImportFileFromMemory
+ */
+ASSIMP_API const C_STRUCT aiScene* aiImportFileFromMemoryWithProperties(
+ const char* pBuffer,
+ unsigned int pLength,
+ unsigned int pFlags,
+ const char* pHint,
+ const C_STRUCT aiPropertyStore* pProps);
+
+// --------------------------------------------------------------------------------
+/** Apply post-processing to an already-imported scene.
+ *
+ * This is strictly equivalent to calling #aiImportFile()/#aiImportFileEx with the
+ * same flags. However, you can use this separate function to inspect the imported
+ * scene first to fine-tune your post-processing setup.
+ * @param pScene Scene to work on.
+ * @param pFlags Provide a bitwise combination of the #aiPostProcessSteps flags.
+ * @return A pointer to the post-processed data. Post processing is done in-place,
+ * meaning this is still the same #aiScene which you passed for pScene. However,
+ * _if_ post-processing failed, the scene could now be NULL. That's quite a rare
+ * case, post processing steps are not really designed to 'fail'. To be exact,
+ * the #aiProcess_ValidateDataStructure flag is currently the only post processing step
+ * which can actually cause the scene to be reset to NULL.
+ */
+ASSIMP_API const C_STRUCT aiScene* aiApplyPostProcessing(
+ const C_STRUCT aiScene* pScene,
+ unsigned int pFlags);
+
+// --------------------------------------------------------------------------------
+/** Get one of the predefine log streams. This is the quick'n'easy solution to
+ * access Assimp's log system. Attaching a log stream can slightly reduce Assimp's
+ * overall import performance.
+ *
+ * Usage is rather simple (this will stream the log to a file, named log.txt, and
+ * the stdout stream of the process:
+ * @code
+ * struct aiLogStream c;
+ * c = aiGetPredefinedLogStream(aiDefaultLogStream_FILE,"log.txt");
+ * aiAttachLogStream(&c);
+ * c = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL);
+ * aiAttachLogStream(&c);
+ * @endcode
+ *
+ * @param pStreams One of the #aiDefaultLogStream enumerated values.
+ * @param file Solely for the #aiDefaultLogStream_FILE flag: specifies the file to write to.
+ * Pass NULL for all other flags.
+ * @return The log stream. callback is set to NULL if something went wrong.
+ */
+ASSIMP_API C_STRUCT aiLogStream aiGetPredefinedLogStream(
+ C_ENUM aiDefaultLogStream pStreams,
+ const char* file);
+
+// --------------------------------------------------------------------------------
+/** Attach a custom log stream to the libraries' logging system.
+ *
+ * Attaching a log stream can slightly reduce Assimp's overall import
+ * performance. Multiple log-streams can be attached.
+ * @param stream Describes the new log stream.
+ * @note To ensure proper destruction of the logging system, you need to manually
+ * call aiDetachLogStream() on every single log stream you attach.
+ * Alternatively (for the lazy folks) #aiDetachAllLogStreams is provided.
+ */
+ASSIMP_API void aiAttachLogStream(
+ const C_STRUCT aiLogStream* stream);
+
+// --------------------------------------------------------------------------------
+/** Enable verbose logging. Verbose logging includes debug-related stuff and
+ * detailed import statistics. This can have severe impact on import performance
+ * and memory consumption. However, it might be useful to find out why a file
+ * didn't read correctly.
+ * @param d AI_TRUE or AI_FALSE, your decision.
+ */
+ASSIMP_API void aiEnableVerboseLogging(aiBool d);
+
+// --------------------------------------------------------------------------------
+/** Detach a custom log stream from the libraries' logging system.
+ *
+ * This is the counterpart of #aiAttachLogStream. If you attached a stream,
+ * don't forget to detach it again.
+ * @param stream The log stream to be detached.
+ * @return AI_SUCCESS if the log stream has been detached successfully.
+ * @see aiDetachAllLogStreams
+ */
+ASSIMP_API C_ENUM aiReturn aiDetachLogStream(
+ const C_STRUCT aiLogStream* stream);
+
+// --------------------------------------------------------------------------------
+/** Detach all active log streams from the libraries' logging system.
+ * This ensures that the logging system is terminated properly and all
+ * resources allocated by it are actually freed. If you attached a stream,
+ * don't forget to detach it again.
+ * @see aiAttachLogStream
+ * @see aiDetachLogStream
+ */
+ASSIMP_API void aiDetachAllLogStreams(void);
+
+// --------------------------------------------------------------------------------
+/** Releases all resources associated with the given import process.
+ *
+ * Call this function after you're done with the imported data.
+ * @param pScene The imported data to release. NULL is a valid value.
+ */
+ASSIMP_API void aiReleaseImport(
+ const C_STRUCT aiScene* pScene);
+
+// --------------------------------------------------------------------------------
+/** Returns the error text of the last failed import process.
+ *
+ * @return A textual description of the error that occurred at the last
+ * import process. NULL if there was no error. There can't be an error if you
+ * got a non-NULL #aiScene from #aiImportFile/#aiImportFileEx/#aiApplyPostProcessing.
+ */
+ASSIMP_API const char* aiGetErrorString(void);
+
+// --------------------------------------------------------------------------------
+/** Returns whether a given file extension is supported by ASSIMP
+ *
+ * @param szExtension Extension for which the function queries support for.
+ * Must include a leading dot '.'. Example: ".3ds", ".md3"
+ * @return AI_TRUE if the file extension is supported.
+ */
+ASSIMP_API aiBool aiIsExtensionSupported(
+ const char* szExtension);
+
+// --------------------------------------------------------------------------------
+/** Get a list of all file extensions supported by ASSIMP.
+ *
+ * If a file extension is contained in the list this does, of course, not
+ * mean that ASSIMP is able to load all files with this extension.
+ * @param szOut String to receive the extension list.
+ * Format of the list: "*.3ds;*.obj;*.dae". NULL is not a valid parameter.
+ */
+ASSIMP_API void aiGetExtensionList(
+ C_STRUCT aiString* szOut);
+
+// --------------------------------------------------------------------------------
+/** Get the approximated storage required by an imported asset
+ * @param pIn Input asset.
+ * @param in Data structure to be filled.
+ */
+ASSIMP_API void aiGetMemoryRequirements(
+ const C_STRUCT aiScene* pIn,
+ C_STRUCT aiMemoryInfo* in);
+
+
+
+// --------------------------------------------------------------------------------
+/** Create an empty property store. Property stores are used to collect import
+ * settings.
+ * @return New property store. Property stores need to be manually destroyed using
+ * the #aiReleasePropertyStore API function.
+ */
+ASSIMP_API C_STRUCT aiPropertyStore* aiCreatePropertyStore(void);
+
+// --------------------------------------------------------------------------------
+/** Delete a property store.
+ * @param p Property store to be deleted.
+ */
+ASSIMP_API void aiReleasePropertyStore(C_STRUCT aiPropertyStore* p);
+
+// --------------------------------------------------------------------------------
+/** Set an integer property.
+ *
+ * This is the C-version of #Assimp::Importer::SetPropertyInteger(). In the C
+ * interface, properties are always shared by all imports. It is not possible to
+ * specify them per import.
+ *
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
+ * @param szName Name of the configuration property to be set. All supported
+ * public properties are defined in the config.h header file (AI_CONFIG_XXX).
+ * @param value New value for the property
+ */
+ASSIMP_API void aiSetImportPropertyInteger(
+ C_STRUCT aiPropertyStore* store,
+ const char* szName,
+ int value);
+
+// --------------------------------------------------------------------------------
+/** Set a floating-point property.
+ *
+ * This is the C-version of #Assimp::Importer::SetPropertyFloat(). In the C
+ * interface, properties are always shared by all imports. It is not possible to
+ * specify them per import.
+ *
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
+ * @param szName Name of the configuration property to be set. All supported
+ * public properties are defined in the config.h header file (AI_CONFIG_XXX).
+ * @param value New value for the property
+ */
+ASSIMP_API void aiSetImportPropertyFloat(
+ C_STRUCT aiPropertyStore* store,
+ const char* szName,
+ ai_real value);
+
+// --------------------------------------------------------------------------------
+/** Set a string property.
+ *
+ * This is the C-version of #Assimp::Importer::SetPropertyString(). In the C
+ * interface, properties are always shared by all imports. It is not possible to
+ * specify them per import.
+ *
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
+ * @param szName Name of the configuration property to be set. All supported
+ * public properties are defined in the config.h header file (AI_CONFIG_XXX).
+ * @param st New value for the property
+ */
+ASSIMP_API void aiSetImportPropertyString(
+ C_STRUCT aiPropertyStore* store,
+ const char* szName,
+ const C_STRUCT aiString* st);
+
+// --------------------------------------------------------------------------------
+/** Set a matrix property.
+ *
+ * This is the C-version of #Assimp::Importer::SetPropertyMatrix(). In the C
+ * interface, properties are always shared by all imports. It is not possible to
+ * specify them per import.
+ *
+ * @param store Store to modify. Use #aiCreatePropertyStore to obtain a store.
+ * @param szName Name of the configuration property to be set. All supported
+ * public properties are defined in the config.h header file (AI_CONFIG_XXX).
+ * @param mat New value for the property
+ */
+ASSIMP_API void aiSetImportPropertyMatrix(
+ C_STRUCT aiPropertyStore* store,
+ const char* szName,
+ const C_STRUCT aiMatrix4x4* mat);
+
+// --------------------------------------------------------------------------------
+/** Construct a quaternion from a 3x3 rotation matrix.
+ * @param quat Receives the output quaternion.
+ * @param mat Matrix to 'quaternionize'.
+ * @see aiQuaternion(const aiMatrix3x3& pRotMatrix)
+ */
+ASSIMP_API void aiCreateQuaternionFromMatrix(
+ C_STRUCT aiQuaternion* quat,
+ const C_STRUCT aiMatrix3x3* mat);
+
+// --------------------------------------------------------------------------------
+/** Decompose a transformation matrix into its rotational, translational and
+ * scaling components.
+ *
+ * @param mat Matrix to decompose
+ * @param scaling Receives the scaling component
+ * @param rotation Receives the rotational component
+ * @param position Receives the translational component.
+ * @see aiMatrix4x4::Decompose (aiVector3D&, aiQuaternion&, aiVector3D&) const;
+ */
+ASSIMP_API void aiDecomposeMatrix(
+ const C_STRUCT aiMatrix4x4* mat,
+ C_STRUCT aiVector3D* scaling,
+ C_STRUCT aiQuaternion* rotation,
+ C_STRUCT aiVector3D* position);
+
+// --------------------------------------------------------------------------------
+/** Transpose a 4x4 matrix.
+ * @param mat Pointer to the matrix to be transposed
+ */
+ASSIMP_API void aiTransposeMatrix4(
+ C_STRUCT aiMatrix4x4* mat);
+
+// --------------------------------------------------------------------------------
+/** Transpose a 3x3 matrix.
+ * @param mat Pointer to the matrix to be transposed
+ */
+ASSIMP_API void aiTransposeMatrix3(
+ C_STRUCT aiMatrix3x3* mat);
+
+// --------------------------------------------------------------------------------
+/** Transform a vector by a 3x3 matrix
+ * @param vec Vector to be transformed.
+ * @param mat Matrix to transform the vector with.
+ */
+ASSIMP_API void aiTransformVecByMatrix3(
+ C_STRUCT aiVector3D* vec,
+ const C_STRUCT aiMatrix3x3* mat);
+
+// --------------------------------------------------------------------------------
+/** Transform a vector by a 4x4 matrix
+ * @param vec Vector to be transformed.
+ * @param mat Matrix to transform the vector with.
+ */
+ASSIMP_API void aiTransformVecByMatrix4(
+ C_STRUCT aiVector3D* vec,
+ const C_STRUCT aiMatrix4x4* mat);
+
+// --------------------------------------------------------------------------------
+/** Multiply two 4x4 matrices.
+ * @param dst First factor, receives result.
+ * @param src Matrix to be multiplied with 'dst'.
+ */
+ASSIMP_API void aiMultiplyMatrix4(
+ C_STRUCT aiMatrix4x4* dst,
+ const C_STRUCT aiMatrix4x4* src);
+
+// --------------------------------------------------------------------------------
+/** Multiply two 3x3 matrices.
+ * @param dst First factor, receives result.
+ * @param src Matrix to be multiplied with 'dst'.
+ */
+ASSIMP_API void aiMultiplyMatrix3(
+ C_STRUCT aiMatrix3x3* dst,
+ const C_STRUCT aiMatrix3x3* src);
+
+// --------------------------------------------------------------------------------
+/** Get a 3x3 identity matrix.
+ * @param mat Matrix to receive its personal identity
+ */
+ASSIMP_API void aiIdentityMatrix3(
+ C_STRUCT aiMatrix3x3* mat);
+
+// --------------------------------------------------------------------------------
+/** Get a 4x4 identity matrix.
+ * @param mat Matrix to receive its personal identity
+ */
+ASSIMP_API void aiIdentityMatrix4(
+ C_STRUCT aiMatrix4x4* mat);
+
+// --------------------------------------------------------------------------------
+/** Returns the number of import file formats available in the current Assimp build.
+ * Use aiGetImportFormatDescription() to retrieve infos of a specific import format.
+ */
+ASSIMP_API size_t aiGetImportFormatCount(void);
+
+// --------------------------------------------------------------------------------
+/** Returns a description of the nth import file format. Use #aiGetImportFormatCount()
+ * to learn how many import formats are supported.
+ * @param pIndex Index of the import format to retrieve information for. Valid range is
+ * 0 to #aiGetImportFormatCount()
+ * @return A description of that specific import format. NULL if pIndex is out of range.
+ */
+ASSIMP_API const C_STRUCT aiImporterDesc* aiGetImportFormatDescription( size_t pIndex);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_ASSIMP_H_INC
diff --git a/thirdparty/assimp/include/assimp/color4.h b/thirdparty/assimp/include/assimp/color4.h
new file mode 100644
index 0000000000..3c97c8eda2
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/color4.h
@@ -0,0 +1,104 @@
+/*
+---------------------------------------------------------------------------
+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 color4.h
+ * @brief RGBA color structure, including operators when compiling in C++
+ */
+#pragma once
+#ifndef AI_COLOR4D_H_INC
+#define AI_COLOR4D_H_INC
+
+#include "defs.h"
+
+#ifdef __cplusplus
+
+// ----------------------------------------------------------------------------------
+/** Represents a color in Red-Green-Blue space including an
+* alpha component. Color values range from 0 to 1. */
+// ----------------------------------------------------------------------------------
+template <typename TReal>
+class aiColor4t
+{
+public:
+ aiColor4t() AI_NO_EXCEPT : r(), g(), b(), a() {}
+ aiColor4t (TReal _r, TReal _g, TReal _b, TReal _a)
+ : r(_r), g(_g), b(_b), a(_a) {}
+ explicit aiColor4t (TReal _r) : r(_r), g(_r), b(_r), a(_r) {}
+ aiColor4t (const aiColor4t& o) = default;
+
+public:
+ // combined operators
+ const aiColor4t& operator += (const aiColor4t& o);
+ const aiColor4t& operator -= (const aiColor4t& o);
+ const aiColor4t& operator *= (TReal f);
+ const aiColor4t& operator /= (TReal f);
+
+public:
+ // comparison
+ bool operator == (const aiColor4t& other) const;
+ bool operator != (const aiColor4t& other) const;
+ bool operator < (const aiColor4t& other) const;
+
+ // color tuple access, rgba order
+ inline TReal operator[](unsigned int i) const;
+ inline TReal& operator[](unsigned int i);
+
+ /** check whether a color is (close to) black */
+ inline bool IsBlack() const;
+
+public:
+
+ // Red, green, blue and alpha color values
+ TReal r, g, b, a;
+}; // !struct aiColor4D
+
+typedef aiColor4t<ai_real> aiColor4D;
+
+#else
+
+struct aiColor4D {
+ ai_real r, g, b, a;
+};
+
+#endif // __cplusplus
+
+#endif // AI_COLOR4D_H_INC
diff --git a/thirdparty/assimp/include/assimp/color4.inl b/thirdparty/assimp/include/assimp/color4.inl
new file mode 100644
index 0000000000..3192d55f39
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/color4.inl
@@ -0,0 +1,205 @@
+/*
+---------------------------------------------------------------------------
+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 color4.inl
+ * @brief Inline implementation of aiColor4t<TReal> operators
+ */
+#pragma once
+#ifndef AI_COLOR4D_INL_INC
+#define AI_COLOR4D_INL_INC
+
+#ifdef __cplusplus
+#include "color4.h"
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator += (const aiColor4t<TReal>& o) {
+ r += o.r; g += o.g; b += o.b; a += o.a;
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator -= (const aiColor4t<TReal>& o) {
+ r -= o.r; g -= o.g; b -= o.b; a -= o.a;
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator *= (TReal f) {
+ r *= f; g *= f; b *= f; a *= f;
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE const aiColor4t<TReal>& aiColor4t<TReal>::operator /= (TReal f) {
+ r /= f; g /= f; b /= f; a /= f;
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE TReal aiColor4t<TReal>::operator[](unsigned int i) const {
+ switch ( i ) {
+ case 0:
+ return r;
+ case 1:
+ return g;
+ case 2:
+ return b;
+ default:
+ break;
+ }
+ return r;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE TReal& aiColor4t<TReal>::operator[](unsigned int i) {
+ switch ( i ) {
+ case 0:
+ return r;
+ case 1:
+ return g;
+ case 2:
+ return b;
+ default:
+ break;
+ }
+ return r;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE bool aiColor4t<TReal>::operator== (const aiColor4t<TReal>& other) const {
+ return r == other.r && g == other.g && b == other.b && a == other.a;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE bool aiColor4t<TReal>::operator!= (const aiColor4t<TReal>& other) const {
+ return r != other.r || g != other.g || b != other.b || a != other.a;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE bool aiColor4t<TReal>::operator< (const aiColor4t<TReal>& other) const {
+ return r < other.r || (
+ r == other.r && (
+ g < other.g || (
+ g == other.g && (
+ b < other.b || (
+ b == other.b && (
+ a < other.a
+ )
+ )
+ )
+ )
+ )
+ );
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator + (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) {
+ return aiColor4t<TReal>( v1.r + v2.r, v1.g + v2.g, v1.b + v2.b, v1.a + v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator - (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) {
+ return aiColor4t<TReal>( v1.r - v2.r, v1.g - v2.g, v1.b - v2.b, v1.a - v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator * (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) {
+ return aiColor4t<TReal>( v1.r * v2.r, v1.g * v2.g, v1.b * v2.b, v1.a * v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator / (const aiColor4t<TReal>& v1, const aiColor4t<TReal>& v2) {
+ return aiColor4t<TReal>( v1.r / v2.r, v1.g / v2.g, v1.b / v2.b, v1.a / v2.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator * ( TReal f, const aiColor4t<TReal>& v) {
+ return aiColor4t<TReal>( f*v.r, f*v.g, f*v.b, f*v.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator * ( const aiColor4t<TReal>& v, TReal f) {
+ return aiColor4t<TReal>( f*v.r, f*v.g, f*v.b, f*v.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator / ( const aiColor4t<TReal>& v, TReal f) {
+ return v * (1/f);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator / ( TReal f,const aiColor4t<TReal>& v) {
+ return aiColor4t<TReal>(f,f,f,f)/v;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator + ( const aiColor4t<TReal>& v, TReal f) {
+ return aiColor4t<TReal>( f+v.r, f+v.g, f+v.b, f+v.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator - ( const aiColor4t<TReal>& v, TReal f) {
+ return aiColor4t<TReal>( v.r-f, v.g-f, v.b-f, v.a-f);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator + ( TReal f, const aiColor4t<TReal>& v) {
+ return aiColor4t<TReal>( f+v.r, f+v.g, f+v.b, f+v.a);
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE aiColor4t<TReal> operator - ( TReal f, const aiColor4t<TReal>& v) {
+ return aiColor4t<TReal>( f-v.r, f-v.g, f-v.b, f-v.a);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline bool aiColor4t<TReal> :: IsBlack() const {
+ // The alpha component doesn't care here. black is black.
+ static const TReal epsilon = 10e-3f;
+ return std::fabs( r ) < epsilon && std::fabs( g ) < epsilon && std::fabs( b ) < epsilon;
+}
+
+#endif // __cplusplus
+#endif // AI_VECTOR3D_INL_INC
diff --git a/thirdparty/assimp/include/assimp/config.h.in b/thirdparty/assimp/include/assimp/config.h.in
new file mode 100644
index 0000000000..a37ff0b8c8
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/config.h.in
@@ -0,0 +1,992 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2018, 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 config.h
+ * @brief Defines constants for configurable properties for the library
+ *
+ * Typically these properties are set via
+ * #Assimp::Importer::SetPropertyFloat,
+ * #Assimp::Importer::SetPropertyInteger or
+ * #Assimp::Importer::SetPropertyString,
+ * depending on the data type of a property. All properties have a
+ * default value. See the doc for the mentioned methods for more details.
+ *
+ * <br><br>
+ * The corresponding functions for use with the plain-c API are:
+ * #aiSetImportPropertyInteger,
+ * #aiSetImportPropertyFloat,
+ * #aiSetImportPropertyString
+ */
+#pragma once
+#ifndef AI_CONFIG_H_INC
+#define AI_CONFIG_H_INC
+
+
+// ###########################################################################
+// LIBRARY SETTINGS
+// General, global settings
+// ###########################################################################
+
+// ---------------------------------------------------------------------------
+/** @brief Enables time measurements.
+ *
+ * If enabled, measures the time needed for each part of the loading
+ * process (i.e. IO time, importing, postprocessing, ..) and dumps
+ * these timings to the DefaultLogger. See the @link perf Performance
+ * Page@endlink for more information on this topic.
+ *
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_GLOB_MEASURE_TIME \
+ "GLOB_MEASURE_TIME"
+
+
+// ---------------------------------------------------------------------------
+/** @brief Global setting to disable generation of skeleton dummy meshes
+ *
+ * Skeleton dummy meshes are generated as a visualization aid in cases which
+ * the input data contains no geometry, but only animation data.
+ * Property data type: bool. Default value: false
+ */
+// ---------------------------------------------------------------------------
+#define AI_CONFIG_IMPORT_NO_SKELETON_MESHES \
+ "IMPORT_NO_SKELETON_MESHES"
+
+
+
+# if 0 // not implemented yet
+// ---------------------------------------------------------------------------
+/** @brief Set Assimp's multithreading policy.
+ *
+ * This setting is ignored if Assimp was built without boost.thread
+ * support (ASSIMP_BUILD_NO_THREADING, which is implied by ASSIMP_BUILD_BOOST_WORKAROUND).
+ * Possible values are: -1 to let Assimp decide what to do, 0 to disable
+ * multithreading entirely and any number larger than 0 to force a specific
+ * number of threads. Assimp is always free to ignore this settings, which is
+ * merely a hint. Usually, the default value (-1) will be fine. However, if
+ * Assimp is used concurrently from multiple user threads, it might be useful
+ * to limit each Importer instance to a specific number of cores.
+ *
+ * For more information, see the @link threading Threading page@endlink.
+ * Property type: int, default value: -1.
+ */
+#define AI_CONFIG_GLOB_MULTITHREADING \
+ "GLOB_MULTITHREADING"
+#endif
+
+// ###########################################################################
+// POST PROCESSING SETTINGS
+// Various stuff to fine-tune the behavior of a specific post processing step.
+// ###########################################################################
+
+
+// ---------------------------------------------------------------------------
+/** @brief Maximum bone count per mesh for the SplitbyBoneCount step.
+ *
+ * Meshes are split until the maximum number of bones is reached. The default
+ * value is AI_SBBC_DEFAULT_MAX_BONES, which may be altered at
+ * compile-time.
+ * Property data type: integer.
+ */
+// ---------------------------------------------------------------------------
+#define AI_CONFIG_PP_SBBC_MAX_BONES \
+ "PP_SBBC_MAX_BONES"
+
+
+// default limit for bone count
+#if (!defined AI_SBBC_DEFAULT_MAX_BONES)
+# define AI_SBBC_DEFAULT_MAX_BONES 60
+#endif
+
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies the maximum angle that may be between two vertex tangents
+ * that their tangents and bi-tangents are smoothed.
+ *
+ * This applies to the CalcTangentSpace-Step. The angle is specified
+ * in degrees. The maximum value is 175.
+ * Property type: float. Default value: 45 degrees
+ */
+#define AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE \
+ "PP_CT_MAX_SMOOTHING_ANGLE"
+
+// ---------------------------------------------------------------------------
+/** @brief Source UV channel for tangent space computation.
+ *
+ * The specified channel must exist or an error will be raised.
+ * Property type: integer. Default value: 0
+ */
+// ---------------------------------------------------------------------------
+#define AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX \
+ "PP_CT_TEXTURE_CHANNEL_INDEX"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies the maximum angle that may be between two face normals
+ * at the same vertex position that their are smoothed together.
+ *
+ * Sometimes referred to as 'crease angle'.
+ * This applies to the GenSmoothNormals-Step. The angle is specified
+ * in degrees, so 180 is PI. The default value is 175 degrees (all vertex
+ * normals are smoothed). The maximum value is 175, too. Property type: float.
+ * Warning: setting this option may cause a severe loss of performance. The
+ * performance is unaffected if the #AI_CONFIG_FAVOUR_SPEED flag is set but
+ * the output quality may be reduced.
+ */
+#define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE \
+ "PP_GSN_MAX_SMOOTHING_ANGLE"
+
+
+// ---------------------------------------------------------------------------
+/** @brief Sets the colormap (= palette) to be used to decode embedded
+ * textures in MDL (Quake or 3DGS) files.
+ *
+ * This must be a valid path to a file. The file is 768 (256*3) bytes
+ * large and contains RGB triplets for each of the 256 palette entries.
+ * The default value is colormap.lmp. If the file is not found,
+ * a default palette (from Quake 1) is used.
+ * Property type: string.
+ */
+#define AI_CONFIG_IMPORT_MDL_COLORMAP \
+ "IMPORT_MDL_COLORMAP"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_RemoveRedundantMaterials step to
+ * keep materials matching a name in a given list.
+ *
+ * This is a list of 1 to n strings, ' ' serves as delimiter character.
+ * Identifiers containing whitespaces must be enclosed in *single*
+ * quotation marks. For example:<tt>
+ * "keep-me and_me_to anotherMaterialToBeKept \'name with whitespace\'"</tt>.
+ * If a material matches on of these names, it will not be modified or
+ * removed by the postprocessing step nor will other materials be replaced
+ * by a reference to it. <br>
+ * This option might be useful if you are using some magic material names
+ * to pass additional semantics through the content pipeline. This ensures
+ * they won't be optimized away, but a general optimization is still
+ * performed for materials not contained in the list.
+ * Property type: String. Default value: n/a
+ * @note Linefeeds, tabs or carriage returns are treated as whitespace.
+ * Material names are case sensitive.
+ */
+#define AI_CONFIG_PP_RRM_EXCLUDE_LIST \
+ "PP_RRM_EXCLUDE_LIST"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to
+ * keep the scene hierarchy. Meshes are moved to worldspace, but
+ * no optimization is performed (read: meshes with equal materials are not
+ * joined. The total number of meshes won't change).
+ *
+ * This option could be of use for you if the scene hierarchy contains
+ * important additional information which you intend to parse.
+ * For rendering, you can still render all meshes in the scene without
+ * any transformations.
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_PP_PTV_KEEP_HIERARCHY \
+ "PP_PTV_KEEP_HIERARCHY"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to normalize
+ * all vertex components into the [-1,1] range. That is, a bounding box
+ * for the whole scene is computed, the maximum component is taken and all
+ * meshes are scaled appropriately (uniformly of course!).
+ * This might be useful if you don't know the spatial dimension of the input
+ * data*/
+#define AI_CONFIG_PP_PTV_NORMALIZE \
+ "PP_PTV_NORMALIZE"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to use
+ * a users defined matrix as the scene root node transformation before
+ * transforming vertices.
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION \
+ "PP_PTV_ADD_ROOT_TRANSFORMATION"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_PreTransformVertices step to use
+ * a users defined matrix as the scene root node transformation before
+ * transforming vertices. This property correspond to the 'a1' component
+ * of the transformation matrix.
+ * Property type: aiMatrix4x4.
+ */
+#define AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION \
+ "PP_PTV_ROOT_TRANSFORMATION"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_FindDegenerates step to
+ * remove degenerated primitives from the import - immediately.
+ *
+ * The default behaviour converts degenerated triangles to lines and
+ * degenerated lines to points. See the documentation to the
+ * #aiProcess_FindDegenerates step for a detailed example of the various ways
+ * to get rid of these lines and points if you don't want them.
+ * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_PP_FD_REMOVE \
+ "PP_FD_REMOVE"
+
+// ---------------------------------------------------------------------------
+/**
+ * @brief Configures the #aiProcess_FindDegenerates to check the area of a
+ * trinagle to be greates than e-6. If this is not the case the triangle will
+ * be removed if #AI_CONFIG_PP_FD_REMOVE is set to true.
+ */
+#define AI_CONFIG_PP_FD_CHECKAREA \
+ "PP_FD_CHECKAREA"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the #aiProcess_OptimizeGraph step to preserve nodes
+ * matching a name in a given list.
+ *
+ * This is a list of 1 to n strings, ' ' serves as delimiter character.
+ * Identifiers containing whitespaces must be enclosed in *single*
+ * quotation marks. For example:<tt>
+ * "keep-me and_me_to anotherNodeToBeKept \'name with whitespace\'"</tt>.
+ * If a node matches on of these names, it will not be modified or
+ * removed by the postprocessing step.<br>
+ * This option might be useful if you are using some magic node names
+ * to pass additional semantics through the content pipeline. This ensures
+ * they won't be optimized away, but a general optimization is still
+ * performed for nodes not contained in the list.
+ * Property type: String. Default value: n/a
+ * @note Linefeeds, tabs or carriage returns are treated as whitespace.
+ * Node names are case sensitive.
+ */
+#define AI_CONFIG_PP_OG_EXCLUDE_LIST \
+ "PP_OG_EXCLUDE_LIST"
+
+// ---------------------------------------------------------------------------
+/** @brief Set the maximum number of triangles in a mesh.
+ *
+ * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
+ * whether a mesh must be split or not.
+ * @note The default value is AI_SLM_DEFAULT_MAX_TRIANGLES
+ * Property type: integer.
+ */
+#define AI_CONFIG_PP_SLM_TRIANGLE_LIMIT \
+ "PP_SLM_TRIANGLE_LIMIT"
+
+// default value for AI_CONFIG_PP_SLM_TRIANGLE_LIMIT
+#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES)
+# define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the maximum number of vertices in a mesh.
+ *
+ * This is used by the "SplitLargeMeshes" PostProcess-Step to determine
+ * whether a mesh must be split or not.
+ * @note The default value is AI_SLM_DEFAULT_MAX_VERTICES
+ * Property type: integer.
+ */
+#define AI_CONFIG_PP_SLM_VERTEX_LIMIT \
+ "PP_SLM_VERTEX_LIMIT"
+
+// default value for AI_CONFIG_PP_SLM_VERTEX_LIMIT
+#if (!defined AI_SLM_DEFAULT_MAX_VERTICES)
+# define AI_SLM_DEFAULT_MAX_VERTICES 1000000
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the maximum number of bones affecting a single vertex
+ *
+ * This is used by the #aiProcess_LimitBoneWeights PostProcess-Step.
+ * @note The default value is AI_LMW_MAX_WEIGHTS
+ * Property type: integer.*/
+#define AI_CONFIG_PP_LBW_MAX_WEIGHTS \
+ "PP_LBW_MAX_WEIGHTS"
+
+// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
+#if (!defined AI_LMW_MAX_WEIGHTS)
+# define AI_LMW_MAX_WEIGHTS 0x4
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+// ---------------------------------------------------------------------------
+/** @brief Lower the deboning threshold in order to remove more bones.
+ *
+ * This is used by the #aiProcess_Debone PostProcess-Step.
+ * @note The default value is AI_DEBONE_THRESHOLD
+ * Property type: float.*/
+#define AI_CONFIG_PP_DB_THRESHOLD \
+ "PP_DB_THRESHOLD"
+
+// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
+#if (!defined AI_DEBONE_THRESHOLD)
+# define AI_DEBONE_THRESHOLD 1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------------------------------------------------------------------------
+/** @brief Require all bones qualify for deboning before removing any
+ *
+ * This is used by the #aiProcess_Debone PostProcess-Step.
+ * @note The default value is 0
+ * Property type: bool.*/
+#define AI_CONFIG_PP_DB_ALL_OR_NONE \
+ "PP_DB_ALL_OR_NONE"
+
+/** @brief Default value for the #AI_CONFIG_PP_ICL_PTCACHE_SIZE property
+ */
+#ifndef PP_ICL_PTCACHE_SIZE
+# define PP_ICL_PTCACHE_SIZE 12
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the size of the post-transform vertex cache to optimize the
+ * vertices for. This configures the #aiProcess_ImproveCacheLocality step.
+ *
+ * The size is given in vertices. Of course you can't know how the vertex
+ * format will exactly look like after the import returns, but you can still
+ * guess what your meshes will probably have.
+ * @note The default value is #PP_ICL_PTCACHE_SIZE. That results in slight
+ * performance improvements for most nVidia/AMD cards since 2002.
+ * Property type: integer.
+ */
+#define AI_CONFIG_PP_ICL_PTCACHE_SIZE "PP_ICL_PTCACHE_SIZE"
+
+// ---------------------------------------------------------------------------
+/** @brief Enumerates components of the aiScene and aiMesh data structures
+ * that can be excluded from the import using the #aiProcess_RemoveComponent step.
+ *
+ * See the documentation to #aiProcess_RemoveComponent for more details.
+ */
+enum aiComponent
+{
+ /** Normal vectors */
+#ifdef SWIG
+ aiComponent_NORMALS = 0x2,
+#else
+ aiComponent_NORMALS = 0x2u,
+#endif
+
+ /** Tangents and bitangents go always together ... */
+#ifdef SWIG
+ aiComponent_TANGENTS_AND_BITANGENTS = 0x4,
+#else
+ aiComponent_TANGENTS_AND_BITANGENTS = 0x4u,
+#endif
+
+ /** ALL color sets
+ * Use aiComponent_COLORn(N) to specify the N'th set */
+ aiComponent_COLORS = 0x8,
+
+ /** ALL texture UV sets
+ * aiComponent_TEXCOORDn(N) to specify the N'th set */
+ aiComponent_TEXCOORDS = 0x10,
+
+ /** Removes all bone weights from all meshes.
+ * The scenegraph nodes corresponding to the bones are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_BONEWEIGHTS = 0x20,
+
+ /** Removes all node animations (aiScene::mAnimations).
+ * The corresponding scenegraph nodes are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_ANIMATIONS = 0x40,
+
+ /** Removes all embedded textures (aiScene::mTextures) */
+ aiComponent_TEXTURES = 0x80,
+
+ /** Removes all light sources (aiScene::mLights).
+ * The corresponding scenegraph nodes are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_LIGHTS = 0x100,
+
+ /** Removes all cameras (aiScene::mCameras).
+ * The corresponding scenegraph nodes are NOT removed.
+ * use the #aiProcess_OptimizeGraph step to do this */
+ aiComponent_CAMERAS = 0x200,
+
+ /** Removes all meshes (aiScene::mMeshes). */
+ aiComponent_MESHES = 0x400,
+
+ /** Removes all materials. One default material will
+ * be generated, so aiScene::mNumMaterials will be 1. */
+ aiComponent_MATERIALS = 0x800,
+
+
+ /** This value is not used. It is just there to force the
+ * compiler to map this enum to a 32 Bit integer. */
+#ifndef SWIG
+ _aiComponent_Force32Bit = 0x9fffffff
+#endif
+};
+
+// Remove a specific color channel 'n'
+#define aiComponent_COLORSn(n) (1u << (n+20u))
+
+// Remove a specific UV channel 'n'
+#define aiComponent_TEXCOORDSn(n) (1u << (n+25u))
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_RemoveComponent step:
+ * Specifies the parts of the data structure to be removed.
+ *
+ * See the documentation to this step for further details. The property
+ * is expected to be an integer, a bitwise combination of the
+ * #aiComponent flags defined above in this header. The default
+ * value is 0. Important: if no valid mesh is remaining after the
+ * step has been executed (e.g you thought it was funny to specify ALL
+ * of the flags defined above) the import FAILS. Mainly because there is
+ * no data to work on anymore ...
+ */
+#define AI_CONFIG_PP_RVC_FLAGS \
+ "PP_RVC_FLAGS"
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_SortByPType step:
+ * Specifies which primitive types are removed by the step.
+ *
+ * This is a bitwise combination of the aiPrimitiveType flags.
+ * Specifying all of them is illegal, of course. A typical use would
+ * be to exclude all line and point meshes from the import. This
+ * is an integer property, its default value is 0.
+ */
+#define AI_CONFIG_PP_SBP_REMOVE \
+ "PP_SBP_REMOVE"
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_FindInvalidData step:
+ * Specifies the floating-point accuracy for animation values. The step
+ * checks for animation tracks where all frame values are absolutely equal
+ * and removes them. This tweakable controls the epsilon for floating-point
+ * comparisons - two keys are considered equal if the invariant
+ * abs(n0-n1)>epsilon holds true for all vector respectively quaternion
+ * components. The default value is 0.f - comparisons are exact then.
+ */
+#define AI_CONFIG_PP_FID_ANIM_ACCURACY \
+ "PP_FID_ANIM_ACCURACY"
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_FindInvalidData step:
+ * Set to true to ignore texture coordinates. This may be useful if you have
+ * to assign different kind of textures like one for the summer or one for the winter.
+ */
+#define AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS \
+ "PP_FID_IGNORE_TEXTURECOORDS"
+
+// TransformUVCoords evaluates UV scalings
+#define AI_UVTRAFO_SCALING 0x1
+
+// TransformUVCoords evaluates UV rotations
+#define AI_UVTRAFO_ROTATION 0x2
+
+// TransformUVCoords evaluates UV translation
+#define AI_UVTRAFO_TRANSLATION 0x4
+
+// Everything baked together -> default value
+#define AI_UVTRAFO_ALL (AI_UVTRAFO_SCALING | AI_UVTRAFO_ROTATION | AI_UVTRAFO_TRANSLATION)
+
+// ---------------------------------------------------------------------------
+/** @brief Input parameter to the #aiProcess_TransformUVCoords step:
+ * Specifies which UV transformations are evaluated.
+ *
+ * This is a bitwise combination of the AI_UVTRAFO_XXX flags (integer
+ * property, of course). By default all transformations are enabled
+ * (AI_UVTRAFO_ALL).
+ */
+#define AI_CONFIG_PP_TUV_EVALUATE \
+ "PP_TUV_EVALUATE"
+
+// ---------------------------------------------------------------------------
+/** @brief A hint to assimp to favour speed against import quality.
+ *
+ * Enabling this option may result in faster loading, but it needn't.
+ * It represents just a hint to loaders and post-processing steps to use
+ * faster code paths, if possible.
+ * This property is expected to be an integer, != 0 stands for true.
+ * The default value is 0.
+ */
+#define AI_CONFIG_FAVOUR_SPEED \
+ "FAVOUR_SPEED"
+
+
+// ###########################################################################
+// IMPORTER SETTINGS
+// Various stuff to fine-tune the behaviour of specific importer plugins.
+// ###########################################################################
+
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will merge all geometry layers present
+ * in the source file or take only the first.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS \
+ "IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read all materials present in the
+ * source file or take only the referenced materials.
+ *
+ * This is void unless IMPORT_FBX_READ_MATERIALS=1.
+ *
+ * The default value is false (0)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS \
+ "IMPORT_FBX_READ_ALL_MATERIALS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read materials.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_MATERIALS \
+ "IMPORT_FBX_READ_MATERIALS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read embedded textures.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_TEXTURES \
+ "IMPORT_FBX_READ_TEXTURES"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read cameras.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_CAMERAS \
+ "IMPORT_FBX_READ_CAMERAS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read light sources.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_LIGHTS \
+ "IMPORT_FBX_READ_LIGHTS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read animations.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS \
+ "IMPORT_FBX_READ_ANIMATIONS"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will act in strict mode in which only
+ * FBX 2013 is supported and any other sub formats are rejected. FBX 2013
+ * is the primary target for the importer, so this format is best
+ * supported and well-tested.
+ *
+ * The default value is false (0)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_STRICT_MODE \
+ "IMPORT_FBX_STRICT_MODE"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will preserve pivot points for
+ * transformations (as extra nodes). If set to false, pivots and offsets
+ * will be evaluated whenever possible.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS \
+ "IMPORT_FBX_PRESERVE_PIVOTS"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the importer will drop empty animation curves or
+ * animation curves which match the bind pose transformation over their
+ * entire defined range.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES \
+ "IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES"
+
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will use the legacy embedded texture naming.
+*
+* The default value is false (0)
+* Property type: bool
+*/
+#define AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING \
+ "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"
+
+// ---------------------------------------------------------------------------
+/** @brief Set the vertex animation keyframe to be imported
+ *
+ * ASSIMP does not support vertex keyframes (only bone animation is supported).
+ * The library reads only one frame of models with vertex animations.
+ * By default this is the first frame.
+ * \note The default value is 0. This option applies to all importers.
+ * However, it is also possible to override the global setting
+ * for a specific loader. You can use the AI_CONFIG_IMPORT_XXX_KEYFRAME
+ * options (where XXX is a placeholder for the file format for which you
+ * want to override the global setting).
+ * Property type: integer.
+ */
+#define AI_CONFIG_IMPORT_GLOBAL_KEYFRAME "IMPORT_GLOBAL_KEYFRAME"
+
+#define AI_CONFIG_IMPORT_MD3_KEYFRAME "IMPORT_MD3_KEYFRAME"
+#define AI_CONFIG_IMPORT_MD2_KEYFRAME "IMPORT_MD2_KEYFRAME"
+#define AI_CONFIG_IMPORT_MDL_KEYFRAME "IMPORT_MDL_KEYFRAME"
+#define AI_CONFIG_IMPORT_MDC_KEYFRAME "IMPORT_MDC_KEYFRAME"
+#define AI_CONFIG_IMPORT_SMD_KEYFRAME "IMPORT_SMD_KEYFRAME"
+#define AI_CONFIG_IMPORT_UNREAL_KEYFRAME "IMPORT_UNREAL_KEYFRAME"
+
+// ---------------------------------------------------------------------------
+/** Smd load multiple animations
+ *
+ * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_SMD_LOAD_ANIMATION_LIST "IMPORT_SMD_LOAD_ANIMATION_LIST"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the AC loader to collect all surfaces which have the
+ * "Backface cull" flag set in separate meshes.
+ *
+ * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL \
+ "IMPORT_AC_SEPARATE_BFCULL"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures whether the AC loader evaluates subdivision surfaces (
+ * indicated by the presence of the 'subdiv' attribute in the file). By
+ * default, Assimp performs the subdivision using the standard
+ * Catmull-Clark algorithm
+ *
+ * * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION \
+ "IMPORT_AC_EVAL_SUBDIVISION"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the UNREAL 3D loader to separate faces with different
+ * surface flags (e.g. two-sided vs. single-sided).
+ *
+ * * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS \
+ "UNREAL_HANDLE_FLAGS"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the terragen import plugin to compute uv's for
+ * terrains, if not given. Furthermore a default texture is assigned.
+ *
+ * UV coordinates for terrains are so simple to compute that you'll usually
+ * want to compute them on your own, if you need them. This option is intended
+ * for model viewers which want to offer an easy way to apply textures to
+ * terrains.
+ * * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_TER_MAKE_UVS \
+ "IMPORT_TER_MAKE_UVS"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the ASE loader to always reconstruct normal vectors
+ * basing on the smoothing groups loaded from the file.
+ *
+ * Some ASE files have carry invalid normals, other don't.
+ * * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS \
+ "IMPORT_ASE_RECONSTRUCT_NORMALS"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the M3D loader to detect and process multi-part
+ * Quake player models.
+ *
+ * These models usually consist of 3 files, lower.md3, upper.md3 and
+ * head.md3. If this property is set to true, Assimp will try to load and
+ * combine all three files if one of them is loaded.
+ * Property type: bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART \
+ "IMPORT_MD3_HANDLE_MULTIPART"
+
+// ---------------------------------------------------------------------------
+/** @brief Tells the MD3 loader which skin files to load.
+ *
+ * When loading MD3 files, Assimp checks whether a file
+ * [md3_file_name]_[skin_name].skin is existing. These files are used by
+ * Quake III to be able to assign different skins (e.g. red and blue team)
+ * to models. 'default', 'red', 'blue' are typical skin names.
+ * Property type: String. Default value: "default".
+ */
+#define AI_CONFIG_IMPORT_MD3_SKIN_NAME \
+ "IMPORT_MD3_SKIN_NAME"
+
+// ---------------------------------------------------------------------------
+/** @brief Specify the Quake 3 shader file to be used for a particular
+ * MD3 file. This can also be a search path.
+ *
+ * By default Assimp's behaviour is as follows: If a MD3 file
+ * <tt>any_path/models/any_q3_subdir/model_name/file_name.md3</tt> is
+ * loaded, the library tries to locate the corresponding shader file in
+ * <tt>any_path/scripts/model_name.shader</tt>. This property overrides this
+ * behaviour. It can either specify a full path to the shader to be loaded
+ * or alternatively the path (relative or absolute) to the directory where
+ * the shaders for all MD3s to be loaded reside. Assimp attempts to open
+ * <tt>IMPORT_MD3_SHADER_SRC/model_name.shader</tt> first, <tt>IMPORT_MD3_SHADER_SRC/file_name.shader</tt>
+ * is the fallback file. Note that IMPORT_MD3_SHADER_SRC should have a terminal (back)slash.
+ * Property type: String. Default value: n/a.
+ */
+#define AI_CONFIG_IMPORT_MD3_SHADER_SRC \
+ "IMPORT_MD3_SHADER_SRC"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the LWO loader to load just one layer from the model.
+ *
+ * LWO files consist of layers and in some cases it could be useful to load
+ * only one of them. This property can be either a string - which specifies
+ * the name of the layer - or an integer - the index of the layer. If the
+ * property is not set the whole LWO model is loaded. Loading fails if the
+ * requested layer is not available. The layer index is zero-based and the
+ * layer name may not be empty.<br>
+ * Property type: Integer. Default value: all layers are loaded.
+ */
+#define AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY \
+ "IMPORT_LWO_ONE_LAYER_ONLY"
+
+// ---------------------------------------------------------------------------
+/** @brief Configures the MD5 loader to not load the MD5ANIM file for
+ * a MD5MESH file automatically.
+ *
+ * The default strategy is to look for a file with the same name but the
+ * MD5ANIM extension in the same directory. If it is found, it is loaded
+ * and combined with the MD5MESH file. This configuration option can be
+ * used to disable this behaviour.
+ *
+ * * Property type: bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD \
+ "IMPORT_MD5_NO_ANIM_AUTOLOAD"
+
+// ---------------------------------------------------------------------------
+/** @brief Defines the begin of the time range for which the LWS loader
+ * evaluates animations and computes aiNodeAnim's.
+ *
+ * Assimp provides full conversion of LightWave's envelope system, including
+ * pre and post conditions. The loader computes linearly subsampled animation
+ * chanels with the frame rate given in the LWS file. This property defines
+ * the start time. Note: animation channels are only generated if a node
+ * has at least one envelope with more tan one key assigned. This property.
+ * is given in frames, '0' is the first frame. By default, if this property
+ * is not set, the importer takes the animation start from the input LWS
+ * file ('FirstFrame' line)<br>
+ * Property type: Integer. Default value: taken from file.
+ *
+ * @see AI_CONFIG_IMPORT_LWS_ANIM_END - end of the imported time range
+ */
+#define AI_CONFIG_IMPORT_LWS_ANIM_START \
+ "IMPORT_LWS_ANIM_START"
+#define AI_CONFIG_IMPORT_LWS_ANIM_END \
+ "IMPORT_LWS_ANIM_END"
+
+// ---------------------------------------------------------------------------
+/** @brief Defines the output frame rate of the IRR loader.
+ *
+ * IRR animations are difficult to convert for Assimp and there will
+ * always be a loss of quality. This setting defines how many keys per second
+ * are returned by the converter.<br>
+ * Property type: integer. Default value: 100
+ */
+#define AI_CONFIG_IMPORT_IRR_ANIM_FPS \
+ "IMPORT_IRR_ANIM_FPS"
+
+// ---------------------------------------------------------------------------
+/** @brief Ogre Importer will try to find referenced materials from this file.
+ *
+ * Ogre meshes reference with material names, this does not tell Assimp the file
+ * where it is located in. Assimp will try to find the source file in the following
+ * order: <material-name>.material, <mesh-filename-base>.material and
+ * lastly the material name defined by this config property.
+ * <br>
+ * Property type: String. Default value: Scene.material.
+ */
+#define AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE \
+ "IMPORT_OGRE_MATERIAL_FILE"
+
+// ---------------------------------------------------------------------------
+/** @brief Ogre Importer detect the texture usage from its filename.
+ *
+ * Ogre material texture units do not define texture type, the textures usage
+ * depends on the used shader or Ogre's fixed pipeline. If this config property
+ * is true Assimp will try to detect the type from the textures filename postfix:
+ * _n, _nrm, _nrml, _normal, _normals and _normalmap for normal map, _s, _spec,
+ * _specular and _specularmap for specular map, _l, _light, _lightmap, _occ
+ * and _occlusion for light map, _disp and _displacement for displacement map.
+ * The matching is case insensitive. Post fix is taken between the last
+ * underscore and the last period.
+ * Default behavior is to detect type from lower cased texture unit name by
+ * matching against: normalmap, specularmap, lightmap and displacementmap.
+ * For both cases if no match is found aiTextureType_DIFFUSE is used.
+ * <br>
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME \
+ "IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME"
+
+ /** @brief Specifies whether the Android JNI asset extraction is supported.
+ *
+ * Turn on this option if you want to manage assets in native
+ * Android application without having to keep the internal directory and asset
+ * manager pointer.
+ */
+ #define AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT "AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the IFC loader skips over IfcSpace elements.
+ *
+ * IfcSpace elements (and their geometric representations) are used to
+ * represent, well, free space in a building storey.<br>
+ * Property type: Bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS "IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the IFC loader will use its own, custom triangulation
+ * algorithm to triangulate wall and floor meshes.
+ *
+ * If this property is set to false, walls will be either triangulated by
+ * #aiProcess_Triangulate or will be passed through as huge polygons with
+ * faked holes (i.e. holes that are connected with the outer boundary using
+ * a dummy edge). It is highly recommended to set this property to true
+ * if you want triangulated data because #aiProcess_Triangulate is known to
+ * have problems with the kind of polygons that the IFC loader spits out for
+ * complicated meshes.
+ * Property type: Bool. Default value: true.
+ */
+#define AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION "IMPORT_IFC_CUSTOM_TRIANGULATION"
+
+// ---------------------------------------------------------------------------
+/** @brief Set the tessellation conic angle for IFC smoothing curves.
+ *
+ * This is used by the IFC importer to determine the tessellation parameter
+ * for smoothing curves.
+ * @note The default value is AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE and the
+ * accepted values are in range [5.0, 120.0].
+ * Property type: Float.
+ */
+#define AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE "IMPORT_IFC_SMOOTHING_ANGLE"
+
+// default value for AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE
+#if (!defined AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE)
+# define AI_IMPORT_IFC_DEFAULT_SMOOTHING_ANGLE 10.0f
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Set the tessellation for IFC cylindrical shapes.
+ *
+ * This is used by the IFC importer to determine the tessellation parameter
+ * for cylindrical shapes, i.e. the number of segments used to approximate a circle.
+ * @note The default value is AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION and the
+ * accepted values are in range [3, 180].
+ * Property type: Integer.
+ */
+#define AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION "IMPORT_IFC_CYLINDRICAL_TESSELLATION"
+
+// default value for AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION
+#if (!defined AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION)
+# define AI_IMPORT_IFC_DEFAULT_CYLINDRICAL_TESSELLATION 32
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the Collada loader will ignore the provided up direction.
+ *
+ * If this property is set to true, the up direction provided in the file header will
+ * be ignored and the file will be loaded as is.
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION"
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies whether the Collada loader should use Collada names as node names.
+ *
+ * If this property is set to true, the Collada names will be used as the
+ * node name. The default is to use the id tag (resp. sid tag, if no id tag is present)
+ * instead.
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES "IMPORT_COLLADA_USE_COLLADA_NAMES"
+
+// ---------- All the Export defines ------------
+
+/** @brief Specifies the xfile use double for real values of float
+ *
+ * Property type: Bool. Default value: false.
+ */
+
+#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT"
+
+/**
+ *
+ */
+#define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS"
+
+/**
+ * @brief Specifies a gobal key factor for scale, float value
+ */
+#define AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY "GLOBAL_SCALE_FACTOR"
+
+#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT)
+# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------- All the Build/Compile-time defines ------------
+
+/** @brief Specifies if double precision is supported inside assimp
+ *
+ * Property type: Bool. Default value: undefined.
+ */
+
+#cmakedefine ASSIMP_DOUBLE_PRECISION 1
+
+#endif // !! AI_CONFIG_H_INC
diff --git a/thirdparty/assimp/include/assimp/defs.h b/thirdparty/assimp/include/assimp/defs.h
new file mode 100644
index 0000000000..4a177e3c3e
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/defs.h
@@ -0,0 +1,303 @@
+/*
+---------------------------------------------------------------------------
+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 defs.h
+ * @brief Assimp build configuration setup. See the notes in the comment
+ * blocks to find out how to customize _your_ Assimp build.
+ */
+
+#pragma once
+#ifndef AI_DEFINES_H_INC
+#define AI_DEFINES_H_INC
+
+#include <assimp/config.h>
+
+//////////////////////////////////////////////////////////////////////////
+/* Define ASSIMP_BUILD_NO_XX_IMPORTER to disable a specific
+ * file format loader. The loader is be excluded from the
+ * build in this case. 'XX' stands for the most common file
+ * extension of the file format. E.g.:
+ * ASSIMP_BUILD_NO_X_IMPORTER disables the X loader.
+ *
+ * If you're unsure about that, take a look at the implementation of the
+ * import plugin you wish to disable. You'll find the right define in the
+ * first lines of the corresponding unit.
+ *
+ * Other (mixed) configuration switches are listed here:
+ * ASSIMP_BUILD_NO_COMPRESSED_X
+ * - Disable support for compressed X files (zip)
+ * ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ * - Disable support for compressed Blender files (zip)
+ * ASSIMP_BUILD_NO_COMPRESSED_IFC
+ * - Disable support for IFCZIP files (unzip)
+ */
+//////////////////////////////////////////////////////////////////////////
+
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_X
+# define ASSIMP_BUILD_NEED_Z_INFLATE
+#endif
+
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+# define ASSIMP_BUILD_NEED_Z_INFLATE
+#endif
+
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
+# define ASSIMP_BUILD_NEED_Z_INFLATE
+# define ASSIMP_BUILD_NEED_UNZIP
+#endif
+
+#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
+# define ASSIMP_BUILD_NEED_Z_INFLATE
+# define ASSIMP_BUILD_NEED_UNZIP
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+/* Define ASSIMP_BUILD_NO_XX_PROCESS to disable a specific
+ * post processing step. This is the current list of process names ('XX'):
+ * CALCTANGENTS
+ * JOINVERTICES
+ * TRIANGULATE
+ * DROPFACENORMALS
+ * GENFACENORMALS
+ * GENVERTEXNORMALS
+ * REMOVEVC
+ * SPLITLARGEMESHES
+ * PRETRANSFORMVERTICES
+ * LIMITBONEWEIGHTS
+ * VALIDATEDS
+ * IMPROVECACHELOCALITY
+ * FIXINFACINGNORMALS
+ * REMOVE_REDUNDANTMATERIALS
+ * OPTIMIZEGRAPH
+ * SORTBYPTYPE
+ * FINDINVALIDDATA
+ * TRANSFORMTEXCOORDS
+ * GENUVCOORDS
+ * ENTITYMESHBUILDER
+ * EMBEDTEXTURES
+ * MAKELEFTHANDED
+ * FLIPUVS
+ * FLIPWINDINGORDER
+ * OPTIMIZEMESHES
+ * OPTIMIZEANIMS
+ * OPTIMIZEGRAPH
+ * GENENTITYMESHES
+ * FIXTEXTUREPATHS */
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef _MSC_VER
+# undef ASSIMP_API
+
+ //////////////////////////////////////////////////////////////////////////
+ /* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */
+ //////////////////////////////////////////////////////////////////////////
+# ifdef ASSIMP_BUILD_DLL_EXPORT
+# define ASSIMP_API __declspec(dllexport)
+# define ASSIMP_API_WINONLY __declspec(dllexport)
+# pragma warning (disable : 4251)
+
+ //////////////////////////////////////////////////////////////////////////
+ /* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in
+ * an external DLL under Windows. Default is static linkage. */
+ //////////////////////////////////////////////////////////////////////////
+# elif (defined ASSIMP_DLL)
+# define ASSIMP_API __declspec(dllimport)
+# define ASSIMP_API_WINONLY __declspec(dllimport)
+# else
+# define ASSIMP_API
+# define ASSIMP_API_WINONLY
+# endif
+
+ /* Force the compiler to inline a function, if possible
+ */
+# define AI_FORCE_INLINE __forceinline
+
+ /* Tells the compiler that a function never returns. Used in code analysis
+ * to skip dead paths (e.g. after an assertion evaluated to false). */
+# define AI_WONT_RETURN __declspec(noreturn)
+
+#elif defined(SWIG)
+
+ /* Do nothing, the relevant defines are all in AssimpSwigPort.i */
+
+#else
+
+# define AI_WONT_RETURN
+
+# define ASSIMP_API __attribute__ ((visibility("default")))
+# define ASSIMP_API_WINONLY
+# define AI_FORCE_INLINE inline
+#endif // (defined _MSC_VER)
+
+#ifdef __GNUC__
+# define AI_WONT_RETURN_SUFFIX __attribute__((noreturn))
+#else
+# define AI_WONT_RETURN_SUFFIX
+#endif // (defined __clang__)
+
+#ifdef __cplusplus
+ /* No explicit 'struct' and 'enum' tags for C++, this keeps showing up
+ * in doxydocs.
+ */
+# define C_STRUCT
+# define C_ENUM
+#else
+ //////////////////////////////////////////////////////////////////////////
+ /* To build the documentation, make sure ASSIMP_DOXYGEN_BUILD
+ * is defined by Doxygen's preprocessor. The corresponding
+ * entries in the DOXYFILE are: */
+ //////////////////////////////////////////////////////////////////////////
+#if 0
+ ENABLE_PREPROCESSING = YES
+ MACRO_EXPANSION = YES
+ EXPAND_ONLY_PREDEF = YES
+ SEARCH_INCLUDES = YES
+ INCLUDE_PATH =
+ INCLUDE_FILE_PATTERNS =
+ PREDEFINED = ASSIMP_DOXYGEN_BUILD=1
+ EXPAND_AS_DEFINED = C_STRUCT C_ENUM
+ SKIP_FUNCTION_MACROS = YES
+#endif
+ //////////////////////////////////////////////////////////////////////////
+ /* Doxygen gets confused if we use c-struct typedefs to avoid
+ * the explicit 'struct' notation. This trick here has the same
+ * effect as the TYPEDEF_HIDES_STRUCT option, but we don't need
+ * to typedef all structs/enums. */
+ //////////////////////////////////////////////////////////////////////////
+# if (defined ASSIMP_DOXYGEN_BUILD)
+# define C_STRUCT
+# define C_ENUM
+# else
+# define C_STRUCT struct
+# define C_ENUM enum
+# endif
+#endif
+
+#if (defined(__BORLANDC__) || defined (__BCPLUSPLUS__))
+#error Currently, Borland is unsupported. Feel free to port Assimp.
+
+// "W8059 Packgröße der Struktur geändert"
+
+#endif
+
+
+ //////////////////////////////////////////////////////////////////////////
+ /* Define ASSIMP_BUILD_SINGLETHREADED to compile assimp
+ * without threading support. The library doesn't utilize
+ * threads then and is itself not threadsafe. */
+ //////////////////////////////////////////////////////////////////////////
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+# define ASSIMP_BUILD_SINGLETHREADED
+#endif
+
+#if defined(_DEBUG) || ! defined(NDEBUG)
+# define ASSIMP_BUILD_DEBUG
+#endif
+
+ //////////////////////////////////////////////////////////////////////////
+ /* Define ASSIMP_DOUBLE_PRECISION to compile assimp
+ * with double precision support (64-bit). */
+ //////////////////////////////////////////////////////////////////////////
+
+#ifdef ASSIMP_DOUBLE_PRECISION
+ typedef double ai_real;
+ typedef signed long long int ai_int;
+ typedef unsigned long long int ai_uint;
+#else // ASSIMP_DOUBLE_PRECISION
+ typedef float ai_real;
+ typedef signed int ai_int;
+ typedef unsigned int ai_uint;
+#endif // ASSIMP_DOUBLE_PRECISION
+
+ //////////////////////////////////////////////////////////////////////////
+ /* Useful constants */
+ //////////////////////////////////////////////////////////////////////////
+
+/* This is PI. Hi PI. */
+#define AI_MATH_PI (3.141592653589793238462643383279 )
+#define AI_MATH_TWO_PI (AI_MATH_PI * 2.0)
+#define AI_MATH_HALF_PI (AI_MATH_PI * 0.5)
+
+/* And this is to avoid endless casts to float */
+#define AI_MATH_PI_F (3.1415926538f)
+#define AI_MATH_TWO_PI_F (AI_MATH_PI_F * 2.0f)
+#define AI_MATH_HALF_PI_F (AI_MATH_PI_F * 0.5f)
+
+/* Tiny macro to convert from radians to degrees and back */
+#define AI_DEG_TO_RAD(x) ((x)*(ai_real)0.0174532925)
+#define AI_RAD_TO_DEG(x) ((x)*(ai_real)57.2957795)
+
+/* Support for big-endian builds */
+#if defined(__BYTE_ORDER__)
+# if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# if !defined(__BIG_ENDIAN__)
+# define __BIG_ENDIAN__
+# endif
+# else /* little endian */
+# if defined (__BIG_ENDIAN__)
+# undef __BIG_ENDIAN__
+# endif
+# endif
+#endif
+#if defined(__BIG_ENDIAN__)
+# define AI_BUILD_BIG_ENDIAN
+#endif
+
+
+/* To avoid running out of memory
+ * This can be adjusted for specific use cases
+ * It's NOT a total limit, just a limit for individual allocations
+ */
+#define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type))
+
+#ifndef _MSC_VER
+# define AI_NO_EXCEPT noexcept
+#else
+# if (_MSC_VER == 1915 )
+# define AI_NO_EXCEPT noexcept
+# else
+# define AI_NO_EXCEPT
+# endif
+#endif
+
+#endif // !! AI_DEFINES_H_INC
diff --git a/thirdparty/assimp/include/assimp/fast_atof.h b/thirdparty/assimp/include/assimp/fast_atof.h
new file mode 100644
index 0000000000..62ea969e97
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/fast_atof.h
@@ -0,0 +1,373 @@
+#pragma once
+
+// Copyright (C) 2002-2007 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine" and the "irrXML" project.
+// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
+
+// ------------------------------------------------------------------------------------
+// Original description: (Schrompf)
+// Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a
+// float inside a large string. Before parsing, it does a strlen on the given point.
+// Changes:
+// 22nd October 08 (Aramis_acg): Added temporary cast to double, added strtoul10_64
+// to ensure long numbers are handled correctly
+// ------------------------------------------------------------------------------------
+
+
+#ifndef FAST_A_TO_F_H_INCLUDED
+#define FAST_A_TO_F_H_INCLUDED
+
+#include <cmath>
+#include <limits>
+#include <stdint.h>
+#include <stdexcept>
+#include <assimp/defs.h>
+
+#include "StringComparison.h"
+#include <assimp/DefaultLogger.hpp>
+
+#ifdef _MSC_VER
+# include <stdint.h>
+#else
+# include <assimp/Compiler/pstdint.h>
+#endif
+
+namespace Assimp {
+
+const double fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug
+ 0.0,
+ 0.1,
+ 0.01,
+ 0.001,
+ 0.0001,
+ 0.00001,
+ 0.000001,
+ 0.0000001,
+ 0.00000001,
+ 0.000000001,
+ 0.0000000001,
+ 0.00000000001,
+ 0.000000000001,
+ 0.0000000000001,
+ 0.00000000000001,
+ 0.000000000000001
+};
+
+
+// ------------------------------------------------------------------------------------
+// Convert a string in decimal format to a number
+// ------------------------------------------------------------------------------------
+inline
+unsigned int strtoul10( const char* in, const char** out=0) {
+ unsigned int value = 0;
+
+ for ( ;; ) {
+ if ( *in < '0' || *in > '9' ) {
+ break;
+ }
+
+ value = ( value * 10 ) + ( *in - '0' );
+ ++in;
+ }
+ if ( out ) {
+ *out = in;
+ }
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert a string in octal format to a number
+// ------------------------------------------------------------------------------------
+inline
+unsigned int strtoul8( const char* in, const char** out=0) {
+ unsigned int value( 0 );
+ for ( ;; ) {
+ if ( *in < '0' || *in > '7' ) {
+ break;
+ }
+
+ value = ( value << 3 ) + ( *in - '0' );
+ ++in;
+ }
+ if ( out ) {
+ *out = in;
+ }
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert a string in hex format to a number
+// ------------------------------------------------------------------------------------
+inline
+unsigned int strtoul16( const char* in, const char** out=0) {
+ unsigned int value( 0 );
+ for ( ;; ) {
+ if ( *in >= '0' && *in <= '9' ) {
+ value = ( value << 4u ) + ( *in - '0' );
+ } else if (*in >= 'A' && *in <= 'F') {
+ value = ( value << 4u ) + ( *in - 'A' ) + 10;
+ } else if (*in >= 'a' && *in <= 'f') {
+ value = ( value << 4u ) + ( *in - 'a' ) + 10;
+ } else {
+ break;
+ }
+ ++in;
+ }
+ if ( out ) {
+ *out = in;
+ }
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert just one hex digit
+// Return value is UINT_MAX if the input character is not a hex digit.
+// ------------------------------------------------------------------------------------
+inline
+unsigned int HexDigitToDecimal(char in) {
+ unsigned int out( UINT_MAX );
+ if ( in >= '0' && in <= '9' ) {
+ out = in - '0';
+ } else if ( in >= 'a' && in <= 'f' ) {
+ out = 10u + in - 'a';
+ } else if ( in >= 'A' && in <= 'F' ) {
+ out = 10u + in - 'A';
+ }
+
+ // return value is UINT_MAX if the input is not a hex digit
+ return out;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert a hex-encoded octet (2 characters, i.e. df or 1a).
+// ------------------------------------------------------------------------------------
+inline
+uint8_t HexOctetToDecimal(const char* in) {
+ return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]);
+}
+
+// ------------------------------------------------------------------------------------
+// signed variant of strtoul10
+// ------------------------------------------------------------------------------------
+inline
+int strtol10( const char* in, const char** out=0) {
+ bool inv = (*in=='-');
+ if ( inv || *in == '+' ) {
+ ++in;
+ }
+
+ int value = strtoul10(in,out);
+ if (inv) {
+ value = -value;
+ }
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Parse a C++-like integer literal - hex and oct prefixes.
+// 0xNNNN - hex
+// 0NNN - oct
+// NNN - dec
+// ------------------------------------------------------------------------------------
+inline
+unsigned int strtoul_cppstyle( const char* in, const char** out=0) {
+ if ('0' == in[0]) {
+ return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out);
+ }
+ return strtoul10(in, out);
+}
+
+// ------------------------------------------------------------------------------------
+// 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, const char** out=0, unsigned int* max_inout=0) {
+ unsigned int cur = 0;
+ uint64_t value = 0;
+
+ if ( *in < '0' || *in > '9' ) {
+ throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." );
+ }
+
+ 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 ) {
+ ASSIMP_LOG_WARN_F( "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;
+}
+
+// ------------------------------------------------------------------------------------
+// signed variant of strtoul10_64
+// ------------------------------------------------------------------------------------
+inline
+int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) {
+ bool inv = (*in == '-');
+ if ( inv || *in == '+' ) {
+ ++in;
+ }
+
+ int64_t value = strtoul10_64(in, out, max_inout);
+ if (inv) {
+ value = -value;
+ }
+ return value;
+}
+
+// Number of relevant decimals for floating-point parsing.
+#define AI_FAST_ATOF_RELAVANT_DECIMALS 15
+
+// ------------------------------------------------------------------------------------
+//! Provides a fast function for converting a string into a float,
+//! about 6 times faster than atof in win32.
+// If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
+// ------------------------------------------------------------------------------------
+template<typename Real>
+inline
+const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) {
+ Real f = 0;
+
+ bool inv = (*c == '-');
+ if (inv || *c == '+') {
+ ++c;
+ }
+
+ if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) {
+ out = std::numeric_limits<Real>::quiet_NaN();
+ c += 3;
+ return c;
+ }
+
+ if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) {
+ out = std::numeric_limits<Real>::infinity();
+ if (inv) {
+ out = -out;
+ }
+ c += 3;
+ if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) {
+ c += 5;
+ }
+ return c;
+ }
+
+ if (!(c[0] >= '0' && c[0] <= '9') &&
+ !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) {
+ throw std::invalid_argument("Cannot parse string "
+ "as real number: does not start with digit "
+ "or decimal point followed by digit.");
+ }
+
+ if (*c != '.' && (! check_comma || c[0] != ',')) {
+ f = static_cast<Real>( strtoul10_64 ( c, &c) );
+ }
+
+ if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') {
+ ++c;
+
+ // NOTE: The original implementation is highly inaccurate here. The precision of a single
+ // IEEE 754 float is not high enough, everything behind the 6th digit tends to be more
+ // inaccurate than it would need to be. Casting to double seems to solve the problem.
+ // strtol_64 is used to prevent integer overflow.
+
+ // Another fix: this tends to become 0 for long numbers if we don't limit the maximum
+ // number of digits to be read. AI_FAST_ATOF_RELAVANT_DECIMALS can be a value between
+ // 1 and 15.
+ unsigned int diff = AI_FAST_ATOF_RELAVANT_DECIMALS;
+ double pl = static_cast<double>( strtoul10_64 ( c, &c, &diff ));
+
+ pl *= fast_atof_table[diff];
+ f += static_cast<Real>( pl );
+ }
+ // For backwards compatibility: eat trailing dots, but not trailing commas.
+ else if (*c == '.') {
+ ++c;
+ }
+
+ // A major 'E' must be allowed. Necessary for proper reading of some DXF files.
+ // Thanks to Zhao Lei to point out that this if() must be outside the if (*c == '.' ..)
+ if (*c == 'e' || *c == 'E') {
+ ++c;
+ const bool einv = (*c=='-');
+ if (einv || *c=='+') {
+ ++c;
+ }
+
+ // The reason float constants are used here is that we've seen cases where compilers
+ // would perform such casts on compile-time constants at runtime, which would be
+ // bad considering how frequently fast_atoreal_move<float> is called in Assimp.
+ Real exp = static_cast<Real>( strtoul10_64(c, &c) );
+ if (einv) {
+ exp = -exp;
+ }
+ f *= std::pow(static_cast<Real>(10.0), exp);
+ }
+
+ if (inv) {
+ f = -f;
+ }
+ out = f;
+ return c;
+}
+
+// ------------------------------------------------------------------------------------
+// The same but more human.
+inline
+ai_real fast_atof(const char* c) {
+ ai_real ret(0.0);
+ fast_atoreal_move<ai_real>(c, ret);
+
+ return ret;
+}
+
+inline
+ai_real fast_atof( const char* c, const char** cout) {
+ ai_real ret(0.0);
+ *cout = fast_atoreal_move<ai_real>(c, ret);
+
+ return ret;
+}
+
+inline
+ai_real fast_atof( const char** inout) {
+ ai_real ret(0.0);
+ *inout = fast_atoreal_move<ai_real>(*inout, ret);
+
+ return ret;
+}
+
+} //! namespace Assimp
+
+#endif // FAST_A_TO_F_H_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/importerdesc.h b/thirdparty/assimp/include/assimp/importerdesc.h
new file mode 100644
index 0000000000..36e387f011
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/importerdesc.h
@@ -0,0 +1,146 @@
+/*
+---------------------------------------------------------------------------
+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 importerdesc.h
+ * @brief #aiImporterFlags, aiImporterDesc implementation.
+ */
+#pragma once
+#ifndef AI_IMPORTER_DESC_H_INC
+#define AI_IMPORTER_DESC_H_INC
+
+
+/** Mixed set of flags for #aiImporterDesc, indicating some features
+ * common to many importers*/
+enum aiImporterFlags
+{
+ /** Indicates that there is a textual encoding of the
+ * file format; and that it is supported.*/
+ aiImporterFlags_SupportTextFlavour = 0x1,
+
+ /** Indicates that there is a binary encoding of the
+ * file format; and that it is supported.*/
+ aiImporterFlags_SupportBinaryFlavour = 0x2,
+
+ /** Indicates that there is a compressed encoding of the
+ * file format; and that it is supported.*/
+ aiImporterFlags_SupportCompressedFlavour = 0x4,
+
+ /** Indicates that the importer reads only a very particular
+ * subset of the file format. This happens commonly for
+ * declarative or procedural formats which cannot easily
+ * be mapped to #aiScene */
+ aiImporterFlags_LimitedSupport = 0x8,
+
+ /** Indicates that the importer is highly experimental and
+ * should be used with care. This only happens for trunk
+ * (i.e. SVN) versions, experimental code is not included
+ * in releases. */
+ aiImporterFlags_Experimental = 0x10
+};
+
+
+/** Meta information about a particular importer. Importers need to fill
+ * this structure, but they can freely decide how talkative they are.
+ * A common use case for loader meta info is a user interface
+ * in which the user can choose between various import/export file
+ * formats. Building such an UI by hand means a lot of maintenance
+ * as importers/exporters are added to Assimp, so it might be useful
+ * to have a common mechanism to query some rough importer
+ * characteristics. */
+struct aiImporterDesc
+{
+ /** Full name of the importer (i.e. Blender3D importer)*/
+ const char* mName;
+
+ /** Original author (left blank if unknown or whole assimp team) */
+ const char* mAuthor;
+
+ /** Current maintainer, left blank if the author maintains */
+ const char* mMaintainer;
+
+ /** Implementation comments, i.e. unimplemented features*/
+ const char* mComments;
+
+ /** These flags indicate some characteristics common to many
+ importers. */
+ unsigned int mFlags;
+
+ /** Minimum format version that can be loaded im major.minor format,
+ both are set to 0 if there is either no version scheme
+ or if the loader doesn't care. */
+ unsigned int mMinMajor;
+ unsigned int mMinMinor;
+
+ /** Maximum format version that can be loaded im major.minor format,
+ both are set to 0 if there is either no version scheme
+ or if the loader doesn't care. Loaders that expect to be
+ forward-compatible to potential future format versions should
+ indicate zero, otherwise they should specify the current
+ maximum version.*/
+ unsigned int mMaxMajor;
+ unsigned int mMaxMinor;
+
+ /** List of file extensions this importer can handle.
+ List entries are separated by space characters.
+ All entries are lower case without a leading dot (i.e.
+ "xml dae" would be a valid value. Note that multiple
+ importers may respond to the same file extension -
+ assimp calls all importers in the order in which they
+ are registered and each importer gets the opportunity
+ to load the file until one importer "claims" the file. Apart
+ from file extension checks, importers typically use
+ other methods to quickly reject files (i.e. magic
+ words) so this does not mean that common or generic
+ file extensions such as XML would be tediously slow. */
+ const char* mFileExtensions;
+};
+
+/** \brief Returns the Importer description for a given extension.
+
+Will return a NULL-pointer if no assigned importer desc. was found for the given extension
+ \param extension [in] The extension to look for
+ \return A pointer showing to the ImporterDesc, \see aiImporterDesc.
+*/
+ASSIMP_API const C_STRUCT aiImporterDesc* aiGetImporterDesc( const char *extension );
+
+#endif // AI_IMPORTER_DESC_H_INC
diff --git a/thirdparty/assimp/include/assimp/irrXMLWrapper.h b/thirdparty/assimp/include/assimp/irrXMLWrapper.h
new file mode 100644
index 0000000000..ec8ee7c76e
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/irrXMLWrapper.h
@@ -0,0 +1,144 @@
+/*
+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 INCLUDED_AI_IRRXML_WRAPPER
+#define INCLUDED_AI_IRRXML_WRAPPER
+
+// some long includes ....
+#include <irrXML.h>
+#include "IOStream.hpp"
+#include "BaseImporter.h"
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------------
+/** @brief Utility class to make IrrXML work together with our custom IO system
+ * See the IrrXML docs for more details.
+ *
+ * Construct IrrXML-Reader in BaseImporter::InternReadFile():
+ * @code
+ * // open the file
+ * std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
+ * if( file.get() == NULL) {
+ * throw DeadlyImportError( "Failed to open file " + pFile + ".");
+ * }
+ *
+ * // generate a XML reader for it
+ * std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
+ * mReader = irr::io::createIrrXMLReader( mIOWrapper.get());
+ * if( !mReader) {
+ * ThrowException( "xxxx: Unable to open file.");
+ * }
+ * @endcode
+ **/
+class CIrrXML_IOStreamReader : public irr::io::IFileReadCallBack {
+public:
+
+ // ----------------------------------------------------------------------------------
+ //! Construction from an existing IOStream
+ explicit CIrrXML_IOStreamReader(IOStream* _stream)
+ : stream (_stream)
+ , t (0)
+ {
+
+ // Map the buffer into memory and convert it to UTF8. IrrXML provides its
+ // own conversion, which is merely a cast from uintNN_t to uint8_t. Thus,
+ // it is not suitable for our purposes and we have to do it BEFORE IrrXML
+ // gets the buffer. Sadly, this forces us to map the whole file into
+ // memory.
+
+ data.resize(stream->FileSize());
+ stream->Read(&data[0],data.size(),1);
+
+ // Remove null characters from the input sequence otherwise the parsing will utterly fail
+ unsigned int size = 0;
+ unsigned int size_max = static_cast<unsigned int>(data.size());
+ for(unsigned int i = 0; i < size_max; i++) {
+ if(data[i] != '\0') {
+ data[size++] = data[i];
+ }
+ }
+ data.resize(size);
+
+ BaseImporter::ConvertToUTF8(data);
+ }
+
+ // ----------------------------------------------------------------------------------
+ //! Virtual destructor
+ virtual ~CIrrXML_IOStreamReader() {}
+
+ // ----------------------------------------------------------------------------------
+ //! Reads an amount of bytes from the file.
+ /** @param buffer: Pointer to output buffer.
+ * @param sizeToRead: Amount of bytes to read
+ * @return Returns how much bytes were read. */
+ virtual int read(void* buffer, int sizeToRead) {
+ if(sizeToRead<0) {
+ return 0;
+ }
+ if(t+sizeToRead>data.size()) {
+ sizeToRead = static_cast<int>(data.size()-t);
+ }
+
+ memcpy(buffer,&data.front()+t,sizeToRead);
+
+ t += sizeToRead;
+ return sizeToRead;
+ }
+
+ // ----------------------------------------------------------------------------------
+ //! Returns size of file in bytes
+ virtual int getSize() {
+ return (int)data.size();
+ }
+
+private:
+ IOStream* stream;
+ std::vector<char> data;
+ size_t t;
+
+}; // ! class CIrrXML_IOStreamReader
+
+} // ! Assimp
+
+#endif // !! INCLUDED_AI_IRRXML_WRAPPER
diff --git a/thirdparty/assimp/include/assimp/light.h b/thirdparty/assimp/include/assimp/light.h
new file mode 100644
index 0000000000..1667cfb8c1
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/light.h
@@ -0,0 +1,259 @@
+/*
+---------------------------------------------------------------------------
+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 light.h
+ * @brief Defines the aiLight data structure
+ */
+
+#pragma once
+#ifndef AI_LIGHT_H_INC
+#define AI_LIGHT_H_INC
+
+#include "types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** Enumerates all supported types of light sources.
+ */
+enum aiLightSourceType
+{
+ aiLightSource_UNDEFINED = 0x0,
+
+ //! A directional light source has a well-defined direction
+ //! but is infinitely far away. That's quite a good
+ //! approximation for sun light.
+ aiLightSource_DIRECTIONAL = 0x1,
+
+ //! A point light source has a well-defined position
+ //! in space but no direction - it emits light in all
+ //! directions. A normal bulb is a point light.
+ aiLightSource_POINT = 0x2,
+
+ //! A spot light source emits light in a specific
+ //! angle. It has a position and a direction it is pointing to.
+ //! A good example for a spot light is a light spot in
+ //! sport arenas.
+ aiLightSource_SPOT = 0x3,
+
+ //! The generic light level of the world, including the bounces
+ //! of all other light sources.
+ //! Typically, there's at most one ambient light in a scene.
+ //! This light type doesn't have a valid position, direction, or
+ //! other properties, just a color.
+ aiLightSource_AMBIENT = 0x4,
+
+ //! An area light is a rectangle with predefined size that uniformly
+ //! emits light from one of its sides. The position is center of the
+ //! rectangle and direction is its normal vector.
+ aiLightSource_AREA = 0x5,
+
+ /** This value is not used. It is just there to force the
+ * compiler to map this enum to a 32 Bit integer.
+ */
+#ifndef SWIG
+ _aiLightSource_Force32Bit = INT_MAX
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to describe a light source.
+ *
+ * Assimp supports multiple sorts of light sources, including
+ * directional, point and spot lights. All of them are defined with just
+ * a single structure and distinguished by their parameters.
+ * Note - some file formats (such as 3DS, ASE) export a "target point" -
+ * the point a spot light is looking at (it can even be animated). Assimp
+ * writes the target point as a subnode of a spotlights's main node,
+ * called "<spotName>.Target". However, this is just additional information
+ * then, the transformation tracks of the main node make the
+ * spot light already point in the right direction.
+*/
+struct aiLight
+{
+ /** The name of the light source.
+ *
+ * There must be a node in the scenegraph with the same name.
+ * This node specifies the position of the light in the scene
+ * hierarchy and can be animated.
+ */
+ C_STRUCT aiString mName;
+
+ /** The type of the light source.
+ *
+ * aiLightSource_UNDEFINED is not a valid value for this member.
+ */
+ C_ENUM aiLightSourceType mType;
+
+ /** Position of the light source in space. Relative to the
+ * transformation of the node corresponding to the light.
+ *
+ * The position is undefined for directional lights.
+ */
+ C_STRUCT aiVector3D mPosition;
+
+ /** Direction of the light source in space. Relative to the
+ * transformation of the node corresponding to the light.
+ *
+ * The direction is undefined for point lights. The vector
+ * may be normalized, but it needn't.
+ */
+ C_STRUCT aiVector3D mDirection;
+
+ /** Up direction of the light source in space. Relative to the
+ * transformation of the node corresponding to the light.
+ *
+ * The direction is undefined for point lights. The vector
+ * may be normalized, but it needn't.
+ */
+ C_STRUCT aiVector3D mUp;
+
+ /** Constant light attenuation factor.
+ *
+ * The intensity of the light source at a given distance 'd' from
+ * the light's position is
+ * @code
+ * Atten = 1/( att0 + att1 * d + att2 * d*d)
+ * @endcode
+ * This member corresponds to the att0 variable in the equation.
+ * Naturally undefined for directional lights.
+ */
+ float mAttenuationConstant;
+
+ /** Linear light attenuation factor.
+ *
+ * The intensity of the light source at a given distance 'd' from
+ * the light's position is
+ * @code
+ * Atten = 1/( att0 + att1 * d + att2 * d*d)
+ * @endcode
+ * This member corresponds to the att1 variable in the equation.
+ * Naturally undefined for directional lights.
+ */
+ float mAttenuationLinear;
+
+ /** Quadratic light attenuation factor.
+ *
+ * The intensity of the light source at a given distance 'd' from
+ * the light's position is
+ * @code
+ * Atten = 1/( att0 + att1 * d + att2 * d*d)
+ * @endcode
+ * This member corresponds to the att2 variable in the equation.
+ * Naturally undefined for directional lights.
+ */
+ float mAttenuationQuadratic;
+
+ /** Diffuse color of the light source
+ *
+ * The diffuse light color is multiplied with the diffuse
+ * material color to obtain the final color that contributes
+ * to the diffuse shading term.
+ */
+ C_STRUCT aiColor3D mColorDiffuse;
+
+ /** Specular color of the light source
+ *
+ * The specular light color is multiplied with the specular
+ * material color to obtain the final color that contributes
+ * to the specular shading term.
+ */
+ C_STRUCT aiColor3D mColorSpecular;
+
+ /** Ambient color of the light source
+ *
+ * The ambient light color is multiplied with the ambient
+ * material color to obtain the final color that contributes
+ * to the ambient shading term. Most renderers will ignore
+ * this value it, is just a remaining of the fixed-function pipeline
+ * that is still supported by quite many file formats.
+ */
+ C_STRUCT aiColor3D mColorAmbient;
+
+ /** Inner angle of a spot light's light cone.
+ *
+ * The spot light has maximum influence on objects inside this
+ * angle. The angle is given in radians. It is 2PI for point
+ * lights and undefined for directional lights.
+ */
+ float mAngleInnerCone;
+
+ /** Outer angle of a spot light's light cone.
+ *
+ * The spot light does not affect objects outside this angle.
+ * The angle is given in radians. It is 2PI for point lights and
+ * undefined for directional lights. The outer angle must be
+ * greater than or equal to the inner angle.
+ * It is assumed that the application uses a smooth
+ * interpolation between the inner and the outer cone of the
+ * spot light.
+ */
+ float mAngleOuterCone;
+
+ /** Size of area light source. */
+ C_STRUCT aiVector2D mSize;
+
+#ifdef __cplusplus
+
+ aiLight() AI_NO_EXCEPT
+ : mType (aiLightSource_UNDEFINED)
+ , mAttenuationConstant (0.f)
+ , mAttenuationLinear (1.f)
+ , mAttenuationQuadratic (0.f)
+ , mAngleInnerCone ((float)AI_MATH_TWO_PI)
+ , mAngleOuterCone ((float)AI_MATH_TWO_PI)
+ , mSize (0.f, 0.f)
+ {
+ }
+
+#endif
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // !! AI_LIGHT_H_INC
diff --git a/thirdparty/assimp/include/assimp/material.h b/thirdparty/assimp/include/assimp/material.h
new file mode 100644
index 0000000000..4b5a1293dd
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/material.h
@@ -0,0 +1,1580 @@
+/*
+---------------------------------------------------------------------------
+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 material.h
+ * @brief Defines the material system of the library
+ */
+#pragma once
+#ifndef AI_MATERIAL_H_INC
+#define AI_MATERIAL_H_INC
+
+#include "types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Name for default materials (2nd is used if meshes have UV coords)
+#define AI_DEFAULT_MATERIAL_NAME "DefaultMaterial"
+
+// ---------------------------------------------------------------------------
+/** @brief Defines how the Nth texture of a specific type is combined with
+ * the result of all previous layers.
+ *
+ * Example (left: key, right: value): <br>
+ * @code
+ * DiffColor0 - gray
+ * DiffTextureOp0 - aiTextureOpMultiply
+ * DiffTexture0 - tex1.png
+ * DiffTextureOp0 - aiTextureOpAdd
+ * DiffTexture1 - tex2.png
+ * @endcode
+ * Written as equation, the final diffuse term for a specific pixel would be:
+ * @code
+ * diffFinal = DiffColor0 * sampleTex(DiffTexture0,UV0) +
+ * sampleTex(DiffTexture1,UV0) * diffContrib;
+ * @endcode
+ * where 'diffContrib' is the intensity of the incoming light for that pixel.
+ */
+enum aiTextureOp
+{
+ /** T = T1 * T2 */
+ aiTextureOp_Multiply = 0x0,
+
+ /** T = T1 + T2 */
+ aiTextureOp_Add = 0x1,
+
+ /** T = T1 - T2 */
+ aiTextureOp_Subtract = 0x2,
+
+ /** T = T1 / T2 */
+ aiTextureOp_Divide = 0x3,
+
+ /** T = (T1 + T2) - (T1 * T2) */
+ aiTextureOp_SmoothAdd = 0x4,
+
+ /** T = T1 + (T2-0.5) */
+ aiTextureOp_SignedAdd = 0x5,
+
+
+#ifndef SWIG
+ _aiTextureOp_Force32Bit = INT_MAX
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Defines how UV coordinates outside the [0...1] range are handled.
+ *
+ * Commonly referred to as 'wrapping mode'.
+ */
+enum aiTextureMapMode
+{
+ /** A texture coordinate u|v is translated to u%1|v%1
+ */
+ aiTextureMapMode_Wrap = 0x0,
+
+ /** Texture coordinates outside [0...1]
+ * are clamped to the nearest valid value.
+ */
+ aiTextureMapMode_Clamp = 0x1,
+
+ /** If the texture coordinates for a pixel are outside [0...1]
+ * the texture is not applied to that pixel
+ */
+ aiTextureMapMode_Decal = 0x3,
+
+ /** A texture coordinate u|v becomes u%1|v%1 if (u-(u%1))%2 is zero and
+ * 1-(u%1)|1-(v%1) otherwise
+ */
+ aiTextureMapMode_Mirror = 0x2,
+
+#ifndef SWIG
+ _aiTextureMapMode_Force32Bit = INT_MAX
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Defines how the mapping coords for a texture are generated.
+ *
+ * Real-time applications typically require full UV coordinates, so the use of
+ * the aiProcess_GenUVCoords step is highly recommended. It generates proper
+ * UV channels for non-UV mapped objects, as long as an accurate description
+ * how the mapping should look like (e.g spherical) is given.
+ * See the #AI_MATKEY_MAPPING property for more details.
+ */
+enum aiTextureMapping
+{
+ /** The mapping coordinates are taken from an UV channel.
+ *
+ * The #AI_MATKEY_UVWSRC key specifies from which UV channel
+ * the texture coordinates are to be taken from (remember,
+ * meshes can have more than one UV channel).
+ */
+ aiTextureMapping_UV = 0x0,
+
+ /** Spherical mapping */
+ aiTextureMapping_SPHERE = 0x1,
+
+ /** Cylindrical mapping */
+ aiTextureMapping_CYLINDER = 0x2,
+
+ /** Cubic mapping */
+ aiTextureMapping_BOX = 0x3,
+
+ /** Planar mapping */
+ aiTextureMapping_PLANE = 0x4,
+
+ /** Undefined mapping. Have fun. */
+ aiTextureMapping_OTHER = 0x5,
+
+
+#ifndef SWIG
+ _aiTextureMapping_Force32Bit = INT_MAX
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Defines the purpose of a texture
+ *
+ * This is a very difficult topic. Different 3D packages support different
+ * kinds of textures. For very common texture types, such as bumpmaps, the
+ * rendering results depend on implementation details in the rendering
+ * pipelines of these applications. Assimp loads all texture references from
+ * the model file and tries to determine which of the predefined texture
+ * types below is the best choice to match the original use of the texture
+ * as closely as possible.<br>
+ *
+ * In content pipelines you'll usually define how textures have to be handled,
+ * and the artists working on models have to conform to this specification,
+ * regardless which 3D tool they're using.
+ */
+enum aiTextureType
+{
+ /** Dummy value.
+ *
+ * No texture, but the value to be used as 'texture semantic'
+ * (#aiMaterialProperty::mSemantic) for all material properties
+ * *not* related to textures.
+ */
+ aiTextureType_NONE = 0x0,
+
+ /** The texture is combined with the result of the diffuse
+ * lighting equation.
+ */
+ aiTextureType_DIFFUSE = 0x1,
+
+ /** The texture is combined with the result of the specular
+ * lighting equation.
+ */
+ aiTextureType_SPECULAR = 0x2,
+
+ /** The texture is combined with the result of the ambient
+ * lighting equation.
+ */
+ aiTextureType_AMBIENT = 0x3,
+
+ /** The texture is added to the result of the lighting
+ * calculation. It isn't influenced by incoming light.
+ */
+ aiTextureType_EMISSIVE = 0x4,
+
+ /** The texture is a height map.
+ *
+ * By convention, higher gray-scale values stand for
+ * higher elevations from the base height.
+ */
+ aiTextureType_HEIGHT = 0x5,
+
+ /** The texture is a (tangent space) normal-map.
+ *
+ * Again, there are several conventions for tangent-space
+ * normal maps. Assimp does (intentionally) not
+ * distinguish here.
+ */
+ aiTextureType_NORMALS = 0x6,
+
+ /** The texture defines the glossiness of the material.
+ *
+ * The glossiness is in fact the exponent of the specular
+ * (phong) lighting equation. Usually there is a conversion
+ * function defined to map the linear color values in the
+ * texture to a suitable exponent. Have fun.
+ */
+ aiTextureType_SHININESS = 0x7,
+
+ /** The texture defines per-pixel opacity.
+ *
+ * Usually 'white' means opaque and 'black' means
+ * 'transparency'. Or quite the opposite. Have fun.
+ */
+ aiTextureType_OPACITY = 0x8,
+
+ /** Displacement texture
+ *
+ * The exact purpose and format is application-dependent.
+ * Higher color values stand for higher vertex displacements.
+ */
+ aiTextureType_DISPLACEMENT = 0x9,
+
+ /** Lightmap texture (aka Ambient Occlusion)
+ *
+ * Both 'Lightmaps' and dedicated 'ambient occlusion maps' are
+ * covered by this material property. The texture contains a
+ * scaling value for the final color value of a pixel. Its
+ * intensity is not affected by incoming light.
+ */
+ aiTextureType_LIGHTMAP = 0xA,
+
+ /** Reflection texture
+ *
+ * Contains the color of a perfect mirror reflection.
+ * Rarely used, almost never for real-time applications.
+ */
+ aiTextureType_REFLECTION = 0xB,
+
+ /** Unknown texture
+ *
+ * A texture reference that does not match any of the definitions
+ * above is considered to be 'unknown'. It is still imported,
+ * but is excluded from any further post-processing.
+ */
+ aiTextureType_UNKNOWN = 0xC,
+
+
+#ifndef SWIG
+ _aiTextureType_Force32Bit = INT_MAX
+#endif
+};
+
+#define AI_TEXTURE_TYPE_MAX aiTextureType_UNKNOWN
+
+// ---------------------------------------------------------------------------
+/** @brief Defines all shading models supported by the library
+ *
+ * The list of shading modes has been taken from Blender.
+ * See Blender documentation for more information. The API does
+ * not distinguish between "specular" and "diffuse" shaders (thus the
+ * specular term for diffuse shading models like Oren-Nayar remains
+ * undefined). <br>
+ * Again, this value is just a hint. Assimp tries to select the shader whose
+ * most common implementation matches the original rendering results of the
+ * 3D modeller which wrote a particular model as closely as possible.
+ */
+enum aiShadingMode
+{
+ /** Flat shading. Shading is done on per-face base,
+ * diffuse only. Also known as 'faceted shading'.
+ */
+ aiShadingMode_Flat = 0x1,
+
+ /** Simple Gouraud shading.
+ */
+ aiShadingMode_Gouraud = 0x2,
+
+ /** Phong-Shading -
+ */
+ aiShadingMode_Phong = 0x3,
+
+ /** Phong-Blinn-Shading
+ */
+ aiShadingMode_Blinn = 0x4,
+
+ /** Toon-Shading per pixel
+ *
+ * Also known as 'comic' shader.
+ */
+ aiShadingMode_Toon = 0x5,
+
+ /** OrenNayar-Shading per pixel
+ *
+ * Extension to standard Lambertian shading, taking the
+ * roughness of the material into account
+ */
+ aiShadingMode_OrenNayar = 0x6,
+
+ /** Minnaert-Shading per pixel
+ *
+ * Extension to standard Lambertian shading, taking the
+ * "darkness" of the material into account
+ */
+ aiShadingMode_Minnaert = 0x7,
+
+ /** CookTorrance-Shading per pixel
+ *
+ * Special shader for metallic surfaces.
+ */
+ aiShadingMode_CookTorrance = 0x8,
+
+ /** No shading at all. Constant light influence of 1.0.
+ */
+ aiShadingMode_NoShading = 0x9,
+
+ /** Fresnel shading
+ */
+ aiShadingMode_Fresnel = 0xa,
+
+
+#ifndef SWIG
+ _aiShadingMode_Force32Bit = INT_MAX
+#endif
+};
+
+
+// ---------------------------------------------------------------------------
+/** @brief Defines some mixed flags for a particular texture.
+ *
+ * Usually you'll instruct your cg artists how textures have to look like ...
+ * and how they will be processed in your application. However, if you use
+ * Assimp for completely generic loading purposes you might also need to
+ * process these flags in order to display as many 'unknown' 3D models as
+ * possible correctly.
+ *
+ * This corresponds to the #AI_MATKEY_TEXFLAGS property.
+*/
+enum aiTextureFlags
+{
+ /** The texture's color values have to be inverted (component-wise 1-n)
+ */
+ aiTextureFlags_Invert = 0x1,
+
+ /** Explicit request to the application to process the alpha channel
+ * of the texture.
+ *
+ * Mutually exclusive with #aiTextureFlags_IgnoreAlpha. These
+ * flags are set if the library can say for sure that the alpha
+ * channel is used/is not used. If the model format does not
+ * define this, it is left to the application to decide whether
+ * the texture alpha channel - if any - is evaluated or not.
+ */
+ aiTextureFlags_UseAlpha = 0x2,
+
+ /** Explicit request to the application to ignore the alpha channel
+ * of the texture.
+ *
+ * Mutually exclusive with #aiTextureFlags_UseAlpha.
+ */
+ aiTextureFlags_IgnoreAlpha = 0x4,
+
+#ifndef SWIG
+ _aiTextureFlags_Force32Bit = INT_MAX
+#endif
+};
+
+
+// ---------------------------------------------------------------------------
+/** @brief Defines alpha-blend flags.
+ *
+ * If you're familiar with OpenGL or D3D, these flags aren't new to you.
+ * They define *how* the final color value of a pixel is computed, basing
+ * on the previous color at that pixel and the new color value from the
+ * material.
+ * The blend formula is:
+ * @code
+ * SourceColor * SourceBlend + DestColor * DestBlend
+ * @endcode
+ * where DestColor is the previous color in the framebuffer at this
+ * position and SourceColor is the material color before the transparency
+ * calculation.<br>
+ * This corresponds to the #AI_MATKEY_BLEND_FUNC property.
+*/
+enum aiBlendMode
+{
+ /**
+ * Formula:
+ * @code
+ * SourceColor*SourceAlpha + DestColor*(1-SourceAlpha)
+ * @endcode
+ */
+ aiBlendMode_Default = 0x0,
+
+ /** Additive blending
+ *
+ * Formula:
+ * @code
+ * SourceColor*1 + DestColor*1
+ * @endcode
+ */
+ aiBlendMode_Additive = 0x1,
+
+ // we don't need more for the moment, but we might need them
+ // in future versions ...
+
+#ifndef SWIG
+ _aiBlendMode_Force32Bit = INT_MAX
+#endif
+};
+
+
+#include "./Compiler/pushpack1.h"
+
+// ---------------------------------------------------------------------------
+/** @brief Defines how an UV channel is transformed.
+ *
+ * This is just a helper structure for the #AI_MATKEY_UVTRANSFORM key.
+ * See its documentation for more details.
+ *
+ * Typically you'll want to build a matrix of this information. However,
+ * we keep separate scaling/translation/rotation values to make it
+ * easier to process and optimize UV transformations internally.
+ */
+struct aiUVTransform
+{
+ /** Translation on the u and v axes.
+ *
+ * The default value is (0|0).
+ */
+ C_STRUCT aiVector2D mTranslation;
+
+ /** Scaling on the u and v axes.
+ *
+ * The default value is (1|1).
+ */
+ C_STRUCT aiVector2D mScaling;
+
+ /** Rotation - in counter-clockwise direction.
+ *
+ * The rotation angle is specified in radians. The
+ * rotation center is 0.5f|0.5f. The default value
+ * 0.f.
+ */
+ ai_real mRotation;
+
+
+#ifdef __cplusplus
+ aiUVTransform() AI_NO_EXCEPT
+ : mTranslation (0.0,0.0)
+ , mScaling (1.0,1.0)
+ , mRotation (0.0)
+ {
+ // nothing to be done here ...
+ }
+#endif
+
+};
+
+#include "./Compiler/poppack1.h"
+
+//! @cond AI_DOX_INCLUDE_INTERNAL
+// ---------------------------------------------------------------------------
+/** @brief A very primitive RTTI system for the contents of material
+ * properties.
+ */
+enum aiPropertyTypeInfo
+{
+ /** Array of single-precision (32 Bit) floats
+ *
+ * It is possible to use aiGetMaterialInteger[Array]() (or the C++-API
+ * aiMaterial::Get()) to query properties stored in floating-point format.
+ * The material system performs the type conversion automatically.
+ */
+ aiPTI_Float = 0x1,
+
+ /** Array of double-precision (64 Bit) floats
+ *
+ * It is possible to use aiGetMaterialInteger[Array]() (or the C++-API
+ * aiMaterial::Get()) to query properties stored in floating-point format.
+ * The material system performs the type conversion automatically.
+ */
+ aiPTI_Double = 0x2,
+
+ /** The material property is an aiString.
+ *
+ * Arrays of strings aren't possible, aiGetMaterialString() (or the
+ * C++-API aiMaterial::Get()) *must* be used to query a string property.
+ */
+ aiPTI_String = 0x3,
+
+ /** Array of (32 Bit) integers
+ *
+ * It is possible to use aiGetMaterialFloat[Array]() (or the C++-API
+ * aiMaterial::Get()) to query properties stored in integer format.
+ * The material system performs the type conversion automatically.
+ */
+ aiPTI_Integer = 0x4,
+
+
+ /** Simple binary buffer, content undefined. Not convertible to anything.
+ */
+ aiPTI_Buffer = 0x5,
+
+
+ /** This value is not used. It is just there to force the
+ * compiler to map this enum to a 32 Bit integer.
+ */
+#ifndef SWIG
+ _aiPTI_Force32Bit = INT_MAX
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Data structure for a single material property
+ *
+ * As an user, you'll probably never need to deal with this data structure.
+ * Just use the provided aiGetMaterialXXX() or aiMaterial::Get() family
+ * of functions to query material properties easily. Processing them
+ * manually is faster, but it is not the recommended way. It isn't worth
+ * the effort. <br>
+ * Material property names follow a simple scheme:
+ * @code
+ * $<name>
+ * ?<name>
+ * A public property, there must be corresponding AI_MATKEY_XXX define
+ * 2nd: Public, but ignored by the #aiProcess_RemoveRedundantMaterials
+ * post-processing step.
+ * ~<name>
+ * A temporary property for internal use.
+ * @endcode
+ * @see aiMaterial
+ */
+struct aiMaterialProperty
+{
+ /** Specifies the name of the property (key)
+ * Keys are generally case insensitive.
+ */
+ C_STRUCT aiString mKey;
+
+ /** Textures: Specifies their exact usage semantic.
+ * For non-texture properties, this member is always 0
+ * (or, better-said, #aiTextureType_NONE).
+ */
+ unsigned int mSemantic;
+
+ /** Textures: Specifies the index of the texture.
+ * For non-texture properties, this member is always 0.
+ */
+ unsigned int mIndex;
+
+ /** Size of the buffer mData is pointing to, in bytes.
+ * This value may not be 0.
+ */
+ unsigned int mDataLength;
+
+ /** Type information for the property.
+ *
+ * Defines the data layout inside the data buffer. This is used
+ * by the library internally to perform debug checks and to
+ * utilize proper type conversions.
+ * (It's probably a hacky solution, but it works.)
+ */
+ C_ENUM aiPropertyTypeInfo mType;
+
+ /** Binary buffer to hold the property's value.
+ * The size of the buffer is always mDataLength.
+ */
+ char* mData;
+
+#ifdef __cplusplus
+
+ aiMaterialProperty() AI_NO_EXCEPT
+ : mSemantic( 0 )
+ , mIndex( 0 )
+ , mDataLength( 0 )
+ , mType( aiPTI_Float )
+ , mData(nullptr) {
+ // empty
+ }
+
+ ~aiMaterialProperty() {
+ delete[] mData;
+ mData = nullptr;
+ }
+
+#endif
+};
+//! @endcond
+
+#ifdef __cplusplus
+} // We need to leave the "C" block here to allow template member functions
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Data structure for a material
+*
+* Material data is stored using a key-value structure. A single key-value
+* pair is called a 'material property'. C++ users should use the provided
+* member functions of aiMaterial to process material properties, C users
+* have to stick with the aiMaterialGetXXX family of unbound functions.
+* The library defines a set of standard keys (AI_MATKEY_XXX).
+*/
+#ifdef __cplusplus
+struct ASSIMP_API aiMaterial
+#else
+struct aiMaterial
+#endif
+{
+
+#ifdef __cplusplus
+
+public:
+
+ aiMaterial();
+ ~aiMaterial();
+
+ // -------------------------------------------------------------------
+ /**
+ * @brief Returns the name of the material.
+ * @return The name of the material.
+ */
+ // -------------------------------------------------------------------
+ aiString GetName();
+
+ // -------------------------------------------------------------------
+ /** @brief Retrieve an array of Type values with a specific key
+ * from the material
+ *
+ * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+ * @param type .. set by AI_MATKEY_XXX
+ * @param idx .. set by AI_MATKEY_XXX
+ * @param pOut Pointer to a buffer to receive the result.
+ * @param pMax Specifies the size of the given buffer, in Type's.
+ * Receives the number of values (not bytes!) read.
+ * NULL is a valid value for this parameter.
+ */
+ template <typename Type>
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, Type* pOut, unsigned int* pMax) const;
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, int* pOut, unsigned int* pMax) const;
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, ai_real* pOut, unsigned int* pMax) const;
+
+ // -------------------------------------------------------------------
+ /** @brief Retrieve a Type value with a specific key
+ * from the material
+ *
+ * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+ * @param type Specifies the type of the texture to be retrieved (
+ * e.g. diffuse, specular, height map ...)
+ * @param idx Index of the texture to be retrieved.
+ * @param pOut Reference to receive the output value
+ */
+ template <typename Type>
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx,Type& pOut) const;
+
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, int& pOut) const;
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, ai_real& pOut) const;
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, aiString& pOut) const;
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, aiColor3D& pOut) const;
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, aiColor4D& pOut) const;
+
+ aiReturn Get(const char* pKey,unsigned int type,
+ unsigned int idx, aiUVTransform& pOut) const;
+
+ // -------------------------------------------------------------------
+ /** Get the number of textures for a particular texture type.
+ * @param type Texture type to check for
+ * @return Number of textures for this type.
+ * @note A texture can be easily queried using #GetTexture() */
+ unsigned int GetTextureCount(aiTextureType type) const;
+
+ // -------------------------------------------------------------------
+ /** Helper function to get all parameters pertaining to a
+ * particular texture slot from a material.
+ *
+ * This function is provided just for convenience, you could also
+ * read the single material properties manually.
+ * @param type Specifies the type of the texture to be retrieved (
+ * e.g. diffuse, specular, height map ...)
+ * @param index Index of the texture to be retrieved. The function fails
+ * if there is no texture of that type with this index.
+ * #GetTextureCount() can be used to determine the number of textures
+ * per texture type.
+ * @param path Receives the path to the texture.
+ * If the texture is embedded, receives a '*' followed by the id of
+ * the texture (for the textures stored in the corresponding scene) which
+ * can be converted to an int using a function like atoi.
+ * NULL is a valid value.
+ * @param mapping The texture mapping.
+ * NULL is allowed as value.
+ * @param uvindex Receives the UV index of the texture.
+ * NULL is a valid value.
+ * @param blend Receives the blend factor for the texture
+ * NULL is a valid value.
+ * @param op Receives the texture operation to be performed between
+ * this texture and the previous texture. NULL is allowed as value.
+ * @param mapmode Receives the mapping modes to be used for the texture.
+ * The parameter may be NULL but if it is a valid pointer it MUST
+ * point to an array of 3 aiTextureMapMode's (one for each
+ * axis: UVW order (=XYZ)).
+ */
+ // -------------------------------------------------------------------
+ aiReturn GetTexture(aiTextureType type,
+ unsigned int index,
+ C_STRUCT aiString* path,
+ aiTextureMapping* mapping = NULL,
+ unsigned int* uvindex = NULL,
+ ai_real* blend = NULL,
+ aiTextureOp* op = NULL,
+ aiTextureMapMode* mapmode = NULL) const;
+
+
+ // Setters
+
+
+ // ------------------------------------------------------------------------------
+ /** @brief Add a property with a given key and type info to the material
+ * structure
+ *
+ * @param pInput Pointer to input data
+ * @param pSizeInBytes Size of input data
+ * @param pKey Key/Usage of the property (AI_MATKEY_XXX)
+ * @param type Set by the AI_MATKEY_XXX macro
+ * @param index Set by the AI_MATKEY_XXX macro
+ * @param pType Type information hint */
+ aiReturn AddBinaryProperty (const void* pInput,
+ unsigned int pSizeInBytes,
+ const char* pKey,
+ unsigned int type ,
+ unsigned int index ,
+ aiPropertyTypeInfo pType);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Add a string property with a given key and type info to the
+ * material structure
+ *
+ * @param pInput Input string
+ * @param pKey Key/Usage of the property (AI_MATKEY_XXX)
+ * @param type Set by the AI_MATKEY_XXX macro
+ * @param index Set by the AI_MATKEY_XXX macro */
+ aiReturn AddProperty (const aiString* pInput,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Add a property with a given key to the material structure
+ * @param pInput Pointer to the input data
+ * @param pNumValues Number of values in the array
+ * @param pKey Key/Usage of the property (AI_MATKEY_XXX)
+ * @param type Set by the AI_MATKEY_XXX macro
+ * @param index Set by the AI_MATKEY_XXX macro */
+ template<class TYPE>
+ aiReturn AddProperty (const TYPE* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ aiReturn AddProperty (const aiVector3D* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ aiReturn AddProperty (const aiColor3D* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ aiReturn AddProperty (const aiColor4D* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ aiReturn AddProperty (const int* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ aiReturn AddProperty (const float* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ aiReturn AddProperty (const double* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ aiReturn AddProperty (const aiUVTransform* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Remove a given key from the list.
+ *
+ * The function fails if the key isn't found
+ * @param pKey Key to be deleted
+ * @param type Set by the AI_MATKEY_XXX macro
+ * @param index Set by the AI_MATKEY_XXX macro */
+ aiReturn RemoveProperty (const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Removes all properties from the material.
+ *
+ * The data array remains allocated so adding new properties is quite fast. */
+ void Clear();
+
+ // ------------------------------------------------------------------------------
+ /** Copy the property list of a material
+ * @param pcDest Destination material
+ * @param pcSrc Source material
+ */
+ static void CopyPropertyList(aiMaterial* pcDest,
+ const aiMaterial* pcSrc);
+
+
+#endif
+
+ /** List of all material properties loaded. */
+ C_STRUCT aiMaterialProperty** mProperties;
+
+ /** Number of properties in the data base */
+ unsigned int mNumProperties;
+
+ /** Storage allocated */
+ unsigned int mNumAllocated;
+};
+
+// Go back to extern "C" again
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_NAME "?mat.name",0,0
+#define AI_MATKEY_TWOSIDED "$mat.twosided",0,0
+#define AI_MATKEY_SHADING_MODEL "$mat.shadingm",0,0
+#define AI_MATKEY_ENABLE_WIREFRAME "$mat.wireframe",0,0
+#define AI_MATKEY_BLEND_FUNC "$mat.blend",0,0
+#define AI_MATKEY_OPACITY "$mat.opacity",0,0
+#define AI_MATKEY_TRANSPARENCYFACTOR "$mat.transparencyfactor",0,0
+#define AI_MATKEY_BUMPSCALING "$mat.bumpscaling",0,0
+#define AI_MATKEY_SHININESS "$mat.shininess",0,0
+#define AI_MATKEY_REFLECTIVITY "$mat.reflectivity",0,0
+#define AI_MATKEY_SHININESS_STRENGTH "$mat.shinpercent",0,0
+#define AI_MATKEY_REFRACTI "$mat.refracti",0,0
+#define AI_MATKEY_COLOR_DIFFUSE "$clr.diffuse",0,0
+#define AI_MATKEY_COLOR_AMBIENT "$clr.ambient",0,0
+#define AI_MATKEY_COLOR_SPECULAR "$clr.specular",0,0
+#define AI_MATKEY_COLOR_EMISSIVE "$clr.emissive",0,0
+#define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0
+#define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",0,0
+#define AI_MATKEY_GLOBAL_BACKGROUND_IMAGE "?bg.global",0,0
+#define AI_MATKEY_GLOBAL_SHADERLANG "?sh.lang",0,0
+#define AI_MATKEY_SHADER_VERTEX "?sh.vs",0,0
+#define AI_MATKEY_SHADER_FRAGMENT "?sh.fs",0,0
+#define AI_MATKEY_SHADER_GEO "?sh.gs",0,0
+#define AI_MATKEY_SHADER_TESSELATION "?sh.ts",0,0
+#define AI_MATKEY_SHADER_PRIMITIVE "?sh.ps",0,0
+#define AI_MATKEY_SHADER_COMPUTE "?sh.cs",0,0
+
+// ---------------------------------------------------------------------------
+// Pure key names for all texture-related properties
+//! @cond MATS_DOC_FULL
+#define _AI_MATKEY_TEXTURE_BASE "$tex.file"
+#define _AI_MATKEY_UVWSRC_BASE "$tex.uvwsrc"
+#define _AI_MATKEY_TEXOP_BASE "$tex.op"
+#define _AI_MATKEY_MAPPING_BASE "$tex.mapping"
+#define _AI_MATKEY_TEXBLEND_BASE "$tex.blend"
+#define _AI_MATKEY_MAPPINGMODE_U_BASE "$tex.mapmodeu"
+#define _AI_MATKEY_MAPPINGMODE_V_BASE "$tex.mapmodev"
+#define _AI_MATKEY_TEXMAP_AXIS_BASE "$tex.mapaxis"
+#define _AI_MATKEY_UVTRANSFORM_BASE "$tex.uvtrafo"
+#define _AI_MATKEY_TEXFLAGS_BASE "$tex.flags"
+//! @endcond
+
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_TEXTURE(type, N) _AI_MATKEY_TEXTURE_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_TEXTURE_DIFFUSE(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_TEXTURE_SPECULAR(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_TEXTURE_AMBIENT(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_TEXTURE_EMISSIVE(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_TEXTURE_NORMALS(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_TEXTURE_HEIGHT(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_TEXTURE_SHININESS(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_TEXTURE_OPACITY(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_TEXTURE_DISPLACEMENT(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_TEXTURE_LIGHTMAP(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_TEXTURE_REFLECTION(N) \
+ AI_MATKEY_TEXTURE(aiTextureType_REFLECTION,N)
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_UVWSRC(type, N) _AI_MATKEY_UVWSRC_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_UVWSRC_DIFFUSE(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_UVWSRC_SPECULAR(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_UVWSRC_AMBIENT(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_UVWSRC_EMISSIVE(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_UVWSRC_NORMALS(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_UVWSRC_HEIGHT(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_UVWSRC_SHININESS(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_UVWSRC_OPACITY(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_UVWSRC_DISPLACEMENT(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_UVWSRC_LIGHTMAP(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_UVWSRC_REFLECTION(N) \
+ AI_MATKEY_UVWSRC(aiTextureType_REFLECTION,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_TEXOP(type, N) _AI_MATKEY_TEXOP_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_TEXOP_DIFFUSE(N) \
+ AI_MATKEY_TEXOP(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_TEXOP_SPECULAR(N) \
+ AI_MATKEY_TEXOP(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_TEXOP_AMBIENT(N) \
+ AI_MATKEY_TEXOP(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_TEXOP_EMISSIVE(N) \
+ AI_MATKEY_TEXOP(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_TEXOP_NORMALS(N) \
+ AI_MATKEY_TEXOP(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_TEXOP_HEIGHT(N) \
+ AI_MATKEY_TEXOP(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_TEXOP_SHININESS(N) \
+ AI_MATKEY_TEXOP(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_TEXOP_OPACITY(N) \
+ AI_MATKEY_TEXOP(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_TEXOP_DISPLACEMENT(N) \
+ AI_MATKEY_TEXOP(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_TEXOP_LIGHTMAP(N) \
+ AI_MATKEY_TEXOP(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_TEXOP_REFLECTION(N) \
+ AI_MATKEY_TEXOP(aiTextureType_REFLECTION,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_MAPPING(type, N) _AI_MATKEY_MAPPING_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_MAPPING_DIFFUSE(N) \
+ AI_MATKEY_MAPPING(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_MAPPING_SPECULAR(N) \
+ AI_MATKEY_MAPPING(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_MAPPING_AMBIENT(N) \
+ AI_MATKEY_MAPPING(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_MAPPING_EMISSIVE(N) \
+ AI_MATKEY_MAPPING(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_MAPPING_NORMALS(N) \
+ AI_MATKEY_MAPPING(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_MAPPING_HEIGHT(N) \
+ AI_MATKEY_MAPPING(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_MAPPING_SHININESS(N) \
+ AI_MATKEY_MAPPING(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_MAPPING_OPACITY(N) \
+ AI_MATKEY_MAPPING(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_MAPPING_DISPLACEMENT(N) \
+ AI_MATKEY_MAPPING(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_MAPPING_LIGHTMAP(N) \
+ AI_MATKEY_MAPPING(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_MAPPING_REFLECTION(N) \
+ AI_MATKEY_MAPPING(aiTextureType_REFLECTION,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_TEXBLEND(type, N) _AI_MATKEY_TEXBLEND_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_TEXBLEND_DIFFUSE(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_TEXBLEND_SPECULAR(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_TEXBLEND_AMBIENT(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_TEXBLEND_EMISSIVE(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_TEXBLEND_NORMALS(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_TEXBLEND_HEIGHT(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_TEXBLEND_SHININESS(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_TEXBLEND_OPACITY(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_TEXBLEND_DISPLACEMENT(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_TEXBLEND_LIGHTMAP(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_TEXBLEND_REFLECTION(N) \
+ AI_MATKEY_TEXBLEND(aiTextureType_REFLECTION,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_MAPPINGMODE_U(type, N) _AI_MATKEY_MAPPINGMODE_U_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_MAPPINGMODE_U_DIFFUSE(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_SPECULAR(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_AMBIENT(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_EMISSIVE(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_NORMALS(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_HEIGHT(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_SHININESS(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_OPACITY(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_DISPLACEMENT(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_MAPPINGMODE_U_REFLECTION(N) \
+ AI_MATKEY_MAPPINGMODE_U(aiTextureType_REFLECTION,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_MAPPINGMODE_V(type, N) _AI_MATKEY_MAPPINGMODE_V_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_MAPPINGMODE_V_DIFFUSE(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_SPECULAR(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_AMBIENT(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_EMISSIVE(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_NORMALS(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_HEIGHT(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_SHININESS(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_OPACITY(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_DISPLACEMENT(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_MAPPINGMODE_V_REFLECTION(N) \
+ AI_MATKEY_MAPPINGMODE_V(aiTextureType_REFLECTION,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_TEXMAP_AXIS(type, N) _AI_MATKEY_TEXMAP_AXIS_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_TEXMAP_AXIS_DIFFUSE(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_SPECULAR(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_AMBIENT(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_EMISSIVE(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_NORMALS(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_HEIGHT(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_SHININESS(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_OPACITY(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_DISPLACEMENT(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_LIGHTMAP(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_TEXMAP_AXIS_REFLECTION(N) \
+ AI_MATKEY_TEXMAP_AXIS(aiTextureType_REFLECTION,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_UVTRANSFORM(type, N) _AI_MATKEY_UVTRANSFORM_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_UVTRANSFORM_DIFFUSE(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_UVTRANSFORM_SPECULAR(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_UVTRANSFORM_AMBIENT(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_UVTRANSFORM_EMISSIVE(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_UVTRANSFORM_NORMALS(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_UVTRANSFORM_HEIGHT(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_UVTRANSFORM_SHININESS(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_UVTRANSFORM_OPACITY(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_UVTRANSFORM_DISPLACEMENT(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_UVTRANSFORM_LIGHTMAP(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_UVTRANSFORM_REFLECTION(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_REFLECTION,N)
+
+#define AI_MATKEY_UVTRANSFORM_UNKNOWN(N) \
+ AI_MATKEY_UVTRANSFORM(aiTextureType_UNKNOWN,N)
+
+//! @endcond
+// ---------------------------------------------------------------------------
+#define AI_MATKEY_TEXFLAGS(type, N) _AI_MATKEY_TEXFLAGS_BASE,type,N
+
+// For backward compatibility and simplicity
+//! @cond MATS_DOC_FULL
+#define AI_MATKEY_TEXFLAGS_DIFFUSE(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_DIFFUSE,N)
+
+#define AI_MATKEY_TEXFLAGS_SPECULAR(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_SPECULAR,N)
+
+#define AI_MATKEY_TEXFLAGS_AMBIENT(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_AMBIENT,N)
+
+#define AI_MATKEY_TEXFLAGS_EMISSIVE(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_EMISSIVE,N)
+
+#define AI_MATKEY_TEXFLAGS_NORMALS(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_NORMALS,N)
+
+#define AI_MATKEY_TEXFLAGS_HEIGHT(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_HEIGHT,N)
+
+#define AI_MATKEY_TEXFLAGS_SHININESS(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_SHININESS,N)
+
+#define AI_MATKEY_TEXFLAGS_OPACITY(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_OPACITY,N)
+
+#define AI_MATKEY_TEXFLAGS_DISPLACEMENT(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_DISPLACEMENT,N)
+
+#define AI_MATKEY_TEXFLAGS_LIGHTMAP(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_LIGHTMAP,N)
+
+#define AI_MATKEY_TEXFLAGS_REFLECTION(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_REFLECTION,N)
+
+#define AI_MATKEY_TEXFLAGS_UNKNOWN(N) \
+ AI_MATKEY_TEXFLAGS(aiTextureType_UNKNOWN,N)
+
+//! @endcond
+//!
+// ---------------------------------------------------------------------------
+/** @brief Retrieve a material property with a specific key from the material
+ *
+ * @param pMat Pointer to the input material. May not be NULL
+ * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+ * @param type Specifies the type of the texture to be retrieved (
+ * e.g. diffuse, specular, height map ...)
+ * @param index Index of the texture to be retrieved.
+ * @param pPropOut Pointer to receive a pointer to a valid aiMaterialProperty
+ * structure or NULL if the key has not been found. */
+// ---------------------------------------------------------------------------
+ASSIMP_API C_ENUM aiReturn aiGetMaterialProperty(
+ const C_STRUCT aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ const C_STRUCT aiMaterialProperty** pPropOut);
+
+// ---------------------------------------------------------------------------
+/** @brief Retrieve an array of float values with a specific key
+ * from the material
+ *
+ * Pass one of the AI_MATKEY_XXX constants for the last three parameters (the
+ * example reads the #AI_MATKEY_UVTRANSFORM property of the first diffuse texture)
+ * @code
+ * aiUVTransform trafo;
+ * unsigned int max = sizeof(aiUVTransform);
+ * if (AI_SUCCESS != aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE,0),
+ * (float*)&trafo, &max) || sizeof(aiUVTransform) != max)
+ * {
+ * // error handling
+ * }
+ * @endcode
+ *
+ * @param pMat Pointer to the input material. May not be NULL
+ * @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+ * @param pOut Pointer to a buffer to receive the result.
+ * @param pMax Specifies the size of the given buffer, in float's.
+ * Receives the number of values (not bytes!) read.
+ * @param type (see the code sample above)
+ * @param index (see the code sample above)
+ * @return Specifies whether the key has been found. If not, the output
+ * arrays remains unmodified and pMax is set to 0.*/
+// ---------------------------------------------------------------------------
+ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray(
+ const C_STRUCT aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ ai_real* pOut,
+ unsigned int* pMax);
+
+
+#ifdef __cplusplus
+
+// ---------------------------------------------------------------------------
+/** @brief Retrieve a single float property with a specific key from the material.
+*
+* Pass one of the AI_MATKEY_XXX constants for the last three parameters (the
+* example reads the #AI_MATKEY_SHININESS_STRENGTH property of the first diffuse texture)
+* @code
+* float specStrength = 1.f; // default value, remains unmodified if we fail.
+* aiGetMaterialFloat(mat, AI_MATKEY_SHININESS_STRENGTH,
+* (float*)&specStrength);
+* @endcode
+*
+* @param pMat Pointer to the input material. May not be NULL
+* @param pKey Key to search for. One of the AI_MATKEY_XXX constants.
+* @param pOut Receives the output float.
+* @param type (see the code sample above)
+* @param index (see the code sample above)
+* @return Specifies whether the key has been found. If not, the output
+* float remains unmodified.*/
+// ---------------------------------------------------------------------------
+inline aiReturn aiGetMaterialFloat(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ ai_real* pOut)
+{
+ return aiGetMaterialFloatArray(pMat,pKey,type,index,pOut,(unsigned int*)0x0);
+}
+
+#else
+
+// Use our friend, the C preprocessor
+#define aiGetMaterialFloat (pMat, type, index, pKey, pOut) \
+ aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL)
+
+#endif //!__cplusplus
+
+
+// ---------------------------------------------------------------------------
+/** @brief Retrieve an array of integer values with a specific key
+ * from a material
+ *
+ * See the sample for aiGetMaterialFloatArray for more information.*/
+ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ int* pOut,
+ unsigned int* pMax);
+
+
+#ifdef __cplusplus
+
+// ---------------------------------------------------------------------------
+/** @brief Retrieve an integer property with a specific key from a material
+ *
+ * See the sample for aiGetMaterialFloat for more information.*/
+// ---------------------------------------------------------------------------
+inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ int* pOut)
+{
+ return aiGetMaterialIntegerArray(pMat,pKey,type,index,pOut,(unsigned int*)0x0);
+}
+
+#else
+
+// use our friend, the C preprocessor
+#define aiGetMaterialInteger (pMat, type, index, pKey, pOut) \
+ aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL)
+
+#endif //!__cplusplus
+
+// ---------------------------------------------------------------------------
+/** @brief Retrieve a color value from the material property table
+*
+* See the sample for aiGetMaterialFloat for more information*/
+// ---------------------------------------------------------------------------
+ASSIMP_API C_ENUM aiReturn aiGetMaterialColor(const C_STRUCT aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ C_STRUCT aiColor4D* pOut);
+
+
+// ---------------------------------------------------------------------------
+/** @brief Retrieve a aiUVTransform value from the material property table
+*
+* See the sample for aiGetMaterialFloat for more information*/
+// ---------------------------------------------------------------------------
+ASSIMP_API C_ENUM aiReturn aiGetMaterialUVTransform(const C_STRUCT aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ C_STRUCT aiUVTransform* pOut);
+
+
+// ---------------------------------------------------------------------------
+/** @brief Retrieve a string from the material property table
+*
+* See the sample for aiGetMaterialFloat for more information.*/
+// ---------------------------------------------------------------------------
+ASSIMP_API C_ENUM aiReturn aiGetMaterialString(const C_STRUCT aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ C_STRUCT aiString* pOut);
+
+// ---------------------------------------------------------------------------
+/** Get the number of textures for a particular texture type.
+ * @param[in] pMat Pointer to the input material. May not be NULL
+ * @param type Texture type to check for
+ * @return Number of textures for this type.
+ * @note A texture can be easily queried using #aiGetMaterialTexture() */
+// ---------------------------------------------------------------------------
+ASSIMP_API unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat,
+ C_ENUM aiTextureType type);
+
+// ---------------------------------------------------------------------------
+/** @brief Helper function to get all values pertaining to a particular
+ * texture slot from a material structure.
+ *
+ * This function is provided just for convenience. You could also read the
+ * texture by parsing all of its properties manually. This function bundles
+ * all of them in a huge function monster.
+ *
+ * @param[in] mat Pointer to the input material. May not be NULL
+ * @param[in] type Specifies the texture stack to read from (e.g. diffuse,
+ * specular, height map ...).
+ * @param[in] index Index of the texture. The function fails if the
+ * requested index is not available for this texture type.
+ * #aiGetMaterialTextureCount() can be used to determine the number of
+ * textures in a particular texture stack.
+ * @param[out] path Receives the output path
+ * If the texture is embedded, receives a '*' followed by the id of
+ * the texture (for the textures stored in the corresponding scene) which
+ * can be converted to an int using a function like atoi.
+ * This parameter must be non-null.
+ * @param mapping The texture mapping mode to be used.
+ * Pass NULL if you're not interested in this information.
+ * @param[out] uvindex For UV-mapped textures: receives the index of the UV
+ * source channel. Unmodified otherwise.
+ * Pass NULL if you're not interested in this information.
+ * @param[out] blend Receives the blend factor for the texture
+ * Pass NULL if you're not interested in this information.
+ * @param[out] op Receives the texture blend operation to be perform between
+ * this texture and the previous texture.
+ * Pass NULL if you're not interested in this information.
+ * @param[out] mapmode Receives the mapping modes to be used for the texture.
+ * Pass NULL if you're not interested in this information. Otherwise,
+ * pass a pointer to an array of two aiTextureMapMode's (one for each
+ * axis, UV order).
+ * @param[out] flags Receives the the texture flags.
+ * @return AI_SUCCESS on success, otherwise something else. Have fun.*/
+// ---------------------------------------------------------------------------
+#ifdef __cplusplus
+ASSIMP_API aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat,
+ aiTextureType type,
+ unsigned int index,
+ aiString* path,
+ aiTextureMapping* mapping = NULL,
+ unsigned int* uvindex = NULL,
+ ai_real* blend = NULL,
+ aiTextureOp* op = NULL,
+ aiTextureMapMode* mapmode = NULL,
+ unsigned int* flags = NULL);
+#else
+C_ENUM aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat,
+ C_ENUM aiTextureType type,
+ unsigned int index,
+ C_STRUCT aiString* path,
+ C_ENUM aiTextureMapping* mapping /*= NULL*/,
+ unsigned int* uvindex /*= NULL*/,
+ ai_real* blend /*= NULL*/,
+ C_ENUM aiTextureOp* op /*= NULL*/,
+ C_ENUM aiTextureMapMode* mapmode /*= NULL*/,
+ unsigned int* flags /*= NULL*/);
+#endif // !#ifdef __cplusplus
+
+
+#ifdef __cplusplus
+}
+
+#include "material.inl"
+
+#endif //!__cplusplus
+
+#endif //!!AI_MATERIAL_H_INC
diff --git a/thirdparty/assimp/include/assimp/material.inl b/thirdparty/assimp/include/assimp/material.inl
new file mode 100644
index 0000000000..b05d6af6c3
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/material.inl
@@ -0,0 +1,390 @@
+/*
+---------------------------------------------------------------------------
+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 material.inl
+ * @brief Defines the C++ getters for the material system
+ */
+
+#pragma once
+#ifndef AI_MATERIAL_INL_INC
+#define AI_MATERIAL_INL_INC
+
+// ---------------------------------------------------------------------------
+inline aiPropertyTypeInfo ai_real_to_property_type_info(float)
+{
+ return aiPTI_Float;
+}
+
+inline aiPropertyTypeInfo ai_real_to_property_type_info(double)
+{
+ return aiPTI_Double;
+}
+// ---------------------------------------------------------------------------
+
+//! @cond never
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::GetTexture( aiTextureType type,
+ unsigned int index,
+ C_STRUCT aiString* path,
+ aiTextureMapping* mapping /*= NULL*/,
+ unsigned int* uvindex /*= NULL*/,
+ ai_real* blend /*= NULL*/,
+ aiTextureOp* op /*= NULL*/,
+ aiTextureMapMode* mapmode /*= NULL*/) const
+{
+ return ::aiGetMaterialTexture(this,type,index,path,mapping,uvindex,blend,op,mapmode);
+}
+
+// ---------------------------------------------------------------------------
+inline unsigned int aiMaterial::GetTextureCount(aiTextureType type) const
+{
+ return ::aiGetMaterialTextureCount(this,type);
+}
+
+// ---------------------------------------------------------------------------
+template <typename Type>
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx, Type* pOut,
+ unsigned int* pMax) const
+{
+ unsigned int iNum = pMax ? *pMax : 1;
+
+ const aiMaterialProperty* prop;
+ const aiReturn ret = ::aiGetMaterialProperty(this,pKey,type,idx,
+ (const aiMaterialProperty**)&prop);
+ if ( AI_SUCCESS == ret ) {
+
+ if (prop->mDataLength < sizeof(Type)*iNum) {
+ return AI_FAILURE;
+ }
+
+ if (prop->mType != aiPTI_Buffer) {
+ return AI_FAILURE;
+ }
+
+ iNum = std::min((size_t)iNum,prop->mDataLength / sizeof(Type));
+ ::memcpy(pOut,prop->mData,iNum * sizeof(Type));
+ if (pMax) {
+ *pMax = iNum;
+ }
+ }
+ return ret;
+}
+
+// ---------------------------------------------------------------------------
+template <typename Type>
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,Type& pOut) const
+{
+ const aiMaterialProperty* prop;
+ const aiReturn ret = ::aiGetMaterialProperty(this,pKey,type,idx,
+ (const aiMaterialProperty**)&prop);
+ if ( AI_SUCCESS == ret ) {
+
+ if (prop->mDataLength < sizeof(Type)) {
+ return AI_FAILURE;
+ }
+
+ if (prop->mType != aiPTI_Buffer) {
+ return AI_FAILURE;
+ }
+
+ ::memcpy( &pOut, prop->mData, sizeof( Type ) );
+ }
+ return ret;
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,ai_real* pOut,
+ unsigned int* pMax) const
+{
+ return ::aiGetMaterialFloatArray(this,pKey,type,idx,pOut,pMax);
+}
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,int* pOut,
+ unsigned int* pMax) const
+{
+ return ::aiGetMaterialIntegerArray(this,pKey,type,idx,pOut,pMax);
+}
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,ai_real& pOut) const
+{
+ return aiGetMaterialFloat(this,pKey,type,idx,&pOut);
+}
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,int& pOut) const
+{
+ return aiGetMaterialInteger(this,pKey,type,idx,&pOut);
+}
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,aiColor4D& pOut) const
+{
+ return aiGetMaterialColor(this,pKey,type,idx,&pOut);
+}
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,aiColor3D& pOut) const
+{
+ aiColor4D c;
+ const aiReturn ret = aiGetMaterialColor(this,pKey,type,idx,&c);
+ pOut = aiColor3D(c.r,c.g,c.b);
+ return ret;
+}
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,aiString& pOut) const
+{
+ return aiGetMaterialString(this,pKey,type,idx,&pOut);
+}
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::Get(const char* pKey,unsigned int type,
+ unsigned int idx,aiUVTransform& pOut) const
+{
+ return aiGetMaterialUVTransform(this,pKey,type,idx,&pOut);
+}
+
+
+// ---------------------------------------------------------------------------
+template<class TYPE>
+aiReturn aiMaterial::AddProperty (const TYPE* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(TYPE),
+ pKey,type,index,aiPTI_Buffer);
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::AddProperty(const float* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(float),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::AddProperty(const double* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(double),
+ pKey,type,index,aiPTI_Double);
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::AddProperty(const aiUVTransform* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiUVTransform),
+ pKey,type,index,ai_real_to_property_type_info(pInput->mRotation));
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::AddProperty(const aiColor4D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiColor4D),
+ pKey,type,index,ai_real_to_property_type_info(pInput->a));
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::AddProperty(const aiColor3D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiColor3D),
+ pKey,type,index,ai_real_to_property_type_info(pInput->b));
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::AddProperty(const aiVector3D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiVector3D),
+ pKey,type,index,ai_real_to_property_type_info(pInput->x));
+}
+
+// ---------------------------------------------------------------------------
+inline aiReturn aiMaterial::AddProperty(const int* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(int),
+ pKey,type,index,aiPTI_Integer);
+}
+
+
+// ---------------------------------------------------------------------------
+// The template specializations below are for backwards compatibility.
+// The recommended way to add material properties is using the non-template
+// overloads.
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn aiMaterial::AddProperty<float>(const float* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(float),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn aiMaterial::AddProperty<double>(const double* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(double),
+ pKey,type,index,aiPTI_Double);
+}
+
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn aiMaterial::AddProperty<aiUVTransform>(const aiUVTransform* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiUVTransform),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn aiMaterial::AddProperty<aiColor4D>(const aiColor4D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiColor4D),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn aiMaterial::AddProperty<aiColor3D>(const aiColor3D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiColor3D),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn aiMaterial::AddProperty<aiVector3D>(const aiVector3D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiVector3D),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ---------------------------------------------------------------------------
+template<>
+inline aiReturn aiMaterial::AddProperty<int>(const int* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(int),
+ pKey,type,index,aiPTI_Integer);
+}
+
+//! @endcond
+
+#endif //! AI_MATERIAL_INL_INC
diff --git a/thirdparty/assimp/include/assimp/matrix3x3.h b/thirdparty/assimp/include/assimp/matrix3x3.h
new file mode 100644
index 0000000000..22b69561ff
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/matrix3x3.h
@@ -0,0 +1,183 @@
+/*
+---------------------------------------------------------------------------
+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 matrix3x3.h
+ * @brief Definition of a 3x3 matrix, including operators when compiling in C++
+ */
+#pragma once
+#ifndef AI_MATRIX3X3_H_INC
+#define AI_MATRIX3X3_H_INC
+
+#include "defs.h"
+
+#ifdef __cplusplus
+
+template <typename T> class aiMatrix4x4t;
+template <typename T> class aiVector2t;
+
+// ---------------------------------------------------------------------------
+/** @brief Represents a row-major 3x3 matrix
+ *
+ * There's much confusion about matrix layouts (column vs. row order).
+ * This is *always* a row-major matrix. Not even with the
+ * #aiProcess_ConvertToLeftHanded flag, which absolutely does not affect
+ * matrix order - it just affects the handedness of the coordinate system
+ * defined thereby.
+ */
+template <typename TReal>
+class aiMatrix3x3t
+{
+public:
+
+ aiMatrix3x3t() AI_NO_EXCEPT :
+ a1(static_cast<TReal>(1.0f)), a2(), a3(),
+ b1(), b2(static_cast<TReal>(1.0f)), b3(),
+ c1(), c2(), c3(static_cast<TReal>(1.0f)) {}
+
+ aiMatrix3x3t ( TReal _a1, TReal _a2, TReal _a3,
+ TReal _b1, TReal _b2, TReal _b3,
+ TReal _c1, TReal _c2, TReal _c3) :
+ a1(_a1), a2(_a2), a3(_a3),
+ b1(_b1), b2(_b2), b3(_b3),
+ c1(_c1), c2(_c2), c3(_c3)
+ {}
+
+public:
+
+ // matrix multiplication.
+ aiMatrix3x3t& operator *= (const aiMatrix3x3t& m);
+ aiMatrix3x3t operator * (const aiMatrix3x3t& m) const;
+
+ // array access operators
+ TReal* operator[] (unsigned int p_iIndex);
+ const TReal* operator[] (unsigned int p_iIndex) const;
+
+ // comparison operators
+ bool operator== (const aiMatrix4x4t<TReal>& m) const;
+ bool operator!= (const aiMatrix4x4t<TReal>& m) const;
+
+ bool Equal(const aiMatrix4x4t<TReal>& m, TReal epsilon = 1e-6) const;
+
+ template <typename TOther>
+ operator aiMatrix3x3t<TOther> () const;
+
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Construction from a 4x4 matrix. The remaining parts
+ * of the matrix are ignored.
+ */
+ explicit aiMatrix3x3t( const aiMatrix4x4t<TReal>& pMatrix);
+
+ // -------------------------------------------------------------------
+ /** @brief Transpose the matrix
+ */
+ aiMatrix3x3t& Transpose();
+
+ // -------------------------------------------------------------------
+ /** @brief Invert the matrix.
+ * If the matrix is not invertible all elements are set to qnan.
+ * Beware, use (f != f) to check whether a TReal f is qnan.
+ */
+ aiMatrix3x3t& Inverse();
+ TReal Determinant() const;
+
+public:
+ // -------------------------------------------------------------------
+ /** @brief Returns a rotation matrix for a rotation around z
+ * @param a Rotation angle, in radians
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix3x3t& RotationZ(TReal a, aiMatrix3x3t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a rotation matrix for a rotation around
+ * an arbitrary axis.
+ *
+ * @param a Rotation angle, in radians
+ * @param axis Axis to rotate around
+ * @param out To be filled
+ */
+ static aiMatrix3x3t& Rotation( TReal a,
+ const aiVector3t<TReal>& axis, aiMatrix3x3t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a translation matrix
+ * @param v Translation vector
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix3x3t& Translation( const aiVector2t<TReal>& v, aiMatrix3x3t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief A function for creating a rotation matrix that rotates a
+ * vector called "from" into another vector called "to".
+ * Input : from[3], to[3] which both must be *normalized* non-zero vectors
+ * Output: mtx[3][3] -- a 3x3 matrix in column-major form
+ * Authors: Tomas Möller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+ static aiMatrix3x3t& FromToMatrix(const aiVector3t<TReal>& from,
+ const aiVector3t<TReal>& to, aiMatrix3x3t& out);
+
+public:
+ TReal a1, a2, a3;
+ TReal b1, b2, b3;
+ TReal c1, c2, c3;
+};
+
+typedef aiMatrix3x3t<ai_real> aiMatrix3x3;
+
+#else
+
+struct aiMatrix3x3 {
+ ai_real a1, a2, a3;
+ ai_real b1, b2, b3;
+ ai_real c1, c2, c3;
+};
+
+#endif // __cplusplus
+
+#endif // AI_MATRIX3X3_H_INC
diff --git a/thirdparty/assimp/include/assimp/matrix3x3.inl b/thirdparty/assimp/include/assimp/matrix3x3.inl
new file mode 100644
index 0000000000..d9d45a3e92
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/matrix3x3.inl
@@ -0,0 +1,357 @@
+/*
+---------------------------------------------------------------------------
+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 matrix3x3.inl
+ * @brief Inline implementation of the 3x3 matrix operators
+ */
+#pragma once
+#ifndef AI_MATRIX3X3_INL_INC
+#define AI_MATRIX3X3_INL_INC
+
+#ifdef __cplusplus
+#include "matrix3x3.h"
+
+#include "matrix4x4.h"
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+// ------------------------------------------------------------------------------------------------
+// Construction from a 4x4 matrix. The remaining parts of the matrix are ignored.
+template <typename TReal>
+inline aiMatrix3x3t<TReal>::aiMatrix3x3t( const aiMatrix4x4t<TReal>& pMatrix)
+{
+ a1 = pMatrix.a1; a2 = pMatrix.a2; a3 = pMatrix.a3;
+ b1 = pMatrix.b1; b2 = pMatrix.b2; b3 = pMatrix.b3;
+ c1 = pMatrix.c1; c2 = pMatrix.c2; c3 = pMatrix.c3;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::operator *= (const aiMatrix3x3t<TReal>& m)
+{
+ *this = aiMatrix3x3t<TReal>(m.a1 * a1 + m.b1 * a2 + m.c1 * a3,
+ m.a2 * a1 + m.b2 * a2 + m.c2 * a3,
+ m.a3 * a1 + m.b3 * a2 + m.c3 * a3,
+ m.a1 * b1 + m.b1 * b2 + m.c1 * b3,
+ m.a2 * b1 + m.b2 * b2 + m.c2 * b3,
+ m.a3 * b1 + m.b3 * b2 + m.c3 * b3,
+ m.a1 * c1 + m.b1 * c2 + m.c1 * c3,
+ m.a2 * c1 + m.b2 * c2 + m.c2 * c3,
+ m.a3 * c1 + m.b3 * c2 + m.c3 * c3);
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+template <typename TOther>
+aiMatrix3x3t<TReal>::operator aiMatrix3x3t<TOther> () const
+{
+ return aiMatrix3x3t<TOther>(static_cast<TOther>(a1),static_cast<TOther>(a2),static_cast<TOther>(a3),
+ static_cast<TOther>(b1),static_cast<TOther>(b2),static_cast<TOther>(b3),
+ static_cast<TOther>(c1),static_cast<TOther>(c2),static_cast<TOther>(c3));
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix3x3t<TReal> aiMatrix3x3t<TReal>::operator* (const aiMatrix3x3t<TReal>& m) const
+{
+ aiMatrix3x3t<TReal> temp( *this);
+ temp *= m;
+ return temp;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline TReal* aiMatrix3x3t<TReal>::operator[] (unsigned int p_iIndex) {
+ switch ( p_iIndex ) {
+ case 0:
+ return &a1;
+ case 1:
+ return &b1;
+ case 2:
+ return &c1;
+ default:
+ break;
+ }
+ return &a1;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline const TReal* aiMatrix3x3t<TReal>::operator[] (unsigned int p_iIndex) const {
+ switch ( p_iIndex ) {
+ case 0:
+ return &a1;
+ case 1:
+ return &b1;
+ case 2:
+ return &c1;
+ default:
+ break;
+ }
+ return &a1;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline bool aiMatrix3x3t<TReal>::operator== (const aiMatrix4x4t<TReal>& m) const
+{
+ return a1 == m.a1 && a2 == m.a2 && a3 == m.a3 &&
+ b1 == m.b1 && b2 == m.b2 && b3 == m.b3 &&
+ c1 == m.c1 && c2 == m.c2 && c3 == m.c3;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline bool aiMatrix3x3t<TReal>::operator!= (const aiMatrix4x4t<TReal>& m) const
+{
+ return !(*this == m);
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline bool aiMatrix3x3t<TReal>::Equal(const aiMatrix4x4t<TReal>& m, TReal epsilon) const {
+ return
+ std::abs(a1 - m.a1) <= epsilon &&
+ std::abs(a2 - m.a2) <= epsilon &&
+ std::abs(a3 - m.a3) <= epsilon &&
+ std::abs(b1 - m.b1) <= epsilon &&
+ std::abs(b2 - m.b2) <= epsilon &&
+ std::abs(b3 - m.b3) <= epsilon &&
+ std::abs(c1 - m.c1) <= epsilon &&
+ std::abs(c2 - m.c2) <= epsilon &&
+ std::abs(c3 - m.c3) <= epsilon;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Transpose()
+{
+ // (TReal&) don't remove, GCC complains cause of packed fields
+ std::swap( (TReal&)a2, (TReal&)b1);
+ std::swap( (TReal&)a3, (TReal&)c1);
+ std::swap( (TReal&)b3, (TReal&)c2);
+ return *this;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline TReal aiMatrix3x3t<TReal>::Determinant() const
+{
+ return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 + a3*b1*c2 - a3*b2*c1;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Inverse()
+{
+ // Compute the reciprocal determinant
+ TReal det = Determinant();
+ if(det == static_cast<TReal>(0.0))
+ {
+ // Matrix not invertible. Setting all elements to nan is not really
+ // correct in a mathematical sense; but at least qnans are easy to
+ // spot. XXX we might throw an exception instead, which would
+ // be even much better to spot :/.
+ const TReal nan = std::numeric_limits<TReal>::quiet_NaN();
+ *this = aiMatrix3x3t<TReal>( nan,nan,nan,nan,nan,nan,nan,nan,nan);
+
+ return *this;
+ }
+
+ TReal invdet = static_cast<TReal>(1.0) / det;
+
+ aiMatrix3x3t<TReal> res;
+ res.a1 = invdet * (b2 * c3 - b3 * c2);
+ res.a2 = -invdet * (a2 * c3 - a3 * c2);
+ res.a3 = invdet * (a2 * b3 - a3 * b2);
+ res.b1 = -invdet * (b1 * c3 - b3 * c1);
+ res.b2 = invdet * (a1 * c3 - a3 * c1);
+ res.b3 = -invdet * (a1 * b3 - a3 * b1);
+ res.c1 = invdet * (b1 * c2 - b2 * c1);
+ res.c2 = -invdet * (a1 * c2 - a2 * c1);
+ res.c3 = invdet * (a1 * b2 - a2 * b1);
+ *this = res;
+
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::RotationZ(TReal a, aiMatrix3x3t<TReal>& out)
+{
+ out.a1 = out.b2 = std::cos(a);
+ out.b1 = std::sin(a);
+ out.a2 = - out.b1;
+
+ out.a3 = out.b3 = out.c1 = out.c2 = 0.f;
+ out.c3 = 1.f;
+
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns a rotation matrix for a rotation around an arbitrary axis.
+template <typename TReal>
+inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Rotation( TReal a, const aiVector3t<TReal>& axis, aiMatrix3x3t<TReal>& out)
+{
+ TReal c = std::cos( a), s = std::sin( a), t = 1 - c;
+ TReal x = axis.x, y = axis.y, z = axis.z;
+
+ // Many thanks to MathWorld and Wikipedia
+ out.a1 = t*x*x + c; out.a2 = t*x*y - s*z; out.a3 = t*x*z + s*y;
+ out.b1 = t*x*y + s*z; out.b2 = t*y*y + c; out.b3 = t*y*z - s*x;
+ out.c1 = t*x*z - s*y; out.c2 = t*y*z + s*x; out.c3 = t*z*z + c;
+
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::Translation( const aiVector2t<TReal>& v, aiMatrix3x3t<TReal>& out)
+{
+ out = aiMatrix3x3t<TReal>();
+ out.a3 = v.x;
+ out.b3 = v.y;
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+/** A function for creating a rotation matrix that rotates a vector called
+ * "from" into another vector called "to".
+ * Input : from[3], to[3] which both must be *normalized* non-zero vectors
+ * Output: mtx[3][3] -- a 3x3 matrix in colum-major form
+ * Authors: Tomas Möller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix3x3t<TReal>& aiMatrix3x3t<TReal>::FromToMatrix(const aiVector3t<TReal>& from,
+ const aiVector3t<TReal>& to, aiMatrix3x3t<TReal>& mtx)
+{
+ const TReal e = from * to;
+ const TReal f = (e < 0)? -e:e;
+
+ if (f > static_cast<TReal>(1.0) - static_cast<TReal>(0.00001)) /* "from" and "to"-vector almost parallel */
+ {
+ aiVector3D u,v; /* temporary storage vectors */
+ aiVector3D x; /* vector most nearly orthogonal to "from" */
+
+ x.x = (from.x > 0.0)? from.x : -from.x;
+ x.y = (from.y > 0.0)? from.y : -from.y;
+ x.z = (from.z > 0.0)? from.z : -from.z;
+
+ if (x.x < x.y)
+ {
+ if (x.x < x.z)
+ {
+ x.x = static_cast<TReal>(1.0);
+ x.y = x.z = static_cast<TReal>(0.0);
+ }
+ else
+ {
+ x.z = static_cast<TReal>(1.0);
+ x.x = x.y = static_cast<TReal>(0.0);
+ }
+ }
+ else
+ {
+ if (x.y < x.z)
+ {
+ x.y = static_cast<TReal>(1.0);
+ x.x = x.z = static_cast<TReal>(0.0);
+ }
+ else
+ {
+ x.z = static_cast<TReal>(1.0);
+ x.x = x.y = static_cast<TReal>(0.0);
+ }
+ }
+
+ u.x = x.x - from.x; u.y = x.y - from.y; u.z = x.z - from.z;
+ v.x = x.x - to.x; v.y = x.y - to.y; v.z = x.z - to.z;
+
+ const TReal c1_ = static_cast<TReal>(2.0) / (u * u);
+ const TReal c2_ = static_cast<TReal>(2.0) / (v * v);
+ const TReal c3_ = c1_ * c2_ * (u * v);
+
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ for (unsigned int j = 0; j < 3; j++)
+ {
+ mtx[i][j] = - c1_ * u[i] * u[j] - c2_ * v[i] * v[j]
+ + c3_ * v[i] * u[j];
+ }
+ mtx[i][i] += static_cast<TReal>(1.0);
+ }
+ }
+ else /* the most common case, unless "from"="to", or "from"=-"to" */
+ {
+ const aiVector3D v = from ^ to;
+ /* ... use this hand optimized version (9 mults less) */
+ const TReal h = static_cast<TReal>(1.0)/(static_cast<TReal>(1.0) + e); /* optimization by Gottfried Chen */
+ const TReal hvx = h * v.x;
+ const TReal hvz = h * v.z;
+ const TReal hvxy = hvx * v.y;
+ const TReal hvxz = hvx * v.z;
+ const TReal hvyz = hvz * v.y;
+ mtx[0][0] = e + hvx * v.x;
+ mtx[0][1] = hvxy - v.z;
+ mtx[0][2] = hvxz + v.y;
+
+ mtx[1][0] = hvxy + v.z;
+ mtx[1][1] = e + h * v.y * v.y;
+ mtx[1][2] = hvyz - v.x;
+
+ mtx[2][0] = hvxz - v.y;
+ mtx[2][1] = hvyz + v.x;
+ mtx[2][2] = e + hvz * v.z;
+ }
+ return mtx;
+}
+
+
+#endif // __cplusplus
+#endif // AI_MATRIX3X3_INL_INC
diff --git a/thirdparty/assimp/include/assimp/matrix4x4.h b/thirdparty/assimp/include/assimp/matrix4x4.h
new file mode 100644
index 0000000000..046bb535f2
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/matrix4x4.h
@@ -0,0 +1,280 @@
+/*
+---------------------------------------------------------------------------
+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 matrix4x4.h
+ * @brief 4x4 matrix structure, including operators when compiling in C++
+ */
+#pragma once
+#ifndef AI_MATRIX4X4_H_INC
+#define AI_MATRIX4X4_H_INC
+
+#include "vector3.h"
+#include "defs.h"
+
+#ifdef __cplusplus
+
+template<typename TReal> class aiMatrix3x3t;
+template<typename TReal> class aiQuaterniont;
+
+// ---------------------------------------------------------------------------
+/** @brief Represents a row-major 4x4 matrix, use this for homogeneous
+ * coordinates.
+ *
+ * There's much confusion about matrix layouts (column vs. row order).
+ * This is *always* a row-major matrix. Not even with the
+ * #aiProcess_ConvertToLeftHanded flag, which absolutely does not affect
+ * matrix order - it just affects the handedness of the coordinate system
+ * defined thereby.
+ */
+template<typename TReal>
+class aiMatrix4x4t
+{
+public:
+
+ /** set to identity */
+ aiMatrix4x4t() AI_NO_EXCEPT;
+
+ /** construction from single values */
+ aiMatrix4x4t ( TReal _a1, TReal _a2, TReal _a3, TReal _a4,
+ TReal _b1, TReal _b2, TReal _b3, TReal _b4,
+ TReal _c1, TReal _c2, TReal _c3, TReal _c4,
+ TReal _d1, TReal _d2, TReal _d3, TReal _d4);
+
+
+ /** construction from 3x3 matrix, remaining elements are set to identity */
+ explicit aiMatrix4x4t( const aiMatrix3x3t<TReal>& m);
+
+ /** construction from position, rotation and scaling components
+ * @param scaling The scaling for the x,y,z axes
+ * @param rotation The rotation as a hamilton quaternion
+ * @param position The position for the x,y,z axes
+ */
+ aiMatrix4x4t(const aiVector3t<TReal>& scaling, const aiQuaterniont<TReal>& rotation,
+ const aiVector3t<TReal>& position);
+
+public:
+
+ // array access operators
+ /** @fn TReal* operator[] (unsigned int p_iIndex)
+ * @param [in] p_iIndex - index of the row.
+ * @return pointer to pointed row.
+ */
+ TReal* operator[] (unsigned int p_iIndex);
+
+ /** @fn const TReal* operator[] (unsigned int p_iIndex) const
+ * @overload TReal* operator[] (unsigned int p_iIndex)
+ */
+ const TReal* operator[] (unsigned int p_iIndex) const;
+
+ // comparison operators
+ bool operator== (const aiMatrix4x4t& m) const;
+ bool operator!= (const aiMatrix4x4t& m) const;
+
+ bool Equal(const aiMatrix4x4t& m, TReal epsilon = 1e-6) const;
+
+ // matrix multiplication.
+ aiMatrix4x4t& operator *= (const aiMatrix4x4t& m);
+ aiMatrix4x4t operator * (const aiMatrix4x4t& m) const;
+ aiMatrix4x4t operator * (const TReal& aFloat) const;
+ aiMatrix4x4t operator + (const aiMatrix4x4t& aMatrix) const;
+
+ template <typename TOther>
+ operator aiMatrix4x4t<TOther> () const;
+
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Transpose the matrix */
+ aiMatrix4x4t& Transpose();
+
+ // -------------------------------------------------------------------
+ /** @brief Invert the matrix.
+ * If the matrix is not invertible all elements are set to qnan.
+ * Beware, use (f != f) to check whether a TReal f is qnan.
+ */
+ aiMatrix4x4t& Inverse();
+ TReal Determinant() const;
+
+
+ // -------------------------------------------------------------------
+ /** @brief Returns true of the matrix is the identity matrix.
+ * The check is performed against a not so small epsilon.
+ */
+ inline bool IsIdentity() const;
+
+ // -------------------------------------------------------------------
+ /** @brief Decompose a trafo matrix into its original components
+ * @param scaling Receives the output scaling for the x,y,z axes
+ * @param rotation Receives the output rotation as a hamilton
+ * quaternion
+ * @param position Receives the output position for the x,y,z axes
+ */
+ void Decompose (aiVector3t<TReal>& scaling, aiQuaterniont<TReal>& rotation,
+ aiVector3t<TReal>& position) const;
+
+ // -------------------------------------------------------------------
+ /** @fn void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const
+ * @brief Decompose a trafo matrix into its original components.
+ * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat
+ * @param [out] pScaling - Receives the output scaling for the x,y,z axes.
+ * @param [out] pRotation - Receives the output rotation as a Euler angles.
+ * @param [out] pPosition - Receives the output position for the x,y,z axes.
+ */
+ void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const;
+
+ // -------------------------------------------------------------------
+ /** @fn void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle, aiVector3t<TReal>& pPosition) const
+ * @brief Decompose a trafo matrix into its original components
+ * Thx to good FAQ at http://www.gamedev.ru/code/articles/faq_matrix_quat
+ * @param [out] pScaling - Receives the output scaling for the x,y,z axes.
+ * @param [out] pRotationAxis - Receives the output rotation axis.
+ * @param [out] pRotationAngle - Receives the output rotation angle for @ref pRotationAxis.
+ * @param [out] pPosition - Receives the output position for the x,y,z axes.
+ */
+ void Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle, aiVector3t<TReal>& pPosition) const;
+
+ // -------------------------------------------------------------------
+ /** @brief Decompose a trafo matrix with no scaling into its
+ * original components
+ * @param rotation Receives the output rotation as a hamilton
+ * quaternion
+ * @param position Receives the output position for the x,y,z axes
+ */
+ void DecomposeNoScaling (aiQuaterniont<TReal>& rotation,
+ aiVector3t<TReal>& position) const;
+
+
+ // -------------------------------------------------------------------
+ /** @brief Creates a trafo matrix from a set of euler angles
+ * @param x Rotation angle for the x-axis, in radians
+ * @param y Rotation angle for the y-axis, in radians
+ * @param z Rotation angle for the z-axis, in radians
+ */
+ aiMatrix4x4t& FromEulerAnglesXYZ(TReal x, TReal y, TReal z);
+ aiMatrix4x4t& FromEulerAnglesXYZ(const aiVector3t<TReal>& blubb);
+
+public:
+ // -------------------------------------------------------------------
+ /** @brief Returns a rotation matrix for a rotation around the x axis
+ * @param a Rotation angle, in radians
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4t& RotationX(TReal a, aiMatrix4x4t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a rotation matrix for a rotation around the y axis
+ * @param a Rotation angle, in radians
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4t& RotationY(TReal a, aiMatrix4x4t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a rotation matrix for a rotation around the z axis
+ * @param a Rotation angle, in radians
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4t& RotationZ(TReal a, aiMatrix4x4t& out);
+
+ // -------------------------------------------------------------------
+ /** Returns a rotation matrix for a rotation around an arbitrary axis.
+ * @param a Rotation angle, in radians
+ * @param axis Rotation axis, should be a normalized vector.
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4t& Rotation(TReal a, const aiVector3t<TReal>& axis,
+ aiMatrix4x4t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a translation matrix
+ * @param v Translation vector
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4t& Translation( const aiVector3t<TReal>& v,
+ aiMatrix4x4t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns a scaling matrix
+ * @param v Scaling vector
+ * @param out Receives the output matrix
+ * @return Reference to the output matrix
+ */
+ static aiMatrix4x4t& Scaling( const aiVector3t<TReal>& v, aiMatrix4x4t& out);
+
+ // -------------------------------------------------------------------
+ /** @brief A function for creating a rotation matrix that rotates a
+ * vector called "from" into another vector called "to".
+ * Input : from[3], to[3] which both must be *normalized* non-zero vectors
+ * Output: mtx[3][3] -- a 3x3 matrix in column-major form
+ * Authors: Tomas Mueller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+ static aiMatrix4x4t& FromToMatrix(const aiVector3t<TReal>& from,
+ const aiVector3t<TReal>& to, aiMatrix4x4t& out);
+
+public:
+ TReal a1, a2, a3, a4;
+ TReal b1, b2, b3, b4;
+ TReal c1, c2, c3, c4;
+ TReal d1, d2, d3, d4;
+};
+
+typedef aiMatrix4x4t<ai_real> aiMatrix4x4;
+
+#else
+
+struct aiMatrix4x4 {
+ ai_real a1, a2, a3, a4;
+ ai_real b1, b2, b3, b4;
+ ai_real c1, c2, c3, c4;
+ ai_real d1, d2, d3, d4;
+};
+
+
+#endif // __cplusplus
+
+#endif // AI_MATRIX4X4_H_INC
diff --git a/thirdparty/assimp/include/assimp/matrix4x4.inl b/thirdparty/assimp/include/assimp/matrix4x4.inl
new file mode 100644
index 0000000000..ebc67a06ec
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/matrix4x4.inl
@@ -0,0 +1,686 @@
+/*
+---------------------------------------------------------------------------
+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 matrix4x4.inl
+ * @brief Inline implementation of the 4x4 matrix operators
+ */
+#pragma once
+#ifndef AI_MATRIX4X4_INL_INC
+#define AI_MATRIX4X4_INL_INC
+
+#ifdef __cplusplus
+
+#include "matrix4x4.h"
+#include "matrix3x3.h"
+#include "quaternion.h"
+
+#include <algorithm>
+#include <limits>
+#include <cmath>
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+aiMatrix4x4t<TReal>::aiMatrix4x4t() AI_NO_EXCEPT :
+ a1(1.0f), a2(), a3(), a4(),
+ b1(), b2(1.0f), b3(), b4(),
+ c1(), c2(), c3(1.0f), c4(),
+ d1(), d2(), d3(), d4(1.0f)
+{
+
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+aiMatrix4x4t<TReal>::aiMatrix4x4t (TReal _a1, TReal _a2, TReal _a3, TReal _a4,
+ TReal _b1, TReal _b2, TReal _b3, TReal _b4,
+ TReal _c1, TReal _c2, TReal _c3, TReal _c4,
+ TReal _d1, TReal _d2, TReal _d3, TReal _d4) :
+ a1(_a1), a2(_a2), a3(_a3), a4(_a4),
+ b1(_b1), b2(_b2), b3(_b3), b4(_b4),
+ c1(_c1), c2(_c2), c3(_c3), c4(_c4),
+ d1(_d1), d2(_d2), d3(_d3), d4(_d4)
+{
+
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+template <typename TOther>
+aiMatrix4x4t<TReal>::operator aiMatrix4x4t<TOther> () const
+{
+ return aiMatrix4x4t<TOther>(static_cast<TOther>(a1),static_cast<TOther>(a2),static_cast<TOther>(a3),static_cast<TOther>(a4),
+ static_cast<TOther>(b1),static_cast<TOther>(b2),static_cast<TOther>(b3),static_cast<TOther>(b4),
+ static_cast<TOther>(c1),static_cast<TOther>(c2),static_cast<TOther>(c3),static_cast<TOther>(c4),
+ static_cast<TOther>(d1),static_cast<TOther>(d2),static_cast<TOther>(d3),static_cast<TOther>(d4));
+}
+
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>::aiMatrix4x4t (const aiMatrix3x3t<TReal>& m)
+{
+ a1 = m.a1; a2 = m.a2; a3 = m.a3; a4 = static_cast<TReal>(0.0);
+ b1 = m.b1; b2 = m.b2; b3 = m.b3; b4 = static_cast<TReal>(0.0);
+ c1 = m.c1; c2 = m.c2; c3 = m.c3; c4 = static_cast<TReal>(0.0);
+ d1 = static_cast<TReal>(0.0); d2 = static_cast<TReal>(0.0); d3 = static_cast<TReal>(0.0); d4 = static_cast<TReal>(1.0);
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>::aiMatrix4x4t (const aiVector3t<TReal>& scaling, const aiQuaterniont<TReal>& rotation, const aiVector3t<TReal>& position)
+{
+ // build a 3x3 rotation matrix
+ aiMatrix3x3t<TReal> m = rotation.GetMatrix();
+
+ a1 = m.a1 * scaling.x;
+ a2 = m.a2 * scaling.x;
+ a3 = m.a3 * scaling.x;
+ a4 = position.x;
+
+ b1 = m.b1 * scaling.y;
+ b2 = m.b2 * scaling.y;
+ b3 = m.b3 * scaling.y;
+ b4 = position.y;
+
+ c1 = m.c1 * scaling.z;
+ c2 = m.c2 * scaling.z;
+ c3 = m.c3 * scaling.z;
+ c4= position.z;
+
+ d1 = static_cast<TReal>(0.0);
+ d2 = static_cast<TReal>(0.0);
+ d3 = static_cast<TReal>(0.0);
+ d4 = static_cast<TReal>(1.0);
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::operator *= (const aiMatrix4x4t<TReal>& m)
+{
+ *this = aiMatrix4x4t<TReal>(
+ m.a1 * a1 + m.b1 * a2 + m.c1 * a3 + m.d1 * a4,
+ m.a2 * a1 + m.b2 * a2 + m.c2 * a3 + m.d2 * a4,
+ m.a3 * a1 + m.b3 * a2 + m.c3 * a3 + m.d3 * a4,
+ m.a4 * a1 + m.b4 * a2 + m.c4 * a3 + m.d4 * a4,
+ m.a1 * b1 + m.b1 * b2 + m.c1 * b3 + m.d1 * b4,
+ m.a2 * b1 + m.b2 * b2 + m.c2 * b3 + m.d2 * b4,
+ m.a3 * b1 + m.b3 * b2 + m.c3 * b3 + m.d3 * b4,
+ m.a4 * b1 + m.b4 * b2 + m.c4 * b3 + m.d4 * b4,
+ m.a1 * c1 + m.b1 * c2 + m.c1 * c3 + m.d1 * c4,
+ m.a2 * c1 + m.b2 * c2 + m.c2 * c3 + m.d2 * c4,
+ m.a3 * c1 + m.b3 * c2 + m.c3 * c3 + m.d3 * c4,
+ m.a4 * c1 + m.b4 * c2 + m.c4 * c3 + m.d4 * c4,
+ m.a1 * d1 + m.b1 * d2 + m.c1 * d3 + m.d1 * d4,
+ m.a2 * d1 + m.b2 * d2 + m.c2 * d3 + m.d2 * d4,
+ m.a3 * d1 + m.b3 * d2 + m.c3 * d3 + m.d3 * d4,
+ m.a4 * d1 + m.b4 * d2 + m.c4 * d3 + m.d4 * d4);
+ return *this;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal> aiMatrix4x4t<TReal>::operator* (const TReal& aFloat) const
+{
+ aiMatrix4x4t<TReal> temp(
+ a1 * aFloat,
+ a2 * aFloat,
+ a3 * aFloat,
+ a4 * aFloat,
+ b1 * aFloat,
+ b2 * aFloat,
+ b3 * aFloat,
+ b4 * aFloat,
+ c1 * aFloat,
+ c2 * aFloat,
+ c3 * aFloat,
+ c4 * aFloat,
+ d1 * aFloat,
+ d2 * aFloat,
+ d3 * aFloat,
+ d4 * aFloat);
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal> aiMatrix4x4t<TReal>::operator+ (const aiMatrix4x4t<TReal>& m) const
+{
+ aiMatrix4x4t<TReal> temp(
+ m.a1 + a1,
+ m.a2 + a2,
+ m.a3 + a3,
+ m.a4 + a4,
+ m.b1 + b1,
+ m.b2 + b2,
+ m.b3 + b3,
+ m.b4 + b4,
+ m.c1 + c1,
+ m.c2 + c2,
+ m.c3 + c3,
+ m.c4 + c4,
+ m.d1 + d1,
+ m.d2 + d2,
+ m.d3 + d3,
+ m.d4 + d4);
+ return temp;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal> aiMatrix4x4t<TReal>::operator* (const aiMatrix4x4t<TReal>& m) const
+{
+ aiMatrix4x4t<TReal> temp( *this);
+ temp *= m;
+ return temp;
+}
+
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Transpose()
+{
+ // (TReal&) don't remove, GCC complains cause of packed fields
+ std::swap( (TReal&)b1, (TReal&)a2);
+ std::swap( (TReal&)c1, (TReal&)a3);
+ std::swap( (TReal&)c2, (TReal&)b3);
+ std::swap( (TReal&)d1, (TReal&)a4);
+ std::swap( (TReal&)d2, (TReal&)b4);
+ std::swap( (TReal&)d3, (TReal&)c4);
+ return *this;
+}
+
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline TReal aiMatrix4x4t<TReal>::Determinant() const
+{
+ return a1*b2*c3*d4 - a1*b2*c4*d3 + a1*b3*c4*d2 - a1*b3*c2*d4
+ + a1*b4*c2*d3 - a1*b4*c3*d2 - a2*b3*c4*d1 + a2*b3*c1*d4
+ - a2*b4*c1*d3 + a2*b4*c3*d1 - a2*b1*c3*d4 + a2*b1*c4*d3
+ + a3*b4*c1*d2 - a3*b4*c2*d1 + a3*b1*c2*d4 - a3*b1*c4*d2
+ + a3*b2*c4*d1 - a3*b2*c1*d4 - a4*b1*c2*d3 + a4*b1*c3*d2
+ - a4*b2*c3*d1 + a4*b2*c1*d3 - a4*b3*c1*d2 + a4*b3*c2*d1;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Inverse()
+{
+ // Compute the reciprocal determinant
+ const TReal det = Determinant();
+ if(det == static_cast<TReal>(0.0))
+ {
+ // Matrix not invertible. Setting all elements to nan is not really
+ // correct in a mathematical sense but it is easy to debug for the
+ // programmer.
+ const TReal nan = std::numeric_limits<TReal>::quiet_NaN();
+ *this = aiMatrix4x4t<TReal>(
+ nan,nan,nan,nan,
+ nan,nan,nan,nan,
+ nan,nan,nan,nan,
+ nan,nan,nan,nan);
+
+ return *this;
+ }
+
+ const TReal invdet = static_cast<TReal>(1.0) / det;
+
+ aiMatrix4x4t<TReal> res;
+ res.a1 = invdet * (b2 * (c3 * d4 - c4 * d3) + b3 * (c4 * d2 - c2 * d4) + b4 * (c2 * d3 - c3 * d2));
+ res.a2 = -invdet * (a2 * (c3 * d4 - c4 * d3) + a3 * (c4 * d2 - c2 * d4) + a4 * (c2 * d3 - c3 * d2));
+ res.a3 = invdet * (a2 * (b3 * d4 - b4 * d3) + a3 * (b4 * d2 - b2 * d4) + a4 * (b2 * d3 - b3 * d2));
+ res.a4 = -invdet * (a2 * (b3 * c4 - b4 * c3) + a3 * (b4 * c2 - b2 * c4) + a4 * (b2 * c3 - b3 * c2));
+ res.b1 = -invdet * (b1 * (c3 * d4 - c4 * d3) + b3 * (c4 * d1 - c1 * d4) + b4 * (c1 * d3 - c3 * d1));
+ res.b2 = invdet * (a1 * (c3 * d4 - c4 * d3) + a3 * (c4 * d1 - c1 * d4) + a4 * (c1 * d3 - c3 * d1));
+ res.b3 = -invdet * (a1 * (b3 * d4 - b4 * d3) + a3 * (b4 * d1 - b1 * d4) + a4 * (b1 * d3 - b3 * d1));
+ res.b4 = invdet * (a1 * (b3 * c4 - b4 * c3) + a3 * (b4 * c1 - b1 * c4) + a4 * (b1 * c3 - b3 * c1));
+ res.c1 = invdet * (b1 * (c2 * d4 - c4 * d2) + b2 * (c4 * d1 - c1 * d4) + b4 * (c1 * d2 - c2 * d1));
+ res.c2 = -invdet * (a1 * (c2 * d4 - c4 * d2) + a2 * (c4 * d1 - c1 * d4) + a4 * (c1 * d2 - c2 * d1));
+ res.c3 = invdet * (a1 * (b2 * d4 - b4 * d2) + a2 * (b4 * d1 - b1 * d4) + a4 * (b1 * d2 - b2 * d1));
+ res.c4 = -invdet * (a1 * (b2 * c4 - b4 * c2) + a2 * (b4 * c1 - b1 * c4) + a4 * (b1 * c2 - b2 * c1));
+ res.d1 = -invdet * (b1 * (c2 * d3 - c3 * d2) + b2 * (c3 * d1 - c1 * d3) + b3 * (c1 * d2 - c2 * d1));
+ res.d2 = invdet * (a1 * (c2 * d3 - c3 * d2) + a2 * (c3 * d1 - c1 * d3) + a3 * (c1 * d2 - c2 * d1));
+ res.d3 = -invdet * (a1 * (b2 * d3 - b3 * d2) + a2 * (b3 * d1 - b1 * d3) + a3 * (b1 * d2 - b2 * d1));
+ res.d4 = invdet * (a1 * (b2 * c3 - b3 * c2) + a2 * (b3 * c1 - b1 * c3) + a3 * (b1 * c2 - b2 * c1));
+ *this = res;
+
+ return *this;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline TReal* aiMatrix4x4t<TReal>::operator[](unsigned int p_iIndex) {
+ if (p_iIndex > 3) {
+ return NULL;
+ }
+ switch ( p_iIndex ) {
+ case 0:
+ return &a1;
+ case 1:
+ return &b1;
+ case 2:
+ return &c1;
+ case 3:
+ return &d1;
+ default:
+ break;
+ }
+ return &a1;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline const TReal* aiMatrix4x4t<TReal>::operator[](unsigned int p_iIndex) const {
+ if (p_iIndex > 3) {
+ return NULL;
+ }
+
+ switch ( p_iIndex ) {
+ case 0:
+ return &a1;
+ case 1:
+ return &b1;
+ case 2:
+ return &c1;
+ case 3:
+ return &d1;
+ default:
+ break;
+ }
+ return &a1;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline bool aiMatrix4x4t<TReal>::operator== (const aiMatrix4x4t<TReal>& m) const
+{
+ return (a1 == m.a1 && a2 == m.a2 && a3 == m.a3 && a4 == m.a4 &&
+ b1 == m.b1 && b2 == m.b2 && b3 == m.b3 && b4 == m.b4 &&
+ c1 == m.c1 && c2 == m.c2 && c3 == m.c3 && c4 == m.c4 &&
+ d1 == m.d1 && d2 == m.d2 && d3 == m.d3 && d4 == m.d4);
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline bool aiMatrix4x4t<TReal>::operator!= (const aiMatrix4x4t<TReal>& m) const
+{
+ return !(*this == m);
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline bool aiMatrix4x4t<TReal>::Equal(const aiMatrix4x4t<TReal>& m, TReal epsilon) const {
+ return
+ std::abs(a1 - m.a1) <= epsilon &&
+ std::abs(a2 - m.a2) <= epsilon &&
+ std::abs(a3 - m.a3) <= epsilon &&
+ std::abs(a4 - m.a4) <= epsilon &&
+ std::abs(b1 - m.b1) <= epsilon &&
+ std::abs(b2 - m.b2) <= epsilon &&
+ std::abs(b3 - m.b3) <= epsilon &&
+ std::abs(b4 - m.b4) <= epsilon &&
+ std::abs(c1 - m.c1) <= epsilon &&
+ std::abs(c2 - m.c2) <= epsilon &&
+ std::abs(c3 - m.c3) <= epsilon &&
+ std::abs(c4 - m.c4) <= epsilon &&
+ std::abs(d1 - m.d1) <= epsilon &&
+ std::abs(d2 - m.d2) <= epsilon &&
+ std::abs(d3 - m.d3) <= epsilon &&
+ std::abs(d4 - m.d4) <= epsilon;
+}
+
+// ----------------------------------------------------------------------------------------
+
+#define ASSIMP_MATRIX4_4_DECOMPOSE_PART \
+ const aiMatrix4x4t<TReal>& _this = *this;/* Create alias for conveniance. */ \
+ \
+ /* extract translation */ \
+ pPosition.x = _this[0][3]; \
+ pPosition.y = _this[1][3]; \
+ pPosition.z = _this[2][3]; \
+ \
+ /* extract the columns of the matrix. */ \
+ aiVector3t<TReal> vCols[3] = { \
+ aiVector3t<TReal>(_this[0][0],_this[1][0],_this[2][0]), \
+ aiVector3t<TReal>(_this[0][1],_this[1][1],_this[2][1]), \
+ aiVector3t<TReal>(_this[0][2],_this[1][2],_this[2][2]) \
+ }; \
+ \
+ /* extract the scaling factors */ \
+ pScaling.x = vCols[0].Length(); \
+ pScaling.y = vCols[1].Length(); \
+ pScaling.z = vCols[2].Length(); \
+ \
+ /* and the sign of the scaling */ \
+ if (Determinant() < 0) pScaling = -pScaling; \
+ \
+ /* and remove all scaling from the matrix */ \
+ if(pScaling.x) vCols[0] /= pScaling.x; \
+ if(pScaling.y) vCols[1] /= pScaling.y; \
+ if(pScaling.z) vCols[2] /= pScaling.z; \
+ \
+ do {} while(false)
+
+
+
+
+template <typename TReal>
+inline void aiMatrix4x4t<TReal>::Decompose (aiVector3t<TReal>& pScaling, aiQuaterniont<TReal>& pRotation,
+ aiVector3t<TReal>& pPosition) const
+{
+ ASSIMP_MATRIX4_4_DECOMPOSE_PART;
+
+ // build a 3x3 rotation matrix
+ aiMatrix3x3t<TReal> m(vCols[0].x,vCols[1].x,vCols[2].x,
+ vCols[0].y,vCols[1].y,vCols[2].y,
+ vCols[0].z,vCols[1].z,vCols[2].z);
+
+ // and generate the rotation quaternion from it
+ pRotation = aiQuaterniont<TReal>(m);
+}
+
+template <typename TReal>
+inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const
+{
+ ASSIMP_MATRIX4_4_DECOMPOSE_PART;
+
+ /*
+ assuming a right-handed coordinate system
+ and post-multiplication of column vectors,
+ the rotation matrix for an euler XYZ rotation is M = Rz * Ry * Rx.
+ combining gives:
+
+ | CE BDE-AF ADE+BF 0 |
+ M = | CF BDF+AE ADF-BE 0 |
+ | -D CB AC 0 |
+ | 0 0 0 1 |
+
+ where
+ A = cos(angle_x), B = sin(angle_x);
+ C = cos(angle_y), D = sin(angle_y);
+ E = cos(angle_z), F = sin(angle_z);
+ */
+
+ // Use a small epsilon to solve floating-point inaccuracies
+ const TReal epsilon = 10e-3f;
+
+ pRotation.y = std::asin(-vCols[0].z);// D. Angle around oY.
+
+ TReal C = std::cos(pRotation.y);
+
+ if(std::fabs(C) > epsilon)
+ {
+ // Finding angle around oX.
+ TReal tan_x = vCols[2].z / C;// A
+ TReal tan_y = vCols[1].z / C;// B
+
+ pRotation.x = std::atan2(tan_y, tan_x);
+ // Finding angle around oZ.
+ tan_x = vCols[0].x / C;// E
+ tan_y = vCols[0].y / C;// F
+ pRotation.z = std::atan2(tan_y, tan_x);
+ }
+ else
+ {// oY is fixed.
+ pRotation.x = 0;// Set angle around oX to 0. => A == 1, B == 0, C == 0, D == 1.
+
+ // And finding angle around oZ.
+ TReal tan_x = vCols[1].y;// BDF+AE => E
+ TReal tan_y = -vCols[1].x;// BDE-AF => F
+
+ pRotation.z = std::atan2(tan_y, tan_x);
+ }
+}
+
+#undef ASSIMP_MATRIX4_4_DECOMPOSE_PART
+
+template <typename TReal>
+inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotationAxis, TReal& pRotationAngle,
+ aiVector3t<TReal>& pPosition) const
+{
+aiQuaterniont<TReal> pRotation;
+
+ Decompose(pScaling, pRotation, pPosition);
+ pRotation.Normalize();
+
+ TReal angle_cos = pRotation.w;
+ TReal angle_sin = std::sqrt(1.0f - angle_cos * angle_cos);
+
+ pRotationAngle = std::acos(angle_cos) * 2;
+
+ // Use a small epsilon to solve floating-point inaccuracies
+ const TReal epsilon = 10e-3f;
+
+ if(std::fabs(angle_sin) < epsilon) angle_sin = 1;
+
+ pRotationAxis.x = pRotation.x / angle_sin;
+ pRotationAxis.y = pRotation.y / angle_sin;
+ pRotationAxis.z = pRotation.z / angle_sin;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline void aiMatrix4x4t<TReal>::DecomposeNoScaling (aiQuaterniont<TReal>& rotation,
+ aiVector3t<TReal>& position) const
+{
+ const aiMatrix4x4t<TReal>& _this = *this;
+
+ // extract translation
+ position.x = _this[0][3];
+ position.y = _this[1][3];
+ position.z = _this[2][3];
+
+ // extract rotation
+ rotation = aiQuaterniont<TReal>((aiMatrix3x3t<TReal>)_this);
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::FromEulerAnglesXYZ(const aiVector3t<TReal>& blubb)
+{
+ return FromEulerAnglesXYZ(blubb.x,blubb.y,blubb.z);
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::FromEulerAnglesXYZ(TReal x, TReal y, TReal z)
+{
+ aiMatrix4x4t<TReal>& _this = *this;
+
+ TReal cx = std::cos(x);
+ TReal sx = std::sin(x);
+ TReal cy = std::cos(y);
+ TReal sy = std::sin(y);
+ TReal cz = std::cos(z);
+ TReal sz = std::sin(z);
+
+ // mz*my*mx
+ _this.a1 = cz * cy;
+ _this.a2 = cz * sy * sx - sz * cx;
+ _this.a3 = sz * sx + cz * sy * cx;
+
+ _this.b1 = sz * cy;
+ _this.b2 = cz * cx + sz * sy * sx;
+ _this.b3 = sz * sy * cx - cz * sx;
+
+ _this.c1 = -sy;
+ _this.c2 = cy * sx;
+ _this.c3 = cy * cx;
+
+ return *this;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline bool aiMatrix4x4t<TReal>::IsIdentity() const
+{
+ // Use a small epsilon to solve floating-point inaccuracies
+ const static TReal epsilon = 10e-3f;
+
+ return (a2 <= epsilon && a2 >= -epsilon &&
+ a3 <= epsilon && a3 >= -epsilon &&
+ a4 <= epsilon && a4 >= -epsilon &&
+ b1 <= epsilon && b1 >= -epsilon &&
+ b3 <= epsilon && b3 >= -epsilon &&
+ b4 <= epsilon && b4 >= -epsilon &&
+ c1 <= epsilon && c1 >= -epsilon &&
+ c2 <= epsilon && c2 >= -epsilon &&
+ c4 <= epsilon && c4 >= -epsilon &&
+ d1 <= epsilon && d1 >= -epsilon &&
+ d2 <= epsilon && d2 >= -epsilon &&
+ d3 <= epsilon && d3 >= -epsilon &&
+ a1 <= 1.f+epsilon && a1 >= 1.f-epsilon &&
+ b2 <= 1.f+epsilon && b2 >= 1.f-epsilon &&
+ c3 <= 1.f+epsilon && c3 >= 1.f-epsilon &&
+ d4 <= 1.f+epsilon && d4 >= 1.f-epsilon);
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::RotationX(TReal a, aiMatrix4x4t<TReal>& out)
+{
+ /*
+ | 1 0 0 0 |
+ M = | 0 cos(A) -sin(A) 0 |
+ | 0 sin(A) cos(A) 0 |
+ | 0 0 0 1 | */
+ out = aiMatrix4x4t<TReal>();
+ out.b2 = out.c3 = std::cos(a);
+ out.b3 = -(out.c2 = std::sin(a));
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::RotationY(TReal a, aiMatrix4x4t<TReal>& out)
+{
+ /*
+ | cos(A) 0 sin(A) 0 |
+ M = | 0 1 0 0 |
+ | -sin(A) 0 cos(A) 0 |
+ | 0 0 0 1 |
+ */
+ out = aiMatrix4x4t<TReal>();
+ out.a1 = out.c3 = std::cos(a);
+ out.c1 = -(out.a3 = std::sin(a));
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::RotationZ(TReal a, aiMatrix4x4t<TReal>& out)
+{
+ /*
+ | cos(A) -sin(A) 0 0 |
+ M = | sin(A) cos(A) 0 0 |
+ | 0 0 1 0 |
+ | 0 0 0 1 | */
+ out = aiMatrix4x4t<TReal>();
+ out.a1 = out.b2 = std::cos(a);
+ out.a2 = -(out.b1 = std::sin(a));
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+// Returns a rotation matrix for a rotation around an arbitrary axis.
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Rotation( TReal a, const aiVector3t<TReal>& axis, aiMatrix4x4t<TReal>& out)
+{
+ TReal c = std::cos( a), s = std::sin( a), t = 1 - c;
+ TReal x = axis.x, y = axis.y, z = axis.z;
+
+ // Many thanks to MathWorld and Wikipedia
+ out.a1 = t*x*x + c; out.a2 = t*x*y - s*z; out.a3 = t*x*z + s*y;
+ out.b1 = t*x*y + s*z; out.b2 = t*y*y + c; out.b3 = t*y*z - s*x;
+ out.c1 = t*x*z - s*y; out.c2 = t*y*z + s*x; out.c3 = t*z*z + c;
+ out.a4 = out.b4 = out.c4 = static_cast<TReal>(0.0);
+ out.d1 = out.d2 = out.d3 = static_cast<TReal>(0.0);
+ out.d4 = static_cast<TReal>(1.0);
+
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Translation( const aiVector3t<TReal>& v, aiMatrix4x4t<TReal>& out)
+{
+ out = aiMatrix4x4t<TReal>();
+ out.a4 = v.x;
+ out.b4 = v.y;
+ out.c4 = v.z;
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::Scaling( const aiVector3t<TReal>& v, aiMatrix4x4t<TReal>& out)
+{
+ out = aiMatrix4x4t<TReal>();
+ out.a1 = v.x;
+ out.b2 = v.y;
+ out.c3 = v.z;
+ return out;
+}
+
+// ----------------------------------------------------------------------------------------
+/** A function for creating a rotation matrix that rotates a vector called
+ * "from" into another vector called "to".
+ * Input : from[3], to[3] which both must be *normalized* non-zero vectors
+ * Output: mtx[3][3] -- a 3x3 matrix in colum-major form
+ * Authors: Tomas Möller, John Hughes
+ * "Efficiently Building a Matrix to Rotate One Vector to Another"
+ * Journal of Graphics Tools, 4(4):1-4, 1999
+ */
+// ----------------------------------------------------------------------------------------
+template <typename TReal>
+inline aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::FromToMatrix(const aiVector3t<TReal>& from,
+ const aiVector3t<TReal>& to, aiMatrix4x4t<TReal>& mtx)
+{
+ aiMatrix3x3t<TReal> m3;
+ aiMatrix3x3t<TReal>::FromToMatrix(from,to,m3);
+ mtx = aiMatrix4x4t<TReal>(m3);
+ return mtx;
+}
+
+#endif // __cplusplus
+#endif // AI_MATRIX4X4_INL_INC
diff --git a/thirdparty/assimp/include/assimp/mesh.h b/thirdparty/assimp/include/assimp/mesh.h
new file mode 100644
index 0000000000..36f3ed2afd
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/mesh.h
@@ -0,0 +1,852 @@
+/*
+---------------------------------------------------------------------------
+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 mesh.h
+ * @brief Declares the data structures in which the imported geometry is
+ returned by ASSIMP: aiMesh, aiFace and aiBone data structures.
+ */
+#pragma once
+#ifndef AI_MESH_H_INC
+#define AI_MESH_H_INC
+
+#include "types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+// Limits. These values are required to match the settings Assimp was
+// compiled against. Therefore, do not redefine them unless you build the
+// library from source using the same definitions.
+// ---------------------------------------------------------------------------
+
+/** @def AI_MAX_FACE_INDICES
+ * Maximum number of indices per face (polygon). */
+
+#ifndef AI_MAX_FACE_INDICES
+# define AI_MAX_FACE_INDICES 0x7fff
+#endif
+
+/** @def AI_MAX_BONE_WEIGHTS
+ * Maximum number of indices per face (polygon). */
+
+#ifndef AI_MAX_BONE_WEIGHTS
+# define AI_MAX_BONE_WEIGHTS 0x7fffffff
+#endif
+
+/** @def AI_MAX_VERTICES
+ * Maximum number of vertices per mesh. */
+
+#ifndef AI_MAX_VERTICES
+# define AI_MAX_VERTICES 0x7fffffff
+#endif
+
+/** @def AI_MAX_FACES
+ * Maximum number of faces per mesh. */
+
+#ifndef AI_MAX_FACES
+# define AI_MAX_FACES 0x7fffffff
+#endif
+
+/** @def AI_MAX_NUMBER_OF_COLOR_SETS
+ * Supported number of vertex color sets per mesh. */
+
+#ifndef AI_MAX_NUMBER_OF_COLOR_SETS
+# define AI_MAX_NUMBER_OF_COLOR_SETS 0x8
+#endif // !! AI_MAX_NUMBER_OF_COLOR_SETS
+
+/** @def AI_MAX_NUMBER_OF_TEXTURECOORDS
+ * Supported number of texture coord sets (UV(W) channels) per mesh */
+
+#ifndef AI_MAX_NUMBER_OF_TEXTURECOORDS
+# define AI_MAX_NUMBER_OF_TEXTURECOORDS 0x8
+#endif // !! AI_MAX_NUMBER_OF_TEXTURECOORDS
+
+// ---------------------------------------------------------------------------
+/** @brief A single face in a mesh, referring to multiple vertices.
+ *
+ * If mNumIndices is 3, we call the face 'triangle', for mNumIndices > 3
+ * it's called 'polygon' (hey, that's just a definition!).
+ * <br>
+ * aiMesh::mPrimitiveTypes can be queried to quickly examine which types of
+ * primitive are actually present in a mesh. The #aiProcess_SortByPType flag
+ * executes a special post-processing algorithm which splits meshes with
+ * *different* primitive types mixed up (e.g. lines and triangles) in several
+ * 'clean' submeshes. Furthermore there is a configuration option (
+ * #AI_CONFIG_PP_SBP_REMOVE) to force #aiProcess_SortByPType to remove
+ * specific kinds of primitives from the imported scene, completely and forever.
+ * In many cases you'll probably want to set this setting to
+ * @code
+ * aiPrimitiveType_LINE|aiPrimitiveType_POINT
+ * @endcode
+ * Together with the #aiProcess_Triangulate flag you can then be sure that
+ * #aiFace::mNumIndices is always 3.
+ * @note Take a look at the @link data Data Structures page @endlink for
+ * more information on the layout and winding order of a face.
+ */
+struct aiFace
+{
+ //! Number of indices defining this face.
+ //! The maximum value for this member is #AI_MAX_FACE_INDICES.
+ unsigned int mNumIndices;
+
+ //! Pointer to the indices array. Size of the array is given in numIndices.
+ unsigned int* mIndices;
+
+#ifdef __cplusplus
+
+ //! Default constructor
+ aiFace() AI_NO_EXCEPT
+ : mNumIndices( 0 )
+ , mIndices( nullptr ) {
+ // empty
+ }
+
+ //! Default destructor. Delete the index array
+ ~aiFace()
+ {
+ delete [] mIndices;
+ }
+
+ //! Copy constructor. Copy the index array
+ aiFace( const aiFace& o)
+ : mNumIndices(0)
+ , mIndices( nullptr ) {
+ *this = o;
+ }
+
+ //! Assignment operator. Copy the index array
+ aiFace& operator = ( const aiFace& o) {
+ if (&o == this) {
+ return *this;
+ }
+
+ delete[] mIndices;
+ mNumIndices = o.mNumIndices;
+ if (mNumIndices) {
+ mIndices = new unsigned int[mNumIndices];
+ ::memcpy( mIndices, o.mIndices, mNumIndices * sizeof( unsigned int));
+ } else {
+ mIndices = nullptr;
+ }
+
+ return *this;
+ }
+
+ //! Comparison operator. Checks whether the index array
+ //! of two faces is identical
+ bool operator== (const aiFace& o) const {
+ if (mIndices == o.mIndices) {
+ return true;
+ }
+
+ if (nullptr != mIndices && mNumIndices != o.mNumIndices) {
+ return false;
+ }
+
+ if (nullptr == mIndices) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < this->mNumIndices; ++i) {
+ if (mIndices[i] != o.mIndices[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //! Inverse comparison operator. Checks whether the index
+ //! array of two faces is NOT identical
+ bool operator != (const aiFace& o) const {
+ return !(*this == o);
+ }
+#endif // __cplusplus
+}; // struct aiFace
+
+
+// ---------------------------------------------------------------------------
+/** @brief A single influence of a bone on a vertex.
+ */
+struct aiVertexWeight {
+ //! Index of the vertex which is influenced by the bone.
+ unsigned int mVertexId;
+
+ //! The strength of the influence in the range (0...1).
+ //! The influence from all bones at one vertex amounts to 1.
+ float mWeight;
+
+#ifdef __cplusplus
+
+ //! Default constructor
+ aiVertexWeight() AI_NO_EXCEPT
+ : mVertexId(0)
+ , mWeight(0.0f) {
+ // empty
+ }
+
+ //! Initialization from a given index and vertex weight factor
+ //! \param pID ID
+ //! \param pWeight Vertex weight factor
+ aiVertexWeight( unsigned int pID, float pWeight )
+ : mVertexId( pID )
+ , mWeight( pWeight ) {
+ // empty
+ }
+
+ bool operator == ( const aiVertexWeight &rhs ) const {
+ return ( mVertexId == rhs.mVertexId && mWeight == rhs.mWeight );
+ }
+
+ bool operator != ( const aiVertexWeight &rhs ) const {
+ return ( *this == rhs );
+ }
+
+#endif // __cplusplus
+};
+
+
+// ---------------------------------------------------------------------------
+/** @brief A single bone of a mesh.
+ *
+ * A bone has a name by which it can be found in the frame hierarchy and by
+ * which it can be addressed by animations. In addition it has a number of
+ * influences on vertices, and a matrix relating the mesh position to the
+ * position of the bone at the time of binding.
+ */
+struct aiBone {
+ //! The name of the bone.
+ C_STRUCT aiString mName;
+
+ //! The number of vertices affected by this bone.
+ //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS.
+ unsigned int mNumWeights;
+
+ //! The influence weights of this bone, by vertex index.
+ C_STRUCT aiVertexWeight* mWeights;
+
+ /** Matrix that transforms from bone space to mesh space in bind pose.
+ *
+ * This matrix describes the position of the mesh
+ * in the local space of this bone when the skeleton was bound.
+ * Thus it can be used directly to determine a desired vertex position,
+ * given the world-space transform of the bone when animated,
+ * and the position of the vertex in mesh space.
+ *
+ * It is sometimes called an inverse-bind matrix,
+ * or inverse bind pose matrix.
+ */
+ C_STRUCT aiMatrix4x4 mOffsetMatrix;
+
+#ifdef __cplusplus
+
+ //! Default constructor
+ aiBone() AI_NO_EXCEPT
+ : mName()
+ , mNumWeights( 0 )
+ , mWeights( nullptr )
+ , mOffsetMatrix() {
+ // empty
+ }
+
+ //! Copy constructor
+ aiBone(const aiBone& other)
+ : mName( other.mName )
+ , mNumWeights( other.mNumWeights )
+ , mWeights(nullptr)
+ , mOffsetMatrix( other.mOffsetMatrix ) {
+ if (other.mWeights && other.mNumWeights) {
+ mWeights = new aiVertexWeight[mNumWeights];
+ ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight));
+ }
+ }
+
+
+ //! Assignment operator
+ aiBone &operator=(const aiBone& other) {
+ if (this == &other) {
+ return *this;
+ }
+
+ mName = other.mName;
+ mNumWeights = other.mNumWeights;
+ mOffsetMatrix = other.mOffsetMatrix;
+
+ if (other.mWeights && other.mNumWeights)
+ {
+ if (mWeights) {
+ delete[] mWeights;
+ }
+
+ mWeights = new aiVertexWeight[mNumWeights];
+ ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight));
+ }
+
+ return *this;
+ }
+
+ bool operator == ( const aiBone &rhs ) const {
+ if ( mName != rhs.mName || mNumWeights != rhs.mNumWeights ) {
+ return false;
+ }
+
+ for ( size_t i = 0; i < mNumWeights; ++i ) {
+ if ( mWeights[ i ] != rhs.mWeights[ i ] ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ //! Destructor - deletes the array of vertex weights
+ ~aiBone() {
+ delete [] mWeights;
+ }
+#endif // __cplusplus
+};
+
+
+// ---------------------------------------------------------------------------
+/** @brief Enumerates the types of geometric primitives supported by Assimp.
+ *
+ * @see aiFace Face data structure
+ * @see aiProcess_SortByPType Per-primitive sorting of meshes
+ * @see aiProcess_Triangulate Automatic triangulation
+ * @see AI_CONFIG_PP_SBP_REMOVE Removal of specific primitive types.
+ */
+enum aiPrimitiveType
+{
+ /** A point primitive.
+ *
+ * This is just a single vertex in the virtual world,
+ * #aiFace contains just one index for such a primitive.
+ */
+ aiPrimitiveType_POINT = 0x1,
+
+ /** A line primitive.
+ *
+ * This is a line defined through a start and an end position.
+ * #aiFace contains exactly two indices for such a primitive.
+ */
+ aiPrimitiveType_LINE = 0x2,
+
+ /** A triangular primitive.
+ *
+ * A triangle consists of three indices.
+ */
+ aiPrimitiveType_TRIANGLE = 0x4,
+
+ /** A higher-level polygon with more than 3 edges.
+ *
+ * A triangle is a polygon, but polygon in this context means
+ * "all polygons that are not triangles". The "Triangulate"-Step
+ * is provided for your convenience, it splits all polygons in
+ * triangles (which are much easier to handle).
+ */
+ aiPrimitiveType_POLYGON = 0x8,
+
+
+ /** This value is not used. It is just here to force the
+ * compiler to map this enum to a 32 Bit integer.
+ */
+#ifndef SWIG
+ _aiPrimitiveType_Force32Bit = INT_MAX
+#endif
+}; //! enum aiPrimitiveType
+
+// Get the #aiPrimitiveType flag for a specific number of face indices
+#define AI_PRIMITIVE_TYPE_FOR_N_INDICES(n) \
+ ((n) > 3 ? aiPrimitiveType_POLYGON : (aiPrimitiveType)(1u << ((n)-1)))
+
+
+
+// ---------------------------------------------------------------------------
+/** @brief An AnimMesh is an attachment to an #aiMesh stores per-vertex
+ * animations for a particular frame.
+ *
+ * You may think of an #aiAnimMesh as a `patch` for the host mesh, which
+ * replaces only certain vertex data streams at a particular time.
+ * Each mesh stores n attached attached meshes (#aiMesh::mAnimMeshes).
+ * The actual relationship between the time line and anim meshes is
+ * established by #aiMeshAnim, which references singular mesh attachments
+ * by their ID and binds them to a time offset.
+*/
+struct aiAnimMesh
+{
+ /**Anim Mesh name */
+ C_STRUCT aiString mName;
+
+ /** Replacement for aiMesh::mVertices. If this array is non-NULL,
+ * it *must* contain mNumVertices entries. The corresponding
+ * array in the host mesh must be non-NULL as well - animation
+ * meshes may neither add or nor remove vertex components (if
+ * a replacement array is NULL and the corresponding source
+ * array is not, the source data is taken instead)*/
+ C_STRUCT aiVector3D* mVertices;
+
+ /** Replacement for aiMesh::mNormals. */
+ C_STRUCT aiVector3D* mNormals;
+
+ /** Replacement for aiMesh::mTangents. */
+ C_STRUCT aiVector3D* mTangents;
+
+ /** Replacement for aiMesh::mBitangents. */
+ C_STRUCT aiVector3D* mBitangents;
+
+ /** Replacement for aiMesh::mColors */
+ C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ /** Replacement for aiMesh::mTextureCoords */
+ C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ /** The number of vertices in the aiAnimMesh, and thus the length of all
+ * the member arrays.
+ *
+ * This has always the same value as the mNumVertices property in the
+ * corresponding aiMesh. It is duplicated here merely to make the length
+ * of the member arrays accessible even if the aiMesh is not known, e.g.
+ * from language bindings.
+ */
+ unsigned int mNumVertices;
+
+ /**
+ * Weight of the AnimMesh.
+ */
+ float mWeight;
+
+#ifdef __cplusplus
+
+ aiAnimMesh() AI_NO_EXCEPT
+ : mVertices( nullptr )
+ , mNormals(nullptr)
+ , mTangents(nullptr)
+ , mBitangents(nullptr)
+ , mColors()
+ , mTextureCoords()
+ , mNumVertices( 0 )
+ , mWeight( 0.0f )
+ {
+ // fixme consider moving this to the ctor initializer list as well
+ for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++){
+ mTextureCoords[a] = nullptr;
+ }
+ for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) {
+ mColors[a] = nullptr;
+ }
+ }
+
+ ~aiAnimMesh()
+ {
+ delete [] mVertices;
+ delete [] mNormals;
+ delete [] mTangents;
+ delete [] mBitangents;
+ for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) {
+ delete [] mTextureCoords[a];
+ }
+ for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) {
+ delete [] mColors[a];
+ }
+ }
+
+ /** Check whether the anim mesh overrides the vertex positions
+ * of its host mesh*/
+ bool HasPositions() const {
+ return mVertices != nullptr;
+ }
+
+ /** Check whether the anim mesh overrides the vertex normals
+ * of its host mesh*/
+ bool HasNormals() const {
+ return mNormals != nullptr;
+ }
+
+ /** Check whether the anim mesh overrides the vertex tangents
+ * and bitangents of its host mesh. As for aiMesh,
+ * tangents and bitangents always go together. */
+ bool HasTangentsAndBitangents() const {
+ return mTangents != nullptr;
+ }
+
+ /** Check whether the anim mesh overrides a particular
+ * set of vertex colors on his host mesh.
+ * @param pIndex 0<index<AI_MAX_NUMBER_OF_COLOR_SETS */
+ bool HasVertexColors( unsigned int pIndex) const {
+ return pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS ? false : mColors[pIndex] != nullptr;
+ }
+
+ /** Check whether the anim mesh overrides a particular
+ * set of texture coordinates on his host mesh.
+ * @param pIndex 0<index<AI_MAX_NUMBER_OF_TEXTURECOORDS */
+ bool HasTextureCoords( unsigned int pIndex) const {
+ return pIndex >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? false : mTextureCoords[pIndex] != nullptr;
+ }
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Enumerates the methods of mesh morphing supported by Assimp.
+ */
+enum aiMorphingMethod
+{
+ /** Interpolation between morph targets */
+ aiMorphingMethod_VERTEX_BLEND = 0x1,
+
+ /** Normalized morphing between morph targets */
+ aiMorphingMethod_MORPH_NORMALIZED = 0x2,
+
+ /** Relative morphing between morph targets */
+ aiMorphingMethod_MORPH_RELATIVE = 0x3,
+
+ /** This value is not used. It is just here to force the
+ * compiler to map this enum to a 32 Bit integer.
+ */
+#ifndef SWIG
+ _aiMorphingMethod_Force32Bit = INT_MAX
+#endif
+}; //! enum aiMorphingMethod
+
+// ---------------------------------------------------------------------------
+/** @brief A mesh represents a geometry or model with a single material.
+*
+* It usually consists of a number of vertices and a series of primitives/faces
+* referencing the vertices. In addition there might be a series of bones, each
+* of them addressing a number of vertices with a certain weight. Vertex data
+* is presented in channels with each channel containing a single per-vertex
+* information such as a set of texture coords or a normal vector.
+* If a data pointer is non-null, the corresponding data stream is present.
+* From C++-programs you can also use the comfort functions Has*() to
+* test for the presence of various data streams.
+*
+* A Mesh uses only a single material which is referenced by a material ID.
+* @note The mPositions member is usually not optional. However, vertex positions
+* *could* be missing if the #AI_SCENE_FLAGS_INCOMPLETE flag is set in
+* @code
+* aiScene::mFlags
+* @endcode
+*/
+struct aiMesh
+{
+ /** Bitwise combination of the members of the #aiPrimitiveType enum.
+ * This specifies which types of primitives are present in the mesh.
+ * The "SortByPrimitiveType"-Step can be used to make sure the
+ * output meshes consist of one primitive type each.
+ */
+ unsigned int mPrimitiveTypes;
+
+ /** The number of vertices in this mesh.
+ * This is also the size of all of the per-vertex data arrays.
+ * The maximum value for this member is #AI_MAX_VERTICES.
+ */
+ unsigned int mNumVertices;
+
+ /** The number of primitives (triangles, polygons, lines) in this mesh.
+ * This is also the size of the mFaces array.
+ * The maximum value for this member is #AI_MAX_FACES.
+ */
+ unsigned int mNumFaces;
+
+ /** Vertex positions.
+ * This array is always present in a mesh. The array is
+ * mNumVertices in size.
+ */
+ C_STRUCT aiVector3D* mVertices;
+
+ /** Vertex normals.
+ * The array contains normalized vectors, NULL if not present.
+ * The array is mNumVertices in size. Normals are undefined for
+ * point and line primitives. A mesh consisting of points and
+ * lines only may not have normal vectors. Meshes with mixed
+ * primitive types (i.e. lines and triangles) may have normals,
+ * but the normals for vertices that are only referenced by
+ * point or line primitives are undefined and set to QNaN (WARN:
+ * qNaN compares to inequal to *everything*, even to qNaN itself.
+ * Using code like this to check whether a field is qnan is:
+ * @code
+ * #define IS_QNAN(f) (f != f)
+ * @endcode
+ * still dangerous because even 1.f == 1.f could evaluate to false! (
+ * remember the subtleties of IEEE754 artithmetics). Use stuff like
+ * @c fpclassify instead.
+ * @note Normal vectors computed by Assimp are always unit-length.
+ * However, this needn't apply for normals that have been taken
+ * directly from the model file.
+ */
+ C_STRUCT aiVector3D* mNormals;
+
+ /** Vertex tangents.
+ * The tangent of a vertex points in the direction of the positive
+ * X texture axis. The array contains normalized vectors, NULL if
+ * not present. The array is mNumVertices in size. A mesh consisting
+ * of points and lines only may not have normal vectors. Meshes with
+ * mixed primitive types (i.e. lines and triangles) may have
+ * normals, but the normals for vertices that are only referenced by
+ * point or line primitives are undefined and set to qNaN. See
+ * the #mNormals member for a detailed discussion of qNaNs.
+ * @note If the mesh contains tangents, it automatically also
+ * contains bitangents.
+ */
+ C_STRUCT aiVector3D* mTangents;
+
+ /** Vertex bitangents.
+ * The bitangent of a vertex points in the direction of the positive
+ * Y texture axis. The array contains normalized vectors, NULL if not
+ * present. The array is mNumVertices in size.
+ * @note If the mesh contains tangents, it automatically also contains
+ * bitangents.
+ */
+ C_STRUCT aiVector3D* mBitangents;
+
+ /** Vertex color sets.
+ * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex
+ * colors per vertex. NULL if not present. Each array is
+ * mNumVertices in size if present.
+ */
+ C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ /** Vertex texture coords, also known as UV channels.
+ * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per
+ * vertex. NULL if not present. The array is mNumVertices in size.
+ */
+ C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ /** Specifies the number of components for a given UV channel.
+ * Up to three channels are supported (UVW, for accessing volume
+ * or cube maps). If the value is 2 for a given channel n, the
+ * component p.z of mTextureCoords[n][p] is set to 0.0f.
+ * If the value is 1 for a given channel, p.y is set to 0.0f, too.
+ * @note 4D coords are not supported
+ */
+ unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ /** The faces the mesh is constructed from.
+ * Each face refers to a number of vertices by their indices.
+ * This array is always present in a mesh, its size is given
+ * in mNumFaces. If the #AI_SCENE_FLAGS_NON_VERBOSE_FORMAT
+ * is NOT set each face references an unique set of vertices.
+ */
+ C_STRUCT aiFace* mFaces;
+
+ /** The number of bones this mesh contains.
+ * Can be 0, in which case the mBones array is NULL.
+ */
+ unsigned int mNumBones;
+
+ /** The bones of this mesh.
+ * A bone consists of a name by which it can be found in the
+ * frame hierarchy and a set of vertex weights.
+ */
+ C_STRUCT aiBone** mBones;
+
+ /** The material used by this mesh.
+ * A mesh uses only a single material. If an imported model uses
+ * multiple materials, the import splits up the mesh. Use this value
+ * as index into the scene's material list.
+ */
+ unsigned int mMaterialIndex;
+
+ /** Name of the mesh. Meshes can be named, but this is not a
+ * requirement and leaving this field empty is totally fine.
+ * There are mainly three uses for mesh names:
+ * - some formats name nodes and meshes independently.
+ * - importers tend to split meshes up to meet the
+ * one-material-per-mesh requirement. Assigning
+ * the same (dummy) name to each of the result meshes
+ * aids the caller at recovering the original mesh
+ * partitioning.
+ * - Vertex animations refer to meshes by their names.
+ **/
+ C_STRUCT aiString mName;
+
+
+ /** The number of attachment meshes. Note! Currently only works with Collada loader. */
+ unsigned int mNumAnimMeshes;
+
+ /** Attachment meshes for this mesh, for vertex-based animation.
+ * Attachment meshes carry replacement data for some of the
+ * mesh'es vertex components (usually positions, normals).
+ * Note! Currently only works with Collada loader.*/
+ C_STRUCT aiAnimMesh** mAnimMeshes;
+
+ /**
+ * Method of morphing when animeshes are specified.
+ */
+ unsigned int mMethod;
+
+#ifdef __cplusplus
+
+ //! Default constructor. Initializes all members to 0
+ aiMesh() AI_NO_EXCEPT
+ : mPrimitiveTypes( 0 )
+ , mNumVertices( 0 )
+ , mNumFaces( 0 )
+ , mVertices( nullptr )
+ , mNormals(nullptr)
+ , mTangents(nullptr)
+ , mBitangents(nullptr)
+ , mColors()
+ , mTextureCoords()
+ , mNumUVComponents()
+ , mFaces(nullptr)
+ , mNumBones( 0 )
+ , mBones(nullptr)
+ , mMaterialIndex( 0 )
+ , mNumAnimMeshes( 0 )
+ , mAnimMeshes(nullptr)
+ , mMethod( 0 ) {
+ for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) {
+ mNumUVComponents[a] = 0;
+ mTextureCoords[a] = nullptr;
+ }
+
+ for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+ mColors[a] = nullptr;
+ }
+ }
+
+ //! Deletes all storage allocated for the mesh
+ ~aiMesh() {
+ delete [] mVertices;
+ delete [] mNormals;
+ delete [] mTangents;
+ delete [] mBitangents;
+ for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) {
+ delete [] mTextureCoords[a];
+ }
+ for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) {
+ delete [] mColors[a];
+ }
+
+ // DO NOT REMOVE THIS ADDITIONAL CHECK
+ if (mNumBones && mBones) {
+ for( unsigned int a = 0; a < mNumBones; a++) {
+ delete mBones[a];
+ }
+ delete [] mBones;
+ }
+
+ if (mNumAnimMeshes && mAnimMeshes) {
+ for( unsigned int a = 0; a < mNumAnimMeshes; a++) {
+ delete mAnimMeshes[a];
+ }
+ delete [] mAnimMeshes;
+ }
+
+ delete [] mFaces;
+ }
+
+ //! Check whether the mesh contains positions. Provided no special
+ //! scene flags are set, this will always be true
+ bool HasPositions() const
+ { return mVertices != nullptr && mNumVertices > 0; }
+
+ //! Check whether the mesh contains faces. If no special scene flags
+ //! are set this should always return true
+ bool HasFaces() const
+ { return mFaces != nullptr && mNumFaces > 0; }
+
+ //! Check whether the mesh contains normal vectors
+ bool HasNormals() const
+ { return mNormals != nullptr && mNumVertices > 0; }
+
+ //! Check whether the mesh contains tangent and bitangent vectors
+ //! It is not possible that it contains tangents and no bitangents
+ //! (or the other way round). The existence of one of them
+ //! implies that the second is there, too.
+ bool HasTangentsAndBitangents() const
+ { return mTangents != nullptr && mBitangents != nullptr && mNumVertices > 0; }
+
+ //! Check whether the mesh contains a vertex color set
+ //! \param pIndex Index of the vertex color set
+ bool HasVertexColors( unsigned int pIndex) const {
+ if (pIndex >= AI_MAX_NUMBER_OF_COLOR_SETS) {
+ return false;
+ } else {
+ return mColors[pIndex] != nullptr && mNumVertices > 0;
+ }
+ }
+
+ //! Check whether the mesh contains a texture coordinate set
+ //! \param pIndex Index of the texture coordinates set
+ bool HasTextureCoords( unsigned int pIndex) const {
+ if (pIndex >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+ return false;
+ } else {
+ return mTextureCoords[pIndex] != nullptr && mNumVertices > 0;
+ }
+ }
+
+ //! Get the number of UV channels the mesh contains
+ unsigned int GetNumUVChannels() const {
+ unsigned int n( 0 );
+ while (n < AI_MAX_NUMBER_OF_TEXTURECOORDS && mTextureCoords[n]) {
+ ++n;
+ }
+
+ return n;
+ }
+
+ //! Get the number of vertex color channels the mesh contains
+ unsigned int GetNumColorChannels() const {
+ unsigned int n(0);
+ while (n < AI_MAX_NUMBER_OF_COLOR_SETS && mColors[n]) {
+ ++n;
+ }
+ return n;
+ }
+
+ //! Check whether the mesh contains bones
+ bool HasBones() const {
+ return mBones != nullptr && mNumBones > 0;
+ }
+
+#endif // __cplusplus
+};
+
+#ifdef __cplusplus
+}
+#endif //! extern "C"
+#endif // AI_MESH_H_INC
+
diff --git a/thirdparty/assimp/include/assimp/metadata.h b/thirdparty/assimp/include/assimp/metadata.h
new file mode 100644
index 0000000000..3a1dd1442a
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/metadata.h
@@ -0,0 +1,380 @@
+/*
+---------------------------------------------------------------------------
+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 metadata.h
+ * @brief Defines the data structures for holding node meta information.
+ */
+#pragma once
+#ifndef AI_METADATA_H_INC
+#define AI_METADATA_H_INC
+
+#if defined(_MSC_VER) && (_MSC_VER <= 1500)
+# include "Compiler/pstdint.h"
+#else
+# include <stdint.h>
+#endif
+
+// -------------------------------------------------------------------------------
+/**
+ * Enum used to distinguish data types
+ */
+ // -------------------------------------------------------------------------------
+typedef enum aiMetadataType {
+ AI_BOOL = 0,
+ AI_INT32 = 1,
+ AI_UINT64 = 2,
+ AI_FLOAT = 3,
+ AI_DOUBLE = 4,
+ AI_AISTRING = 5,
+ AI_AIVECTOR3D = 6,
+ AI_META_MAX = 7,
+
+#ifndef SWIG
+ FORCE_32BIT = INT_MAX
+#endif
+} aiMetadataType;
+
+// -------------------------------------------------------------------------------
+/**
+ * Metadata entry
+ *
+ * The type field uniquely identifies the underlying type of the data field
+ */
+ // -------------------------------------------------------------------------------
+struct aiMetadataEntry {
+ aiMetadataType mType;
+ void* mData;
+};
+
+#ifdef __cplusplus
+
+#include <string>
+
+// -------------------------------------------------------------------------------
+/**
+ * Helper functions to get the aiType enum entry for a type
+ */
+ // -------------------------------------------------------------------------------
+
+inline aiMetadataType GetAiType( bool ) { return AI_BOOL; }
+inline aiMetadataType GetAiType( int32_t ) { return AI_INT32; }
+inline aiMetadataType GetAiType( uint64_t ) { return AI_UINT64; }
+inline aiMetadataType GetAiType( float ) { return AI_FLOAT; }
+inline aiMetadataType GetAiType( double ) { return AI_DOUBLE; }
+inline aiMetadataType GetAiType( const aiString & ) { return AI_AISTRING; }
+inline aiMetadataType GetAiType( const aiVector3D & ) { return AI_AIVECTOR3D; }
+
+#endif // __cplusplus
+
+// -------------------------------------------------------------------------------
+/**
+ * Container for holding metadata.
+ *
+ * Metadata is a key-value store using string keys and values.
+ */
+ // -------------------------------------------------------------------------------
+struct aiMetadata {
+ /** Length of the mKeys and mValues arrays, respectively */
+ unsigned int mNumProperties;
+
+ /** Arrays of keys, may not be NULL. Entries in this array may not be NULL as well. */
+ C_STRUCT aiString* mKeys;
+
+ /** Arrays of values, may not be NULL. Entries in this array may be NULL if the
+ * corresponding property key has no assigned value. */
+ C_STRUCT aiMetadataEntry* mValues;
+
+#ifdef __cplusplus
+
+ /**
+ * @brief The default constructor, set all members to zero by default.
+ */
+ aiMetadata() AI_NO_EXCEPT
+ : mNumProperties(0)
+ , mKeys(nullptr)
+ , mValues(nullptr) {
+ // empty
+ }
+
+ aiMetadata( const aiMetadata &rhs )
+ : mNumProperties( rhs.mNumProperties )
+ , mKeys( nullptr )
+ , mValues( nullptr ) {
+ mKeys = new aiString[ mNumProperties ];
+ for ( size_t i = 0; i < static_cast<size_t>( mNumProperties ); ++i ) {
+ mKeys[ i ] = rhs.mKeys[ i ];
+ }
+ mValues = new aiMetadataEntry[ mNumProperties ];
+ for ( size_t i = 0; i < static_cast<size_t>(mNumProperties); ++i ) {
+ mValues[ i ].mType = rhs.mValues[ i ].mType;
+ switch ( rhs.mValues[ i ].mType ) {
+ case AI_BOOL:
+ mValues[ i ].mData = new bool;
+ ::memcpy( mValues[ i ].mData, rhs.mValues[ i ].mData, sizeof(bool) );
+ break;
+ case AI_INT32: {
+ int32_t v;
+ ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( int32_t ) );
+ mValues[ i ].mData = new int32_t( v );
+ }
+ break;
+ case AI_UINT64: {
+ uint64_t v;
+ ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( uint64_t ) );
+ mValues[ i ].mData = new uint64_t( v );
+ }
+ break;
+ case AI_FLOAT: {
+ float v;
+ ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( float ) );
+ mValues[ i ].mData = new float( v );
+ }
+ break;
+ case AI_DOUBLE: {
+ double v;
+ ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( double ) );
+ mValues[ i ].mData = new double( v );
+ }
+ break;
+ case AI_AISTRING: {
+ aiString v;
+ rhs.Get<aiString>( mKeys[ i ], v );
+ mValues[ i ].mData = new aiString( v );
+ }
+ break;
+ case AI_AIVECTOR3D: {
+ aiVector3D v;
+ rhs.Get<aiVector3D>( mKeys[ i ], v );
+ mValues[ i ].mData = new aiVector3D( v );
+ }
+ break;
+#ifndef SWIG
+ case FORCE_32BIT:
+#endif
+ default:
+ break;
+ }
+
+ }
+ }
+
+ /**
+ * @brief The destructor.
+ */
+ ~aiMetadata() {
+ delete [] mKeys;
+ mKeys = nullptr;
+ if (mValues) {
+ // Delete each metadata entry
+ for (unsigned i=0; i<mNumProperties; ++i) {
+ void* data = mValues[i].mData;
+ switch (mValues[i].mType) {
+ case AI_BOOL:
+ delete static_cast< bool* >( data );
+ break;
+ case AI_INT32:
+ delete static_cast< int32_t* >( data );
+ break;
+ case AI_UINT64:
+ delete static_cast< uint64_t* >( data );
+ break;
+ case AI_FLOAT:
+ delete static_cast< float* >( data );
+ break;
+ case AI_DOUBLE:
+ delete static_cast< double* >( data );
+ break;
+ case AI_AISTRING:
+ delete static_cast< aiString* >( data );
+ break;
+ case AI_AIVECTOR3D:
+ delete static_cast< aiVector3D* >( data );
+ break;
+#ifndef SWIG
+ case FORCE_32BIT:
+#endif
+ default:
+ break;
+ }
+ }
+
+ // Delete the metadata array
+ delete [] mValues;
+ mValues = nullptr;
+ }
+ }
+
+ /**
+ * @brief Allocates property fields + keys.
+ * @param numProperties Number of requested properties.
+ */
+ static inline
+ aiMetadata *Alloc( unsigned int numProperties ) {
+ if ( 0 == numProperties ) {
+ return nullptr;
+ }
+
+ aiMetadata *data = new aiMetadata;
+ data->mNumProperties = numProperties;
+ data->mKeys = new aiString[ data->mNumProperties ]();
+ data->mValues = new aiMetadataEntry[ data->mNumProperties ]();
+
+ return data;
+ }
+
+ /**
+ * @brief Deallocates property fields + keys.
+ */
+ static inline
+ void Dealloc( aiMetadata *metadata ) {
+ delete metadata;
+ }
+
+ template<typename T>
+ inline
+ void Add(const std::string& key, const T& value) {
+ aiString* new_keys = new aiString[mNumProperties + 1];
+ aiMetadataEntry* new_values = new aiMetadataEntry[mNumProperties + 1];
+
+ for(unsigned int i = 0; i < mNumProperties; ++i)
+ {
+ new_keys[i] = mKeys[i];
+ new_values[i] = mValues[i];
+ }
+
+ delete mKeys;
+ delete mValues;
+
+ mKeys = new_keys;
+ mValues = new_values;
+
+ mNumProperties++;
+
+ Set(mNumProperties - 1, key, value);
+ }
+
+ template<typename T>
+ inline
+ bool Set( unsigned index, const std::string& key, const T& value ) {
+ // In range assertion
+ if ( index >= mNumProperties ) {
+ return false;
+ }
+
+ // Ensure that we have a valid key.
+ if ( key.empty() ) {
+ return false;
+ }
+
+ // Set metadata key
+ mKeys[index] = key;
+
+ // Set metadata type
+ mValues[index].mType = GetAiType(value);
+ // Copy the given value to the dynamic storage
+ mValues[index].mData = new T(value);
+
+ return true;
+ }
+
+ template<typename T>
+ inline
+ bool Get( unsigned index, T& value ) const {
+ // In range assertion
+ if ( index >= mNumProperties ) {
+ return false;
+ }
+
+ // Return false if the output data type does
+ // not match the found value's data type
+ if ( GetAiType( value ) != mValues[ index ].mType ) {
+ return false;
+ }
+
+ // Otherwise, output the found value and
+ // return true
+ value = *static_cast<T*>(mValues[index].mData);
+
+ return true;
+ }
+
+ template<typename T>
+ inline
+ bool Get( const aiString& key, T& value ) const {
+ // Search for the given key
+ for ( unsigned int i = 0; i < mNumProperties; ++i ) {
+ if ( mKeys[ i ] == key ) {
+ return Get( i, value );
+ }
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline
+ bool Get( const std::string& key, T& value ) const {
+ return Get(aiString(key), value);
+ }
+
+ /// Return metadata entry for analyzing it by user.
+ /// \param [in] pIndex - index of the entry.
+ /// \param [out] pKey - pointer to the key value.
+ /// \param [out] pEntry - pointer to the entry: type and value.
+ /// \return false - if pIndex is out of range, else - true.
+ inline
+ bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) const {
+ if ( index >= mNumProperties ) {
+ return false;
+ }
+
+ key = &mKeys[index];
+ entry = &mValues[index];
+
+ return true;
+ }
+
+#endif // __cplusplus
+
+};
+
+#endif // AI_METADATA_H_INC
diff --git a/thirdparty/assimp/include/assimp/pbrmaterial.h b/thirdparty/assimp/include/assimp/pbrmaterial.h
new file mode 100644
index 0000000000..ce5f822173
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/pbrmaterial.h
@@ -0,0 +1,77 @@
+/*
+---------------------------------------------------------------------------
+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 pbrmaterial.h
+ * @brief Defines the material system of the library
+ */
+#ifndef AI_PBRMATERIAL_H_INC
+#define AI_PBRMATERIAL_H_INC
+
+#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0
+#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0
+#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0
+#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1
+#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0
+#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0
+#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0
+#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0
+#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0
+#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0
+
+#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord"
+#define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname"
+#define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid"
+#define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag"
+#define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin"
+#define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale"
+#define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength"
+
+#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N
+#define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N
+#define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N
+#define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N
+#define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N
+#define AI_MATKEY_GLTF_TEXTURE_SCALE(type, N) _AI_MATKEY_GLTF_SCALE_BASE, type, N
+#define AI_MATKEY_GLTF_TEXTURE_STRENGTH(type, N) _AI_MATKEY_GLTF_STRENGTH_BASE, type, N
+
+#endif //!!AI_PBRMATERIAL_H_INC
diff --git a/thirdparty/assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h b/thirdparty/assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h
new file mode 100644
index 0000000000..41d8004877
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/port/AndroidJNI/AndroidJNIIOSystem.h
@@ -0,0 +1,92 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2016, 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 Android implementation of IOSystem using the standard C file functions.
+ * Aimed to ease the access to android assets */
+
+#if __ANDROID__ and __ANDROID_API__ > 9 and defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
+#ifndef AI_ANDROIDJNIIOSYSTEM_H_INC
+#define AI_ANDROIDJNIIOSYSTEM_H_INC
+
+#include <assimp/DefaultIOSystem.h>
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+#include <android/native_activity.h>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Android extension to DefaultIOSystem using the standard C file functions */
+class ASSIMP_API AndroidJNIIOSystem : public DefaultIOSystem
+{
+public:
+
+ /** Initialize android activity data */
+ std::string mApkWorkspacePath;
+ AAssetManager* mApkAssetManager;
+
+ /** Constructor. */
+ AndroidJNIIOSystem(ANativeActivity* activity);
+
+ /** Destructor. */
+ ~AndroidJNIIOSystem();
+
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists( const char* pFile) const;
+
+ // -------------------------------------------------------------------
+ /** Opens a file at the given path, with given mode */
+ IOStream* Open( const char* strFile, const char* strMode);
+
+ // ------------------------------------------------------------------------------------------------
+ // Inits Android extractor
+ void AndroidActivityInit(ANativeActivity* activity);
+
+ // ------------------------------------------------------------------------------------------------
+ // Extracts android asset
+ bool AndroidExtractAsset(std::string name);
+
+};
+
+} //!ns Assimp
+
+#endif //AI_ANDROIDJNIIOSYSTEM_H_INC
+#endif //__ANDROID__ and __ANDROID_API__ > 9 and defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
diff --git a/thirdparty/assimp/include/assimp/postprocess.h b/thirdparty/assimp/include/assimp/postprocess.h
new file mode 100644
index 0000000000..c23a5490a5
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/postprocess.h
@@ -0,0 +1,679 @@
+/*
+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 postprocess.h
+ * @brief Definitions for import post processing steps
+ */
+#pragma once
+#ifndef AI_POSTPROCESS_H_INC
+#define AI_POSTPROCESS_H_INC
+
+#include "types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// -----------------------------------------------------------------------------------
+/** @enum aiPostProcessSteps
+ * @brief Defines the flags for all possible post processing steps.
+ *
+ * @note Some steps are influenced by properties set on the Assimp::Importer itself
+ *
+ * @see Assimp::Importer::ReadFile()
+ * @see Assimp::Importer::SetPropertyInteger()
+ * @see aiImportFile
+ * @see aiImportFileEx
+ */
+// -----------------------------------------------------------------------------------
+enum aiPostProcessSteps
+{
+
+ // -------------------------------------------------------------------------
+ /** <hr>Calculates the tangents and bitangents for the imported meshes.
+ *
+ * Does nothing if a mesh does not have normals. You might want this post
+ * processing step to be executed if you plan to use tangent space calculations
+ * such as normal mapping applied to the meshes. There's an importer property,
+ * <tt>#AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE</tt>, which allows you to specify
+ * a maximum smoothing angle for the algorithm. However, usually you'll
+ * want to leave it at the default value.
+ */
+ aiProcess_CalcTangentSpace = 0x1,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Identifies and joins identical vertex data sets within all
+ * imported meshes.
+ *
+ * After this step is run, each mesh contains unique vertices,
+ * so a vertex may be used by multiple faces. You usually want
+ * to use this post processing step. If your application deals with
+ * indexed geometry, this step is compulsory or you'll just waste rendering
+ * time. <b>If this flag is not specified</b>, no vertices are referenced by
+ * more than one face and <b>no index buffer is required</b> for rendering.
+ */
+ aiProcess_JoinIdenticalVertices = 0x2,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Converts all the imported data to a left-handed coordinate space.
+ *
+ * By default the data is returned in a right-handed coordinate space (which
+ * OpenGL prefers). In this space, +X points to the right,
+ * +Z points towards the viewer, and +Y points upwards. In the DirectX
+ * coordinate space +X points to the right, +Y points upwards, and +Z points
+ * away from the viewer.
+ *
+ * You'll probably want to consider this flag if you use Direct3D for
+ * rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this
+ * setting and bundles all conversions typically required for D3D-based
+ * applications.
+ */
+ aiProcess_MakeLeftHanded = 0x4,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Triangulates all faces of all meshes.
+ *
+ * By default the imported mesh data might contain faces with more than 3
+ * indices. For rendering you'll usually want all faces to be triangles.
+ * This post processing step splits up faces with more than 3 indices into
+ * triangles. Line and point primitives are *not* modified! If you want
+ * 'triangles only' with no other kinds of primitives, try the following
+ * solution:
+ * <ul>
+ * <li>Specify both #aiProcess_Triangulate and #aiProcess_SortByPType </li>
+ * <li>Ignore all point and line meshes when you process assimp's output</li>
+ * </ul>
+ */
+ aiProcess_Triangulate = 0x8,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Removes some parts of the data structure (animations, materials,
+ * light sources, cameras, textures, vertex components).
+ *
+ * The components to be removed are specified in a separate
+ * importer property, <tt>#AI_CONFIG_PP_RVC_FLAGS</tt>. This is quite useful
+ * if you don't need all parts of the output structure. Vertex colors
+ * are rarely used today for example... Calling this step to remove unneeded
+ * data from the pipeline as early as possible results in increased
+ * performance and a more optimized output data structure.
+ * This step is also useful if you want to force Assimp to recompute
+ * normals or tangents. The corresponding steps don't recompute them if
+ * they're already there (loaded from the source asset). By using this
+ * step you can make sure they are NOT there.
+ *
+ * This flag is a poor one, mainly because its purpose is usually
+ * misunderstood. Consider the following case: a 3D model has been exported
+ * from a CAD app, and it has per-face vertex colors. Vertex positions can't be
+ * shared, thus the #aiProcess_JoinIdenticalVertices step fails to
+ * optimize the data because of these nasty little vertex colors.
+ * Most apps don't even process them, so it's all for nothing. By using
+ * this step, unneeded components are excluded as early as possible
+ * thus opening more room for internal optimizations.
+ */
+ aiProcess_RemoveComponent = 0x10,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Generates normals for all faces of all meshes.
+ *
+ * This is ignored if normals are already there at the time this flag
+ * is evaluated. Model importers try to load them from the source file, so
+ * they're usually already there. Face normals are shared between all points
+ * of a single face, so a single point can have multiple normals, which
+ * forces the library to duplicate vertices in some cases.
+ * #aiProcess_JoinIdenticalVertices is *senseless* then.
+ *
+ * This flag may not be specified together with #aiProcess_GenSmoothNormals.
+ */
+ aiProcess_GenNormals = 0x20,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Generates smooth normals for all vertices in the mesh.
+ *
+ * This is ignored if normals are already there at the time this flag
+ * is evaluated. Model importers try to load them from the source file, so
+ * they're usually already there.
+ *
+ * This flag may not be specified together with
+ * #aiProcess_GenNormals. There's a importer property,
+ * <tt>#AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE</tt> which allows you to specify
+ * an angle maximum for the normal smoothing algorithm. Normals exceeding
+ * this limit are not smoothed, resulting in a 'hard' seam between two faces.
+ * Using a decent angle here (e.g. 80 degrees) results in very good visual
+ * appearance.
+ */
+ aiProcess_GenSmoothNormals = 0x40,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Splits large meshes into smaller sub-meshes.
+ *
+ * This is quite useful for real-time rendering, where the number of triangles
+ * which can be maximally processed in a single draw-call is limited
+ * by the video driver/hardware. The maximum vertex buffer is usually limited
+ * too. Both requirements can be met with this step: you may specify both a
+ * triangle and vertex limit for a single mesh.
+ *
+ * The split limits can (and should!) be set through the
+ * <tt>#AI_CONFIG_PP_SLM_VERTEX_LIMIT</tt> and <tt>#AI_CONFIG_PP_SLM_TRIANGLE_LIMIT</tt>
+ * importer properties. The default values are <tt>#AI_SLM_DEFAULT_MAX_VERTICES</tt> and
+ * <tt>#AI_SLM_DEFAULT_MAX_TRIANGLES</tt>.
+ *
+ * Note that splitting is generally a time-consuming task, but only if there's
+ * something to split. The use of this step is recommended for most users.
+ */
+ aiProcess_SplitLargeMeshes = 0x80,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Removes the node graph and pre-transforms all vertices with
+ * the local transformation matrices of their nodes.
+ *
+ * The output scene still contains nodes, however there is only a
+ * root node with children, each one referencing only one mesh,
+ * and each mesh referencing one material. For rendering, you can
+ * simply render all meshes in order - you don't need to pay
+ * attention to local transformations and the node hierarchy.
+ * Animations are removed during this step.
+ * This step is intended for applications without a scenegraph.
+ * The step CAN cause some problems: if e.g. a mesh of the asset
+ * contains normals and another, using the same material index, does not,
+ * they will be brought together, but the first meshes's part of
+ * the normal list is zeroed. However, these artifacts are rare.
+ * @note The <tt>#AI_CONFIG_PP_PTV_NORMALIZE</tt> configuration property
+ * can be set to normalize the scene's spatial dimension to the -1...1
+ * range.
+ */
+ aiProcess_PreTransformVertices = 0x100,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Limits the number of bones simultaneously affecting a single vertex
+ * to a maximum value.
+ *
+ * If any vertex is affected by more than the maximum number of bones, the least
+ * important vertex weights are removed and the remaining vertex weights are
+ * renormalized so that the weights still sum up to 1.
+ * The default bone weight limit is 4 (defined as <tt>#AI_LMW_MAX_WEIGHTS</tt> in
+ * config.h), but you can use the <tt>#AI_CONFIG_PP_LBW_MAX_WEIGHTS</tt> importer
+ * property to supply your own limit to the post processing step.
+ *
+ * If you intend to perform the skinning in hardware, this post processing
+ * step might be of interest to you.
+ */
+ aiProcess_LimitBoneWeights = 0x200,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Validates the imported scene data structure.
+ * This makes sure that all indices are valid, all animations and
+ * bones are linked correctly, all material references are correct .. etc.
+ *
+ * It is recommended that you capture Assimp's log output if you use this flag,
+ * so you can easily find out what's wrong if a file fails the
+ * validation. The validator is quite strict and will find *all*
+ * inconsistencies in the data structure... It is recommended that plugin
+ * developers use it to debug their loaders. There are two types of
+ * validation failures:
+ * <ul>
+ * <li>Error: There's something wrong with the imported data. Further
+ * postprocessing is not possible and the data is not usable at all.
+ * The import fails. #Importer::GetErrorString() or #aiGetErrorString()
+ * carry the error message around.</li>
+ * <li>Warning: There are some minor issues (e.g. 1000000 animation
+ * keyframes with the same time), but further postprocessing and use
+ * of the data structure is still safe. Warning details are written
+ * to the log file, <tt>#AI_SCENE_FLAGS_VALIDATION_WARNING</tt> is set
+ * in #aiScene::mFlags</li>
+ * </ul>
+ *
+ * This post-processing step is not time-consuming. Its use is not
+ * compulsory, but recommended.
+ */
+ aiProcess_ValidateDataStructure = 0x400,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Reorders triangles for better vertex cache locality.
+ *
+ * The step tries to improve the ACMR (average post-transform vertex cache
+ * miss ratio) for all meshes. The implementation runs in O(n) and is
+ * roughly based on the 'tipsify' algorithm (see <a href="
+ * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf">this
+ * paper</a>).
+ *
+ * If you intend to render huge models in hardware, this step might
+ * be of interest to you. The <tt>#AI_CONFIG_PP_ICL_PTCACHE_SIZE</tt>
+ * importer property can be used to fine-tune the cache optimization.
+ */
+ aiProcess_ImproveCacheLocality = 0x800,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Searches for redundant/unreferenced materials and removes them.
+ *
+ * This is especially useful in combination with the
+ * #aiProcess_PreTransformVertices and #aiProcess_OptimizeMeshes flags.
+ * Both join small meshes with equal characteristics, but they can't do
+ * their work if two meshes have different materials. Because several
+ * material settings are lost during Assimp's import filters,
+ * (and because many exporters don't check for redundant materials), huge
+ * models often have materials which are are defined several times with
+ * exactly the same settings.
+ *
+ * Several material settings not contributing to the final appearance of
+ * a surface are ignored in all comparisons (e.g. the material name).
+ * So, if you're passing additional information through the
+ * content pipeline (probably using *magic* material names), don't
+ * specify this flag. Alternatively take a look at the
+ * <tt>#AI_CONFIG_PP_RRM_EXCLUDE_LIST</tt> importer property.
+ */
+ aiProcess_RemoveRedundantMaterials = 0x1000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step tries to determine which meshes have normal vectors
+ * that are facing inwards and inverts them.
+ *
+ * The algorithm is simple but effective:
+ * the bounding box of all vertices + their normals is compared against
+ * the volume of the bounding box of all vertices without their normals.
+ * This works well for most objects, problems might occur with planar
+ * surfaces. However, the step tries to filter such cases.
+ * The step inverts all in-facing normals. Generally it is recommended
+ * to enable this step, although the result is not always correct.
+ */
+ aiProcess_FixInfacingNormals = 0x2000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step splits meshes with more than one primitive type in
+ * homogeneous sub-meshes.
+ *
+ * The step is executed after the triangulation step. After the step
+ * returns, just one bit is set in aiMesh::mPrimitiveTypes. This is
+ * especially useful for real-time rendering where point and line
+ * primitives are often ignored or rendered separately.
+ * You can use the <tt>#AI_CONFIG_PP_SBP_REMOVE</tt> importer property to
+ * specify which primitive types you need. This can be used to easily
+ * exclude lines and points, which are rarely used, from the import.
+ */
+ aiProcess_SortByPType = 0x8000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step searches all meshes for degenerate primitives and
+ * converts them to proper lines or points.
+ *
+ * A face is 'degenerate' if one or more of its points are identical.
+ * To have the degenerate stuff not only detected and collapsed but
+ * removed, try one of the following procedures:
+ * <br><b>1.</b> (if you support lines and points for rendering but don't
+ * want the degenerates)<br>
+ * <ul>
+ * <li>Specify the #aiProcess_FindDegenerates flag.
+ * </li>
+ * <li>Set the <tt>#AI_CONFIG_PP_FD_REMOVE</tt> importer property to
+ * 1. This will cause the step to remove degenerate triangles from the
+ * import as soon as they're detected. They won't pass any further
+ * pipeline steps.
+ * </li>
+ * </ul>
+ * <br><b>2.</b>(if you don't support lines and points at all)<br>
+ * <ul>
+ * <li>Specify the #aiProcess_FindDegenerates flag.
+ * </li>
+ * <li>Specify the #aiProcess_SortByPType flag. This moves line and
+ * point primitives to separate meshes.
+ * </li>
+ * <li>Set the <tt>#AI_CONFIG_PP_SBP_REMOVE</tt> importer property to
+ * @code aiPrimitiveType_POINTS | aiPrimitiveType_LINES
+ * @endcode to cause SortByPType to reject point
+ * and line meshes from the scene.
+ * </li>
+ * </ul>
+ *
+ * This step also removes very small triangles with a surface area smaller
+ * than 10^-6. If you rely on having these small triangles, or notice holes
+ * in your model, set the property <tt>#AI_CONFIG_PP_FD_CHECKAREA</tt> to
+ * false.
+ * @note Degenerate polygons are not necessarily evil and that's why
+ * they're not removed by default. There are several file formats which
+ * don't support lines or points, and some exporters bypass the
+ * format specification and write them as degenerate triangles instead.
+ */
+ aiProcess_FindDegenerates = 0x10000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step searches all meshes for invalid data, such as zeroed
+ * normal vectors or invalid UV coords and removes/fixes them. This is
+ * intended to get rid of some common exporter errors.
+ *
+ * This is especially useful for normals. If they are invalid, and
+ * the step recognizes this, they will be removed and can later
+ * be recomputed, i.e. by the #aiProcess_GenSmoothNormals flag.<br>
+ * The step will also remove meshes that are infinitely small and reduce
+ * animation tracks consisting of hundreds if redundant keys to a single
+ * key. The <tt>AI_CONFIG_PP_FID_ANIM_ACCURACY</tt> config property decides
+ * the accuracy of the check for duplicate animation tracks.
+ */
+ aiProcess_FindInvalidData = 0x20000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step converts non-UV mappings (such as spherical or
+ * cylindrical mapping) to proper texture coordinate channels.
+ *
+ * Most applications will support UV mapping only, so you will
+ * probably want to specify this step in every case. Note that Assimp is not
+ * always able to match the original mapping implementation of the
+ * 3D app which produced a model perfectly. It's always better to let the
+ * modelling app compute the UV channels - 3ds max, Maya, Blender,
+ * LightWave, and Modo do this for example.
+ *
+ * @note If this step is not requested, you'll need to process the
+ * <tt>#AI_MATKEY_MAPPING</tt> material property in order to display all assets
+ * properly.
+ */
+ aiProcess_GenUVCoords = 0x40000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step applies per-texture UV transformations and bakes
+ * them into stand-alone vtexture coordinate channels.
+ *
+ * UV transformations are specified per-texture - see the
+ * <tt>#AI_MATKEY_UVTRANSFORM</tt> material key for more information.
+ * This step processes all textures with
+ * transformed input UV coordinates and generates a new (pre-transformed) UV channel
+ * which replaces the old channel. Most applications won't support UV
+ * transformations, so you will probably want to specify this step.
+ *
+ * @note UV transformations are usually implemented in real-time apps by
+ * transforming texture coordinates at vertex shader stage with a 3x3
+ * (homogenous) transformation matrix.
+ */
+ aiProcess_TransformUVCoords = 0x80000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step searches for duplicate meshes and replaces them
+ * with references to the first mesh.
+ *
+ * This step takes a while, so don't use it if speed is a concern.
+ * Its main purpose is to workaround the fact that many export
+ * file formats don't support instanced meshes, so exporters need to
+ * duplicate meshes. This step removes the duplicates again. Please
+ * note that Assimp does not currently support per-node material
+ * assignment to meshes, which means that identical meshes with
+ * different materials are currently *not* joined, although this is
+ * planned for future versions.
+ */
+ aiProcess_FindInstances = 0x100000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>A postprocessing step to reduce the number of meshes.
+ *
+ * This will, in fact, reduce the number of draw calls.
+ *
+ * This is a very effective optimization and is recommended to be used
+ * together with #aiProcess_OptimizeGraph, if possible. The flag is fully
+ * compatible with both #aiProcess_SplitLargeMeshes and #aiProcess_SortByPType.
+ */
+ aiProcess_OptimizeMeshes = 0x200000,
+
+
+ // -------------------------------------------------------------------------
+ /** <hr>A postprocessing step to optimize the scene hierarchy.
+ *
+ * Nodes without animations, bones, lights or cameras assigned are
+ * collapsed and joined.
+ *
+ * Node names can be lost during this step. If you use special 'tag nodes'
+ * to pass additional information through your content pipeline, use the
+ * <tt>#AI_CONFIG_PP_OG_EXCLUDE_LIST</tt> importer property to specify a
+ * list of node names you want to be kept. Nodes matching one of the names
+ * in this list won't be touched or modified.
+ *
+ * Use this flag with caution. Most simple files will be collapsed to a
+ * single node, so complex hierarchies are usually completely lost. This is not
+ * useful for editor environments, but probably a very effective
+ * optimization if you just want to get the model data, convert it to your
+ * own format, and render it as fast as possible.
+ *
+ * This flag is designed to be used with #aiProcess_OptimizeMeshes for best
+ * results.
+ *
+ * @note 'Crappy' scenes with thousands of extremely small meshes packed
+ * in deeply nested nodes exist for almost all file formats.
+ * #aiProcess_OptimizeMeshes in combination with #aiProcess_OptimizeGraph
+ * usually fixes them all and makes them renderable.
+ */
+ aiProcess_OptimizeGraph = 0x400000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step flips all UV coordinates along the y-axis and adjusts
+ * material settings and bitangents accordingly.
+ *
+ * <b>Output UV coordinate system:</b>
+ * @code
+ * 0y|0y ---------- 1x|0y
+ * | |
+ * | |
+ * | |
+ * 0x|1y ---------- 1x|1y
+ * @endcode
+ *
+ * You'll probably want to consider this flag if you use Direct3D for
+ * rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this
+ * setting and bundles all conversions typically required for D3D-based
+ * applications.
+ */
+ aiProcess_FlipUVs = 0x800000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step adjusts the output face winding order to be CW.
+ *
+ * The default face winding order is counter clockwise (CCW).
+ *
+ * <b>Output face order:</b>
+ * @code
+ * x2
+ *
+ * x0
+ * x1
+ * @endcode
+ */
+ aiProcess_FlipWindingOrder = 0x1000000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step splits meshes with many bones into sub-meshes so that each
+ * su-bmesh has fewer or as many bones as a given limit.
+ */
+ aiProcess_SplitByBoneCount = 0x2000000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step removes bones losslessly or according to some threshold.
+ *
+ * In some cases (i.e. formats that require it) exporters are forced to
+ * assign dummy bone weights to otherwise static meshes assigned to
+ * animated meshes. Full, weight-based skinning is expensive while
+ * animating nodes is extremely cheap, so this step is offered to clean up
+ * the data in that regard.
+ *
+ * Use <tt>#AI_CONFIG_PP_DB_THRESHOLD</tt> to control this.
+ * Use <tt>#AI_CONFIG_PP_DB_ALL_OR_NONE</tt> if you want bones removed if and
+ * only if all bones within the scene qualify for removal.
+ */
+ aiProcess_Debone = 0x4000000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>This step will perform a global scale of the model.
+ *
+ * Some importers are providing a mechanism to define a scaling unit for the
+ * model. This post processing step can be used to do so. You need to get the
+ * global scaling from your importer settings like in FBX. Use the flag
+ * AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY from the global property table to configure this.
+ *
+ * Use <tt>#AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY</tt> to setup the global scaing factor.
+ */
+ aiProcess_GlobalScale = 0x8000000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>A postprocessing step to embed of textures.
+ *
+ * This will remove external data dependencies for textures.
+ * If a texture's file does not exist at the specified path
+ * (due, for instance, to an absolute path generated on another system),
+ * it will check if a file with the same name exists at the root folder
+ * of the imported model. And if so, it uses that.
+ */
+ aiProcess_EmbedTextures = 0x10000000,
+
+ // aiProcess_GenEntityMeshes = 0x100000,
+ // aiProcess_OptimizeAnimations = 0x200000
+ // aiProcess_FixTexturePaths = 0x200000
+
+
+ aiProcess_ForceGenNormals = 0x20000000,
+
+ // -------------------------------------------------------------------------
+ /** <hr>Drops normals for all faces of all meshes.
+ *
+ * This is ignored if no normals are present.
+ * Face normals are shared between all points of a single face,
+ * so a single point can have multiple normals, which
+ * forces the library to duplicate vertices in some cases.
+ * #aiProcess_JoinIdenticalVertices is *senseless* then.
+ * This process gives sense back to aiProcess_JoinIdenticalVertices
+ */
+ aiProcess_DropNormals = 0x40000000,
+};
+
+
+// ---------------------------------------------------------------------------------------
+/** @def aiProcess_ConvertToLeftHanded
+ * @brief Shortcut flag for Direct3D-based applications.
+ *
+ * Supersedes the #aiProcess_MakeLeftHanded and #aiProcess_FlipUVs and
+ * #aiProcess_FlipWindingOrder flags.
+ * The output data matches Direct3D's conventions: left-handed geometry, upper-left
+ * origin for UV coordinates and finally clockwise face order, suitable for CCW culling.
+ *
+ * @deprecated
+ */
+#define aiProcess_ConvertToLeftHanded ( \
+ aiProcess_MakeLeftHanded | \
+ aiProcess_FlipUVs | \
+ aiProcess_FlipWindingOrder | \
+ 0 )
+
+
+// ---------------------------------------------------------------------------------------
+/** @def aiProcessPreset_TargetRealtime_Fast
+ * @brief Default postprocess configuration optimizing the data for real-time rendering.
+ *
+ * Applications would want to use this preset to load models on end-user PCs,
+ * maybe for direct use in game.
+ *
+ * If you're using DirectX, don't forget to combine this value with
+ * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations
+ * in your application apply the #aiProcess_TransformUVCoords step, too.
+ * @note Please take the time to read the docs for the steps enabled by this preset.
+ * Some of them offer further configurable properties, while some of them might not be of
+ * use for you so it might be better to not specify them.
+ */
+#define aiProcessPreset_TargetRealtime_Fast ( \
+ aiProcess_CalcTangentSpace | \
+ aiProcess_GenNormals | \
+ aiProcess_JoinIdenticalVertices | \
+ aiProcess_Triangulate | \
+ aiProcess_GenUVCoords | \
+ aiProcess_SortByPType | \
+ 0 )
+
+ // ---------------------------------------------------------------------------------------
+ /** @def aiProcessPreset_TargetRealtime_Quality
+ * @brief Default postprocess configuration optimizing the data for real-time rendering.
+ *
+ * Unlike #aiProcessPreset_TargetRealtime_Fast, this configuration
+ * performs some extra optimizations to improve rendering speed and
+ * to minimize memory usage. It could be a good choice for a level editor
+ * environment where import speed is not so important.
+ *
+ * If you're using DirectX, don't forget to combine this value with
+ * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations
+ * in your application apply the #aiProcess_TransformUVCoords step, too.
+ * @note Please take the time to read the docs for the steps enabled by this preset.
+ * Some of them offer further configurable properties, while some of them might not be
+ * of use for you so it might be better to not specify them.
+ */
+#define aiProcessPreset_TargetRealtime_Quality ( \
+ aiProcess_CalcTangentSpace | \
+ aiProcess_GenSmoothNormals | \
+ aiProcess_JoinIdenticalVertices | \
+ aiProcess_ImproveCacheLocality | \
+ aiProcess_LimitBoneWeights | \
+ aiProcess_RemoveRedundantMaterials | \
+ aiProcess_SplitLargeMeshes | \
+ aiProcess_Triangulate | \
+ aiProcess_GenUVCoords | \
+ aiProcess_SortByPType | \
+ aiProcess_FindDegenerates | \
+ aiProcess_FindInvalidData | \
+ 0 )
+
+ // ---------------------------------------------------------------------------------------
+ /** @def aiProcessPreset_TargetRealtime_MaxQuality
+ * @brief Default postprocess configuration optimizing the data for real-time rendering.
+ *
+ * This preset enables almost every optimization step to achieve perfectly
+ * optimized data. It's your choice for level editor environments where import speed
+ * is not important.
+ *
+ * If you're using DirectX, don't forget to combine this value with
+ * the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations
+ * in your application, apply the #aiProcess_TransformUVCoords step, too.
+ * @note Please take the time to read the docs for the steps enabled by this preset.
+ * Some of them offer further configurable properties, while some of them might not be
+ * of use for you so it might be better to not specify them.
+ */
+#define aiProcessPreset_TargetRealtime_MaxQuality ( \
+ aiProcessPreset_TargetRealtime_Quality | \
+ aiProcess_FindInstances | \
+ aiProcess_ValidateDataStructure | \
+ aiProcess_OptimizeMeshes | \
+ 0 )
+
+
+#ifdef __cplusplus
+} // end of extern "C"
+#endif
+
+#endif // AI_POSTPROCESS_H_INC
diff --git a/thirdparty/assimp/include/assimp/qnan.h b/thirdparty/assimp/include/assimp/qnan.h
new file mode 100644
index 0000000000..0918bde5e7
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/qnan.h
@@ -0,0 +1,165 @@
+/*
+---------------------------------------------------------------------------
+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 qnan.h
+ * @brief Some utilities for our dealings with qnans.
+ *
+ * @note Some loaders use qnans to mark invalid values tempoarily, also
+ * Assimp explicitly enforces undefined normals to be set to qnan.
+ * qnan utilities are available in standard libraries (C99 for example)
+ * but last time I checked compiler coverage was so bad that I decided
+ * to reinvent the wheel.
+ */
+
+#ifndef AI_QNAN_H_INCLUDED
+#define AI_QNAN_H_INCLUDED
+
+#include <assimp/defs.h>
+#include <limits>
+#include <stdint.h>
+
+// ---------------------------------------------------------------------------
+/** Data structure to represent the bit pattern of a 32 Bit
+ * IEEE 754 floating-point number. */
+union _IEEESingle
+{
+ float Float;
+ struct
+ {
+ uint32_t Frac : 23;
+ uint32_t Exp : 8;
+ uint32_t Sign : 1;
+ } IEEE;
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure to represent the bit pattern of a 64 Bit
+ * IEEE 754 floating-point number. */
+union _IEEEDouble
+{
+ double Double;
+ struct
+ {
+ uint64_t Frac : 52;
+ uint64_t Exp : 11;
+ uint64_t Sign : 1;
+ } IEEE;
+};
+
+// ---------------------------------------------------------------------------
+/** Check whether a given float is qNaN.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_qnan(float in)
+{
+ // the straightforward solution does not work:
+ // return (in != in);
+ // compiler generates code like this
+ // load <in> to <register-with-different-width>
+ // compare <register-with-different-width> against <in>
+
+ // FIXME: Use <float> stuff instead? I think fpclassify needs C99
+ _IEEESingle temp;
+ memcpy(&temp, &in, sizeof(float));
+ return (temp.IEEE.Exp == (1u << 8)-1 &&
+ temp.IEEE.Frac);
+}
+
+// ---------------------------------------------------------------------------
+/** Check whether a given double is qNaN.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_qnan(double in)
+{
+ // the straightforward solution does not work:
+ // return (in != in);
+ // compiler generates code like this
+ // load <in> to <register-with-different-width>
+ // compare <register-with-different-width> against <in>
+
+ // FIXME: Use <float> stuff instead? I think fpclassify needs C99
+ _IEEEDouble temp;
+ memcpy(&temp, &in, sizeof(in));
+ return (temp.IEEE.Exp == (1u << 11)-1 &&
+ temp.IEEE.Frac);
+}
+
+// ---------------------------------------------------------------------------
+/** @brief check whether a float is either NaN or (+/-) INF.
+ *
+ * Denorms return false, they're treated like normal values.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_special_float(float in)
+{
+ _IEEESingle temp;
+ memcpy(&temp, &in, sizeof(float));
+ return (temp.IEEE.Exp == (1u << 8)-1);
+}
+
+// ---------------------------------------------------------------------------
+/** @brief check whether a double is either NaN or (+/-) INF.
+ *
+ * Denorms return false, they're treated like normal values.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_special_float(double in)
+{
+ _IEEESingle temp;
+ memcpy(&temp, &in, sizeof(float));
+ return (temp.IEEE.Exp == (1u << 11)-1);
+}
+
+// ---------------------------------------------------------------------------
+/** Check whether a float is NOT qNaN.
+ * @param in Input value */
+template<class TReal>
+AI_FORCE_INLINE bool is_not_qnan(TReal in)
+{
+ return !is_qnan(in);
+}
+
+// ---------------------------------------------------------------------------
+/** @brief Get a fresh qnan. */
+AI_FORCE_INLINE ai_real get_qnan()
+{
+ return std::numeric_limits<ai_real>::quiet_NaN();
+}
+
+#endif // !! AI_QNAN_H_INCLUDED
diff --git a/thirdparty/assimp/include/assimp/quaternion.h b/thirdparty/assimp/include/assimp/quaternion.h
new file mode 100644
index 0000000000..96574d24b9
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/quaternion.h
@@ -0,0 +1,130 @@
+/*
+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 quaternion.h
+ * @brief Quaternion structure, including operators when compiling in C++
+ */
+#pragma once
+#ifndef AI_QUATERNION_H_INC
+#define AI_QUATERNION_H_INC
+
+#ifdef __cplusplus
+
+#include "defs.h"
+
+template <typename TReal> class aiVector3t;
+template <typename TReal> class aiMatrix3x3t;
+
+// ---------------------------------------------------------------------------
+/** Represents a quaternion in a 4D vector. */
+template <typename TReal>
+class aiQuaterniont
+{
+public:
+ aiQuaterniont() AI_NO_EXCEPT : w(1.0), x(), y(), z() {}
+ aiQuaterniont(TReal pw, TReal px, TReal py, TReal pz)
+ : w(pw), x(px), y(py), z(pz) {}
+
+ /** Construct from rotation matrix. Result is undefined if the matrix is not orthonormal. */
+ explicit aiQuaterniont( const aiMatrix3x3t<TReal>& pRotMatrix);
+
+ /** Construct from euler angles */
+ aiQuaterniont( TReal rotx, TReal roty, TReal rotz);
+
+ /** Construct from an axis-angle pair */
+ aiQuaterniont( aiVector3t<TReal> axis, TReal angle);
+
+ /** Construct from a normalized quaternion stored in a vec3 */
+ explicit aiQuaterniont( aiVector3t<TReal> normalized);
+
+ /** Returns a matrix representation of the quaternion */
+ aiMatrix3x3t<TReal> GetMatrix() const;
+
+public:
+
+ bool operator== (const aiQuaterniont& o) const;
+ bool operator!= (const aiQuaterniont& o) const;
+
+ bool Equal(const aiQuaterniont& o, TReal epsilon = 1e-6) const;
+
+public:
+
+ /** Normalize the quaternion */
+ aiQuaterniont& Normalize();
+
+ /** Compute quaternion conjugate */
+ aiQuaterniont& Conjugate ();
+
+ /** Rotate a point by this quaternion */
+ aiVector3t<TReal> Rotate (const aiVector3t<TReal>& in);
+
+ /** Multiply two quaternions */
+ aiQuaterniont operator* (const aiQuaterniont& two) const;
+
+public:
+
+ /** Performs a spherical interpolation between two quaternions and writes the result into the third.
+ * @param pOut Target object to received the interpolated rotation.
+ * @param pStart Start rotation of the interpolation at factor == 0.
+ * @param pEnd End rotation, factor == 1.
+ * @param pFactor Interpolation factor between 0 and 1. Values outside of this range yield undefined results.
+ */
+ static void Interpolate( aiQuaterniont& pOut, const aiQuaterniont& pStart,
+ const aiQuaterniont& pEnd, TReal pFactor);
+
+public:
+
+ //! w,x,y,z components of the quaternion
+ TReal w, x, y, z;
+} ;
+
+typedef aiQuaterniont<ai_real> aiQuaternion;
+
+#else
+
+struct aiQuaternion {
+ ai_real w, x, y, z;
+};
+
+#endif
+
+#endif // AI_QUATERNION_H_INC
diff --git a/thirdparty/assimp/include/assimp/quaternion.inl b/thirdparty/assimp/include/assimp/quaternion.inl
new file mode 100644
index 0000000000..c26648215e
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/quaternion.inl
@@ -0,0 +1,286 @@
+/*
+---------------------------------------------------------------------------
+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 quaternion.inl
+ * @brief Inline implementation of aiQuaterniont<TReal> operators
+ */
+#pragma once
+#ifndef AI_QUATERNION_INL_INC
+#define AI_QUATERNION_INL_INC
+
+#ifdef __cplusplus
+#include "quaternion.h"
+
+#include <cmath>
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+bool aiQuaterniont<TReal>::operator== (const aiQuaterniont& o) const
+{
+ return x == o.x && y == o.y && z == o.z && w == o.w;
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+bool aiQuaterniont<TReal>::operator!= (const aiQuaterniont& o) const
+{
+ return !(*this == o);
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline bool aiQuaterniont<TReal>::Equal(const aiQuaterniont& o, TReal epsilon) const {
+ return
+ std::abs(x - o.x) <= epsilon &&
+ std::abs(y - o.y) <= epsilon &&
+ std::abs(z - o.z) <= epsilon &&
+ std::abs(w - o.w) <= epsilon;
+}
+
+// ---------------------------------------------------------------------------
+// Constructs a quaternion from a rotation matrix
+template<typename TReal>
+inline aiQuaterniont<TReal>::aiQuaterniont( const aiMatrix3x3t<TReal> &pRotMatrix)
+{
+ TReal t = pRotMatrix.a1 + pRotMatrix.b2 + pRotMatrix.c3;
+
+ // large enough
+ if( t > static_cast<TReal>(0))
+ {
+ TReal s = std::sqrt(1 + t) * static_cast<TReal>(2.0);
+ x = (pRotMatrix.c2 - pRotMatrix.b3) / s;
+ y = (pRotMatrix.a3 - pRotMatrix.c1) / s;
+ z = (pRotMatrix.b1 - pRotMatrix.a2) / s;
+ w = static_cast<TReal>(0.25) * s;
+ } // else we have to check several cases
+ else if( pRotMatrix.a1 > pRotMatrix.b2 && pRotMatrix.a1 > pRotMatrix.c3 )
+ {
+ // Column 0:
+ TReal s = std::sqrt( static_cast<TReal>(1.0) + pRotMatrix.a1 - pRotMatrix.b2 - pRotMatrix.c3) * static_cast<TReal>(2.0);
+ x = static_cast<TReal>(0.25) * s;
+ y = (pRotMatrix.b1 + pRotMatrix.a2) / s;
+ z = (pRotMatrix.a3 + pRotMatrix.c1) / s;
+ w = (pRotMatrix.c2 - pRotMatrix.b3) / s;
+ }
+ else if( pRotMatrix.b2 > pRotMatrix.c3)
+ {
+ // Column 1:
+ TReal s = std::sqrt( static_cast<TReal>(1.0) + pRotMatrix.b2 - pRotMatrix.a1 - pRotMatrix.c3) * static_cast<TReal>(2.0);
+ x = (pRotMatrix.b1 + pRotMatrix.a2) / s;
+ y = static_cast<TReal>(0.25) * s;
+ z = (pRotMatrix.c2 + pRotMatrix.b3) / s;
+ w = (pRotMatrix.a3 - pRotMatrix.c1) / s;
+ } else
+ {
+ // Column 2:
+ TReal s = std::sqrt( static_cast<TReal>(1.0) + pRotMatrix.c3 - pRotMatrix.a1 - pRotMatrix.b2) * static_cast<TReal>(2.0);
+ x = (pRotMatrix.a3 + pRotMatrix.c1) / s;
+ y = (pRotMatrix.c2 + pRotMatrix.b3) / s;
+ z = static_cast<TReal>(0.25) * s;
+ w = (pRotMatrix.b1 - pRotMatrix.a2) / s;
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Construction from euler angles
+template<typename TReal>
+inline aiQuaterniont<TReal>::aiQuaterniont( TReal fPitch, TReal fYaw, TReal fRoll )
+{
+ const TReal fSinPitch(std::sin(fPitch*static_cast<TReal>(0.5)));
+ const TReal fCosPitch(std::cos(fPitch*static_cast<TReal>(0.5)));
+ const TReal fSinYaw(std::sin(fYaw*static_cast<TReal>(0.5)));
+ const TReal fCosYaw(std::cos(fYaw*static_cast<TReal>(0.5)));
+ const TReal fSinRoll(std::sin(fRoll*static_cast<TReal>(0.5)));
+ const TReal fCosRoll(std::cos(fRoll*static_cast<TReal>(0.5)));
+ const TReal fCosPitchCosYaw(fCosPitch*fCosYaw);
+ const TReal fSinPitchSinYaw(fSinPitch*fSinYaw);
+ x = fSinRoll * fCosPitchCosYaw - fCosRoll * fSinPitchSinYaw;
+ y = fCosRoll * fSinPitch * fCosYaw + fSinRoll * fCosPitch * fSinYaw;
+ z = fCosRoll * fCosPitch * fSinYaw - fSinRoll * fSinPitch * fCosYaw;
+ w = fCosRoll * fCosPitchCosYaw + fSinRoll * fSinPitchSinYaw;
+}
+
+// ---------------------------------------------------------------------------
+// Returns a matrix representation of the quaternion
+template<typename TReal>
+inline aiMatrix3x3t<TReal> aiQuaterniont<TReal>::GetMatrix() const
+{
+ aiMatrix3x3t<TReal> resMatrix;
+ resMatrix.a1 = static_cast<TReal>(1.0) - static_cast<TReal>(2.0) * (y * y + z * z);
+ resMatrix.a2 = static_cast<TReal>(2.0) * (x * y - z * w);
+ resMatrix.a3 = static_cast<TReal>(2.0) * (x * z + y * w);
+ resMatrix.b1 = static_cast<TReal>(2.0) * (x * y + z * w);
+ resMatrix.b2 = static_cast<TReal>(1.0) - static_cast<TReal>(2.0) * (x * x + z * z);
+ resMatrix.b3 = static_cast<TReal>(2.0) * (y * z - x * w);
+ resMatrix.c1 = static_cast<TReal>(2.0) * (x * z - y * w);
+ resMatrix.c2 = static_cast<TReal>(2.0) * (y * z + x * w);
+ resMatrix.c3 = static_cast<TReal>(1.0) - static_cast<TReal>(2.0) * (x * x + y * y);
+
+ return resMatrix;
+}
+
+// ---------------------------------------------------------------------------
+// Construction from an axis-angle pair
+template<typename TReal>
+inline aiQuaterniont<TReal>::aiQuaterniont( aiVector3t<TReal> axis, TReal angle)
+{
+ axis.Normalize();
+
+ const TReal sin_a = std::sin( angle / 2 );
+ const TReal cos_a = std::cos( angle / 2 );
+ x = axis.x * sin_a;
+ y = axis.y * sin_a;
+ z = axis.z * sin_a;
+ w = cos_a;
+}
+// ---------------------------------------------------------------------------
+// Construction from am existing, normalized quaternion
+template<typename TReal>
+inline aiQuaterniont<TReal>::aiQuaterniont( aiVector3t<TReal> normalized)
+{
+ x = normalized.x;
+ y = normalized.y;
+ z = normalized.z;
+
+ const TReal t = static_cast<TReal>(1.0) - (x*x) - (y*y) - (z*z);
+
+ if (t < static_cast<TReal>(0.0)) {
+ w = static_cast<TReal>(0.0);
+ }
+ else w = std::sqrt (t);
+}
+
+// ---------------------------------------------------------------------------
+// Performs a spherical interpolation between two quaternions
+// Implementation adopted from the gmtl project. All others I found on the net fail in some cases.
+// Congrats, gmtl!
+template<typename TReal>
+inline void aiQuaterniont<TReal>::Interpolate( aiQuaterniont& pOut, const aiQuaterniont& pStart, const aiQuaterniont& pEnd, TReal pFactor)
+{
+ // calc cosine theta
+ TReal cosom = pStart.x * pEnd.x + pStart.y * pEnd.y + pStart.z * pEnd.z + pStart.w * pEnd.w;
+
+ // adjust signs (if necessary)
+ aiQuaterniont end = pEnd;
+ if( cosom < static_cast<TReal>(0.0))
+ {
+ cosom = -cosom;
+ end.x = -end.x; // Reverse all signs
+ end.y = -end.y;
+ end.z = -end.z;
+ end.w = -end.w;
+ }
+
+ // Calculate coefficients
+ TReal sclp, sclq;
+ if( (static_cast<TReal>(1.0) - cosom) > static_cast<TReal>(0.0001)) // 0.0001 -> some epsillon
+ {
+ // Standard case (slerp)
+ TReal omega, sinom;
+ omega = std::acos( cosom); // extract theta from dot product's cos theta
+ sinom = std::sin( omega);
+ sclp = std::sin( (static_cast<TReal>(1.0) - pFactor) * omega) / sinom;
+ sclq = std::sin( pFactor * omega) / sinom;
+ } else
+ {
+ // Very close, do linear interp (because it's faster)
+ sclp = static_cast<TReal>(1.0) - pFactor;
+ sclq = pFactor;
+ }
+
+ pOut.x = sclp * pStart.x + sclq * end.x;
+ pOut.y = sclp * pStart.y + sclq * end.y;
+ pOut.z = sclp * pStart.z + sclq * end.z;
+ pOut.w = sclp * pStart.w + sclq * end.w;
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline aiQuaterniont<TReal>& aiQuaterniont<TReal>::Normalize()
+{
+ // compute the magnitude and divide through it
+ const TReal mag = std::sqrt(x*x + y*y + z*z + w*w);
+ if (mag)
+ {
+ const TReal invMag = static_cast<TReal>(1.0)/mag;
+ x *= invMag;
+ y *= invMag;
+ z *= invMag;
+ w *= invMag;
+ }
+ return *this;
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline aiQuaterniont<TReal> aiQuaterniont<TReal>::operator* (const aiQuaterniont& t) const
+{
+ return aiQuaterniont(w*t.w - x*t.x - y*t.y - z*t.z,
+ w*t.x + x*t.w + y*t.z - z*t.y,
+ w*t.y + y*t.w + z*t.x - x*t.z,
+ w*t.z + z*t.w + x*t.y - y*t.x);
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline aiQuaterniont<TReal>& aiQuaterniont<TReal>::Conjugate ()
+{
+ x = -x;
+ y = -y;
+ z = -z;
+ return *this;
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline aiVector3t<TReal> aiQuaterniont<TReal>::Rotate (const aiVector3t<TReal>& v)
+{
+ aiQuaterniont q2(0.f,v.x,v.y,v.z), q = *this, qinv = q;
+ qinv.Conjugate();
+
+ q = q*q2*qinv;
+ return aiVector3t<TReal>(q.x,q.y,q.z);
+}
+
+#endif
+#endif // AI_QUATERNION_INL_INC
diff --git a/thirdparty/assimp/include/assimp/scene.h b/thirdparty/assimp/include/assimp/scene.h
new file mode 100644
index 0000000000..de0239702d
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/scene.h
@@ -0,0 +1,416 @@
+/*
+---------------------------------------------------------------------------
+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 scene.h
+ * @brief Defines the data structures in which the imported scene is returned.
+ */
+#pragma once
+#ifndef AI_SCENE_H_INC
+#define AI_SCENE_H_INC
+
+#include "types.h"
+#include "texture.h"
+#include "mesh.h"
+#include "light.h"
+#include "camera.h"
+#include "material.h"
+#include "anim.h"
+#include "metadata.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+#endif
+
+// -------------------------------------------------------------------------------
+/**
+ * A node in the imported hierarchy.
+ *
+ * Each node has name, a parent node (except for the root node),
+ * a transformation relative to its parent and possibly several child nodes.
+ * Simple file formats don't support hierarchical structures - for these formats
+ * the imported scene does consist of only a single root node without children.
+ */
+// -------------------------------------------------------------------------------
+struct ASSIMP_API aiNode
+{
+ /** The name of the node.
+ *
+ * The name might be empty (length of zero) but all nodes which
+ * need to be referenced by either bones or animations are named.
+ * Multiple nodes may have the same name, except for nodes which are referenced
+ * by bones (see #aiBone and #aiMesh::mBones). Their names *must* be unique.
+ *
+ * Cameras and lights reference a specific node by name - if there
+ * are multiple nodes with this name, they are assigned to each of them.
+ * <br>
+ * There are no limitations with regard to the characters contained in
+ * the name string as it is usually taken directly from the source file.
+ *
+ * Implementations should be able to handle tokens such as whitespace, tabs,
+ * line feeds, quotation marks, ampersands etc.
+ *
+ * Sometimes assimp introduces new nodes not present in the source file
+ * into the hierarchy (usually out of necessity because sometimes the
+ * source hierarchy format is simply not compatible). Their names are
+ * surrounded by @verbatim <> @endverbatim e.g.
+ * @verbatim<DummyRootNode> @endverbatim.
+ */
+ C_STRUCT aiString mName;
+
+ /** The transformation relative to the node's parent. */
+ C_STRUCT aiMatrix4x4 mTransformation;
+
+ /** Parent node. NULL if this node is the root node. */
+ C_STRUCT aiNode* mParent;
+
+ /** The number of child nodes of this node. */
+ unsigned int mNumChildren;
+
+ /** The child nodes of this node. NULL if mNumChildren is 0. */
+ C_STRUCT aiNode** mChildren;
+
+ /** The number of meshes of this node. */
+ unsigned int mNumMeshes;
+
+ /** The meshes of this node. Each entry is an index into the
+ * mesh list of the #aiScene.
+ */
+ unsigned int* mMeshes;
+
+ /** Metadata associated with this node or NULL if there is no metadata.
+ * Whether any metadata is generated depends on the source file format. See the
+ * @link importer_notes @endlink page for more information on every source file
+ * format. Importers that don't document any metadata don't write any.
+ */
+ C_STRUCT aiMetadata* mMetaData;
+
+#ifdef __cplusplus
+ /** Constructor */
+ aiNode();
+
+ /** Construction from a specific name */
+ explicit aiNode(const std::string& name);
+
+ /** Destructor */
+ ~aiNode();
+
+ /** Searches for a node with a specific name, beginning at this
+ * nodes. Normally you will call this method on the root node
+ * of the scene.
+ *
+ * @param name Name to search for
+ * @return NULL or a valid Node if the search was successful.
+ */
+ inline
+ const aiNode* FindNode(const aiString& name) const {
+ return FindNode(name.data);
+ }
+
+ inline
+ aiNode* FindNode(const aiString& name) {
+ return FindNode(name.data);
+ }
+
+ const aiNode* FindNode(const char* name) const;
+
+ aiNode* FindNode(const char* name);
+
+ /**
+ * @brief Will add new children.
+ * @param numChildren Number of children to add.
+ * @param children The array with pointers showing to the children.
+ */
+ void addChildren(unsigned int numChildren, aiNode **children);
+#endif // __cplusplus
+};
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+// -------------------------------------------------------------------------------
+/**
+ * Specifies that the scene data structure that was imported is not complete.
+ * This flag bypasses some internal validations and allows the import
+ * of animation skeletons, material libraries or camera animation paths
+ * using Assimp. Most applications won't support such data.
+ */
+#define AI_SCENE_FLAGS_INCOMPLETE 0x1
+
+/**
+ * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS)
+ * if the validation is successful. In a validated scene you can be sure that
+ * any cross references in the data structure (e.g. vertex indices) are valid.
+ */
+#define AI_SCENE_FLAGS_VALIDATED 0x2
+
+/**
+ * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS)
+ * if the validation is successful but some issues have been found.
+ * This can for example mean that a texture that does not exist is referenced
+ * by a material or that the bone weights for a vertex don't sum to 1.0 ... .
+ * In most cases you should still be able to use the import. This flag could
+ * be useful for applications which don't capture Assimp's log output.
+ */
+#define AI_SCENE_FLAGS_VALIDATION_WARNING 0x4
+
+/**
+ * This flag is currently only set by the aiProcess_JoinIdenticalVertices step.
+ * It indicates that the vertices of the output meshes aren't in the internal
+ * verbose format anymore. In the verbose format all vertices are unique,
+ * no vertex is ever referenced by more than one face.
+ */
+#define AI_SCENE_FLAGS_NON_VERBOSE_FORMAT 0x8
+
+ /**
+ * Denotes pure height-map terrain data. Pure terrains usually consist of quads,
+ * sometimes triangles, in a regular grid. The x,y coordinates of all vertex
+ * positions refer to the x,y coordinates on the terrain height map, the z-axis
+ * stores the elevation at a specific point.
+ *
+ * TER (Terragen) and HMP (3D Game Studio) are height map formats.
+ * @note Assimp is probably not the best choice for loading *huge* terrains -
+ * fully triangulated data takes extremely much free store and should be avoided
+ * as long as possible (typically you'll do the triangulation when you actually
+ * need to render it).
+ */
+#define AI_SCENE_FLAGS_TERRAIN 0x10
+
+ /**
+ * Specifies that the scene data can be shared between structures. For example:
+ * one vertex in few faces. \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT can not be
+ * used for this because \ref AI_SCENE_FLAGS_NON_VERBOSE_FORMAT has internal
+ * meaning about postprocessing steps.
+ */
+#define AI_SCENE_FLAGS_ALLOW_SHARED 0x20
+
+// -------------------------------------------------------------------------------
+/** The root structure of the imported data.
+ *
+ * Everything that was imported from the given file can be accessed from here.
+ * Objects of this class are generally maintained and owned by Assimp, not
+ * by the caller. You shouldn't want to instance it, nor should you ever try to
+ * delete a given scene on your own.
+ */
+// -------------------------------------------------------------------------------
+struct aiScene
+{
+ /** Any combination of the AI_SCENE_FLAGS_XXX flags. By default
+ * this value is 0, no flags are set. Most applications will
+ * want to reject all scenes with the AI_SCENE_FLAGS_INCOMPLETE
+ * bit set.
+ */
+ unsigned int mFlags;
+
+ /** The root node of the hierarchy.
+ *
+ * There will always be at least the root node if the import
+ * was successful (and no special flags have been set).
+ * Presence of further nodes depends on the format and content
+ * of the imported file.
+ */
+ C_STRUCT aiNode* mRootNode;
+
+ /** The number of meshes in the scene. */
+ unsigned int mNumMeshes;
+
+ /** The array of meshes.
+ *
+ * Use the indices given in the aiNode structure to access
+ * this array. The array is mNumMeshes in size. If the
+ * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always
+ * be at least ONE material.
+ */
+ C_STRUCT aiMesh** mMeshes;
+
+ /** The number of materials in the scene. */
+ unsigned int mNumMaterials;
+
+ /** The array of materials.
+ *
+ * Use the index given in each aiMesh structure to access this
+ * array. The array is mNumMaterials in size. If the
+ * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always
+ * be at least ONE material.
+ */
+ C_STRUCT aiMaterial** mMaterials;
+
+ /** The number of animations in the scene. */
+ unsigned int mNumAnimations;
+
+ /** The array of animations.
+ *
+ * All animations imported from the given file are listed here.
+ * The array is mNumAnimations in size.
+ */
+ C_STRUCT aiAnimation** mAnimations;
+
+ /** The number of textures embedded into the file */
+ unsigned int mNumTextures;
+
+ /** The array of embedded textures.
+ *
+ * Not many file formats embed their textures into the file.
+ * An example is Quake's MDL format (which is also used by
+ * some GameStudio versions)
+ */
+ C_STRUCT aiTexture** mTextures;
+
+ /** The number of light sources in the scene. Light sources
+ * are fully optional, in most cases this attribute will be 0
+ */
+ unsigned int mNumLights;
+
+ /** The array of light sources.
+ *
+ * All light sources imported from the given file are
+ * listed here. The array is mNumLights in size.
+ */
+ C_STRUCT aiLight** mLights;
+
+ /** The number of cameras in the scene. Cameras
+ * are fully optional, in most cases this attribute will be 0
+ */
+ unsigned int mNumCameras;
+
+ /** The array of cameras.
+ *
+ * All cameras imported from the given file are listed here.
+ * The array is mNumCameras in size. The first camera in the
+ * array (if existing) is the default camera view into
+ * the scene.
+ */
+ C_STRUCT aiCamera** mCameras;
+
+ /**
+ * @brief The global metadata assigned to the scene itself.
+ *
+ * This data contains global metadata which belongs to the scene like
+ * unit-conversions, versions, vendors or other model-specific data. This
+ * can be used to store format-specific metadata as well.
+ */
+ C_STRUCT aiMetadata* mMetaData;
+
+
+#ifdef __cplusplus
+
+ //! Default constructor - set everything to 0/NULL
+ ASSIMP_API aiScene();
+
+ //! Destructor
+ ASSIMP_API ~aiScene();
+
+ //! Check whether the scene contains meshes
+ //! Unless no special scene flags are set this will always be true.
+ inline bool HasMeshes() const {
+ return mMeshes != NULL && mNumMeshes > 0;
+ }
+
+ //! Check whether the scene contains materials
+ //! Unless no special scene flags are set this will always be true.
+ inline bool HasMaterials() const {
+ return mMaterials != NULL && mNumMaterials > 0;
+ }
+
+ //! Check whether the scene contains lights
+ inline bool HasLights() const {
+ return mLights != NULL && mNumLights > 0;
+ }
+
+ //! Check whether the scene contains textures
+ inline bool HasTextures() const {
+ return mTextures != NULL && mNumTextures > 0;
+ }
+
+ //! Check whether the scene contains cameras
+ inline bool HasCameras() const {
+ return mCameras != NULL && mNumCameras > 0;
+ }
+
+ //! Check whether the scene contains animations
+ inline bool HasAnimations() const {
+ return mAnimations != NULL && mNumAnimations > 0;
+ }
+
+ //! Returns a short filename from a full path
+ static const char* GetShortFilename(const char* filename) {
+ const char* lastSlash = strrchr(filename, '/');
+ if (lastSlash == nullptr) {
+ lastSlash = strrchr(filename, '\\');
+ }
+ const char* shortFilename = lastSlash != nullptr ? lastSlash + 1 : filename;
+ return shortFilename;
+ }
+
+ //! Returns an embedded texture
+ const aiTexture* GetEmbeddedTexture(const char* filename) const {
+ const char* shortFilename = GetShortFilename(filename);
+ for (unsigned int i = 0; i < mNumTextures; i++) {
+ const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str());
+ if (strcmp(shortTextureFilename, shortFilename) == 0) {
+ return mTextures[i];
+ }
+ }
+ return nullptr;
+ }
+#endif // __cplusplus
+
+ /** Internal data, do not touch */
+#ifdef __cplusplus
+ void* mPrivate;
+#else
+ char* mPrivate;
+#endif
+
+};
+
+#ifdef __cplusplus
+} //! namespace Assimp
+#endif
+
+#endif // AI_SCENE_H_INC
diff --git a/thirdparty/assimp/include/assimp/texture.h b/thirdparty/assimp/include/assimp/texture.h
new file mode 100644
index 0000000000..dc6cbef65c
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/texture.h
@@ -0,0 +1,227 @@
+/*
+---------------------------------------------------------------------------
+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 texture.h
+ * @brief Defines texture helper structures for the library
+ *
+ * Used for file formats which embed their textures into the model file.
+ * Supported are both normal textures, which are stored as uncompressed
+ * pixels, and "compressed" textures, which are stored in a file format
+ * such as PNG or TGA.
+ */
+#pragma once
+#ifndef AI_TEXTURE_H_INC
+#define AI_TEXTURE_H_INC
+
+#include "types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// --------------------------------------------------------------------------------
+
+/** \def AI_EMBEDDED_TEXNAME_PREFIX
+ * \ref AI_MAKE_EMBEDDED_TEXNAME
+ */
+#ifndef AI_EMBEDDED_TEXNAME_PREFIX
+# define AI_EMBEDDED_TEXNAME_PREFIX "*"
+#endif
+
+/** @def AI_MAKE_EMBEDDED_TEXNAME
+ * Used to build the reserved path name used by the material system to
+ * reference textures that are embedded into their corresponding
+ * model files. The parameter specifies the index of the texture
+ * (zero-based, in the aiScene::mTextures array)
+ */
+#if (!defined AI_MAKE_EMBEDDED_TEXNAME)
+# define AI_MAKE_EMBEDDED_TEXNAME(_n_) AI_EMBEDDED_TEXNAME_PREFIX # _n_
+#endif
+
+
+#include "./Compiler/pushpack1.h"
+
+// --------------------------------------------------------------------------------
+/** @brief Helper structure to represent a texel in a ARGB8888 format
+*
+* Used by aiTexture.
+*/
+struct aiTexel
+{
+ unsigned char b,g,r,a;
+
+#ifdef __cplusplus
+ //! Comparison operator
+ bool operator== (const aiTexel& other) const
+ {
+ return b == other.b && r == other.r &&
+ g == other.g && a == other.a;
+ }
+
+ //! Inverse comparison operator
+ bool operator!= (const aiTexel& other) const
+ {
+ return b != other.b || r != other.r ||
+ g != other.g || a != other.a;
+ }
+
+ //! Conversion to a floating-point 4d color
+ operator aiColor4D() const
+ {
+ return aiColor4D(r/255.f,g/255.f,b/255.f,a/255.f);
+ }
+#endif // __cplusplus
+
+} PACK_STRUCT;
+
+#include "./Compiler/poppack1.h"
+
+#define HINTMAXTEXTURELEN 9
+
+// --------------------------------------------------------------------------------
+/** Helper structure to describe an embedded texture
+ *
+ * Normally textures are contained in external files but some file formats embed
+ * them directly in the model file. There are two types of embedded textures:
+ * 1. Uncompressed textures. The color data is given in an uncompressed format.
+ * 2. Compressed textures stored in a file format like png or jpg. The raw file
+ * bytes are given so the application must utilize an image decoder (e.g. DevIL) to
+ * get access to the actual color data.
+ *
+ * Embedded textures are referenced from materials using strings like "*0", "*1", etc.
+ * as the texture paths (a single asterisk character followed by the
+ * zero-based index of the texture in the aiScene::mTextures array).
+ */
+struct aiTexture {
+ /** Width of the texture, in pixels
+ *
+ * If mHeight is zero the texture is compressed in a format
+ * like JPEG. In this case mWidth specifies the size of the
+ * memory area pcData is pointing to, in bytes.
+ */
+ unsigned int mWidth;
+
+ /** Height of the texture, in pixels
+ *
+ * If this value is zero, pcData points to an compressed texture
+ * in any format (e.g. JPEG).
+ */
+ unsigned int mHeight;
+
+ /** A hint from the loader to make it easier for applications
+ * to determine the type of embedded textures.
+ *
+ * If mHeight != 0 this member is show how data is packed. Hint will consist of
+ * two parts: channel order and channel bitness (count of the bits for every
+ * color channel). For simple parsing by the viewer it's better to not omit
+ * absent color channel and just use 0 for bitness. For example:
+ * 1. Image contain RGBA and 8 bit per channel, achFormatHint == "rgba8888";
+ * 2. Image contain ARGB and 8 bit per channel, achFormatHint == "argb8888";
+ * 3. Image contain RGB and 5 bit for R and B channels and 6 bit for G channel, achFormatHint == "rgba5650";
+ * 4. One color image with B channel and 1 bit for it, achFormatHint == "rgba0010";
+ * If mHeight == 0 then achFormatHint is set set to '\\0\\0\\0\\0' if the loader has no additional
+ * information about the texture file format used OR the
+ * file extension of the format without a trailing dot. If there
+ * are multiple file extensions for a format, the shortest
+ * extension is chosen (JPEG maps to 'jpg', not to 'jpeg').
+ * E.g. 'dds\\0', 'pcx\\0', 'jpg\\0'. All characters are lower-case.
+ * The fourth character will always be '\\0'.
+ */
+ char achFormatHint[ HINTMAXTEXTURELEN ];// 8 for string + 1 for terminator.
+
+ /** Data of the texture.
+ *
+ * Points to an array of mWidth * mHeight aiTexel's.
+ * The format of the texture data is always ARGB8888 to
+ * make the implementation for user of the library as easy
+ * as possible. If mHeight = 0 this is a pointer to a memory
+ * buffer of size mWidth containing the compressed texture
+ * data. Good luck, have fun!
+ */
+ C_STRUCT aiTexel* pcData;
+
+ /** Texture original filename
+ *
+ * Used to get the texture reference
+ */
+ C_STRUCT aiString mFilename;
+
+#ifdef __cplusplus
+
+ //! For compressed textures (mHeight == 0): compare the
+ //! format hint against a given string.
+ //! @param s Input string. 3 characters are maximally processed.
+ //! Example values: "jpg", "png"
+ //! @return true if the given string matches the format hint
+ bool CheckFormat(const char* s) const {
+ if (nullptr == s) {
+ return false;
+ }
+
+ return (0 == ::strncmp(achFormatHint, s, sizeof(achFormatHint)));
+ }
+
+ // Construction
+ aiTexture() AI_NO_EXCEPT
+ : mWidth(0)
+ , mHeight(0)
+ , pcData(nullptr)
+ , mFilename() {
+ achFormatHint[0] = achFormatHint[1] = 0;
+ achFormatHint[2] = achFormatHint[3] = 0;
+ }
+
+ // Destruction
+ ~aiTexture () {
+ delete[] pcData;
+ }
+#endif
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AI_TEXTURE_H_INC
diff --git a/thirdparty/assimp/include/assimp/types.h b/thirdparty/assimp/include/assimp/types.h
new file mode 100644
index 0000000000..748e4851f3
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/types.h
@@ -0,0 +1,529 @@
+/*
+---------------------------------------------------------------------------
+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 types.h
+ * Basic data types and primitives, such as vectors or colors.
+ */
+#pragma once
+#ifndef AI_TYPES_H_INC
+#define AI_TYPES_H_INC
+
+// Some runtime headers
+#include <sys/types.h>
+#include <stddef.h>
+#include <string.h>
+#include <limits.h>
+
+// Our compile configuration
+#include "defs.h"
+
+// Some types moved to separate header due to size of operators
+#include "vector3.h"
+#include "vector2.h"
+#include "color4.h"
+#include "matrix3x3.h"
+#include "matrix4x4.h"
+#include "quaternion.h"
+
+#ifdef __cplusplus
+#include <cstring>
+#include <new> // for std::nothrow_t
+#include <string> // for aiString::Set(const std::string&)
+
+namespace Assimp {
+ //! @cond never
+namespace Intern {
+ // --------------------------------------------------------------------
+ /** @brief Internal helper class to utilize our internal new/delete
+ * routines for allocating object of this and derived classes.
+ *
+ * By doing this you can safely share class objects between Assimp
+ * and the application - it works even over DLL boundaries. A good
+ * example is the #IOSystem where the application allocates its custom
+ * #IOSystem, then calls #Importer::SetIOSystem(). When the Importer
+ * destructs, Assimp calls operator delete on the stored #IOSystem.
+ * If it lies on a different heap than Assimp is working with,
+ * the application is determined to crash.
+ */
+ // --------------------------------------------------------------------
+#ifndef SWIG
+ struct ASSIMP_API AllocateFromAssimpHeap {
+ // http://www.gotw.ca/publications/mill15.htm
+
+ // new/delete overload
+ void *operator new ( size_t num_bytes) /* throw( std::bad_alloc ) */;
+ void *operator new ( size_t num_bytes, const std::nothrow_t& ) throw();
+ void operator delete ( void* data);
+
+ // array new/delete overload
+ void *operator new[] ( size_t num_bytes) /* throw( std::bad_alloc ) */;
+ void *operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw();
+ void operator delete[] ( void* data);
+
+ }; // struct AllocateFromAssimpHeap
+#endif
+} // namespace Intern
+ //! @endcond
+} // namespace Assimp
+
+extern "C" {
+#endif
+
+/** Maximum dimension for strings, ASSIMP strings are zero terminated. */
+#ifdef __cplusplus
+ static const size_t MAXLEN = 1024;
+#else
+# define MAXLEN 1024
+#endif
+
+// ----------------------------------------------------------------------------------
+/** Represents a plane in a three-dimensional, euclidean space
+*/
+struct aiPlane {
+#ifdef __cplusplus
+ aiPlane () AI_NO_EXCEPT : a(0.f), b(0.f), c(0.f), d(0.f) {}
+ aiPlane (ai_real _a, ai_real _b, ai_real _c, ai_real _d)
+ : a(_a), b(_b), c(_c), d(_d) {}
+
+ aiPlane (const aiPlane& o) : a(o.a), b(o.b), c(o.c), d(o.d) {}
+
+#endif // !__cplusplus
+
+ //! Plane equation
+ ai_real a,b,c,d;
+}; // !struct aiPlane
+
+// ----------------------------------------------------------------------------------
+/** Represents a ray
+*/
+struct aiRay {
+#ifdef __cplusplus
+ aiRay () AI_NO_EXCEPT {}
+ aiRay (const aiVector3D& _pos, const aiVector3D& _dir)
+ : pos(_pos), dir(_dir) {}
+
+ aiRay (const aiRay& o) : pos (o.pos), dir (o.dir) {}
+
+#endif // !__cplusplus
+
+ //! Position and direction of the ray
+ C_STRUCT aiVector3D pos, dir;
+}; // !struct aiRay
+
+// ----------------------------------------------------------------------------------
+/** Represents a color in Red-Green-Blue space.
+*/
+struct aiColor3D
+{
+#ifdef __cplusplus
+ aiColor3D () AI_NO_EXCEPT : r(0.0f), g(0.0f), b(0.0f) {}
+ aiColor3D (ai_real _r, ai_real _g, ai_real _b) : r(_r), g(_g), b(_b) {}
+ explicit aiColor3D (ai_real _r) : r(_r), g(_r), b(_r) {}
+ aiColor3D (const aiColor3D& o) : r(o.r), g(o.g), b(o.b) {}
+
+ /** Component-wise comparison */
+ // TODO: add epsilon?
+ bool operator == (const aiColor3D& other) const
+ {return r == other.r && g == other.g && b == other.b;}
+
+ /** Component-wise inverse comparison */
+ // TODO: add epsilon?
+ bool operator != (const aiColor3D& other) const
+ {return r != other.r || g != other.g || b != other.b;}
+
+ /** Component-wise comparison */
+ // TODO: add epsilon?
+ bool operator < (const aiColor3D& other) const {
+ return r < other.r || ( r == other.r && (g < other.g || (g == other.g && b < other.b ) ) );
+ }
+
+ /** Component-wise addition */
+ aiColor3D operator+(const aiColor3D& c) const {
+ return aiColor3D(r+c.r,g+c.g,b+c.b);
+ }
+
+ /** Component-wise subtraction */
+ aiColor3D operator-(const aiColor3D& c) const {
+ return aiColor3D(r-c.r,g-c.g,b-c.b);
+ }
+
+ /** Component-wise multiplication */
+ aiColor3D operator*(const aiColor3D& c) const {
+ return aiColor3D(r*c.r,g*c.g,b*c.b);
+ }
+
+ /** Multiply with a scalar */
+ aiColor3D operator*(ai_real f) const {
+ return aiColor3D(r*f,g*f,b*f);
+ }
+
+ /** Access a specific color component */
+ ai_real operator[](unsigned int i) const {
+ return *(&r + i);
+ }
+
+ /** Access a specific color component */
+ ai_real& operator[](unsigned int i) {
+ if ( 0 == i ) {
+ return r;
+ } else if ( 1 == i ) {
+ return g;
+ } else if ( 2 == i ) {
+ return b;
+ }
+ return r;
+ }
+
+ /** Check whether a color is black */
+ bool IsBlack() const {
+ static const ai_real epsilon = ai_real(10e-3);
+ return std::fabs( r ) < epsilon && std::fabs( g ) < epsilon && std::fabs( b ) < epsilon;
+ }
+
+#endif // !__cplusplus
+
+ //! Red, green and blue color values
+ ai_real r, g, b;
+}; // !struct aiColor3D
+
+// ----------------------------------------------------------------------------------
+/** Represents an UTF-8 string, zero byte terminated.
+ *
+ * The character set of an aiString is explicitly defined to be UTF-8. This Unicode
+ * transformation was chosen in the belief that most strings in 3d files are limited
+ * to ASCII, thus the character set needed to be strictly ASCII compatible.
+ *
+ * Most text file loaders provide proper Unicode input file handling, special unicode
+ * characters are correctly transcoded to UTF8 and are kept throughout the libraries'
+ * import pipeline.
+ *
+ * For most applications, it will be absolutely sufficient to interpret the
+ * aiString as ASCII data and work with it as one would work with a plain char*.
+ * Windows users in need of proper support for i.e asian characters can use the
+ * MultiByteToWideChar(), WideCharToMultiByte() WinAPI functionality to convert the
+ * UTF-8 strings to their working character set (i.e. MBCS, WideChar).
+ *
+ * We use this representation instead of std::string to be C-compatible. The
+ * (binary) length of such a string is limited to MAXLEN characters (including the
+ * the terminating zero).
+*/
+struct aiString
+{
+#ifdef __cplusplus
+ /** Default constructor, the string is set to have zero length */
+ aiString() AI_NO_EXCEPT
+ : length( 0 ) {
+ data[0] = '\0';
+
+#ifdef ASSIMP_BUILD_DEBUG
+ // Debug build: overwrite the string on its full length with ESC (27)
+ memset(data+1,27,MAXLEN-1);
+#endif
+ }
+
+ /** Copy constructor */
+ aiString(const aiString& rOther) :
+ length(rOther.length)
+ {
+ // Crop the string to the maximum length
+ length = length>=MAXLEN?MAXLEN-1:length;
+ memcpy( data, rOther.data, length);
+ data[length] = '\0';
+ }
+
+ /** Constructor from std::string */
+ explicit aiString(const std::string& pString) :
+ length(pString.length())
+ {
+ length = length>=MAXLEN?MAXLEN-1:length;
+ memcpy( data, pString.c_str(), length);
+ data[length] = '\0';
+ }
+
+ /** Copy a std::string to the aiString */
+ void Set( const std::string& pString) {
+ if( pString.length() > MAXLEN - 1) {
+ return;
+ }
+ length = pString.length();
+ memcpy( data, pString.c_str(), length);
+ data[length] = 0;
+ }
+
+ /** Copy a const char* to the aiString */
+ void Set( const char* sz) {
+ const size_t len = ::strlen(sz);
+ if( len > MAXLEN - 1) {
+ return;
+ }
+ length = len;
+ memcpy( data, sz, len);
+ data[len] = 0;
+ }
+
+
+ /** Assignment operator */
+ aiString& operator = (const aiString &rOther) {
+ if (this == &rOther) {
+ return *this;
+ }
+
+ length = rOther.length;;
+ memcpy( data, rOther.data, length);
+ data[length] = '\0';
+ return *this;
+ }
+
+
+ /** Assign a const char* to the string */
+ aiString& operator = (const char* sz) {
+ Set(sz);
+ return *this;
+ }
+
+ /** Assign a cstd::string to the string */
+ aiString& operator = ( const std::string& pString) {
+ Set(pString);
+ return *this;
+ }
+
+ /** Comparison operator */
+ bool operator==(const aiString& other) const {
+ return (length == other.length && 0 == memcmp(data,other.data,length));
+ }
+
+ /** Inverse comparison operator */
+ bool operator!=(const aiString& other) const {
+ return (length != other.length || 0 != memcmp(data,other.data,length));
+ }
+
+ /** Append a string to the string */
+ void Append (const char* app) {
+ const size_t len = ::strlen(app);
+ if (!len) {
+ return;
+ }
+ if (length + len >= MAXLEN) {
+ return;
+ }
+
+ memcpy(&data[length],app,len+1);
+ length += len;
+ }
+
+ /** Clear the string - reset its length to zero */
+ void Clear () {
+ length = 0;
+ data[0] = '\0';
+
+#ifdef ASSIMP_BUILD_DEBUG
+ // Debug build: overwrite the string on its full length with ESC (27)
+ memset(data+1,27,MAXLEN-1);
+#endif
+ }
+
+ /** Returns a pointer to the underlying zero-terminated array of characters */
+ const char* C_Str() const {
+ return data;
+ }
+
+#endif // !__cplusplus
+
+ /** Binary length of the string excluding the terminal 0. This is NOT the
+ * logical length of strings containing UTF-8 multi-byte sequences! It's
+ * the number of bytes from the beginning of the string to its end.*/
+ size_t length;
+
+ /** String buffer. Size limit is MAXLEN */
+ char data[MAXLEN];
+} ; // !struct aiString
+
+
+// ----------------------------------------------------------------------------------
+/** Standard return type for some library functions.
+ * Rarely used, and if, mostly in the C API.
+ */
+typedef enum aiReturn
+{
+ /** Indicates that a function was successful */
+ aiReturn_SUCCESS = 0x0,
+
+ /** Indicates that a function failed */
+ aiReturn_FAILURE = -0x1,
+
+ /** Indicates that not enough memory was available
+ * to perform the requested operation
+ */
+ aiReturn_OUTOFMEMORY = -0x3,
+
+ /** @cond never
+ * Force 32-bit size enum
+ */
+ _AI_ENFORCE_ENUM_SIZE = 0x7fffffff
+
+ /// @endcond
+} aiReturn; // !enum aiReturn
+
+// just for backwards compatibility, don't use these constants anymore
+#define AI_SUCCESS aiReturn_SUCCESS
+#define AI_FAILURE aiReturn_FAILURE
+#define AI_OUTOFMEMORY aiReturn_OUTOFMEMORY
+
+// ----------------------------------------------------------------------------------
+/** Seek origins (for the virtual file system API).
+ * Much cooler than using SEEK_SET, SEEK_CUR or SEEK_END.
+ */
+enum aiOrigin
+{
+ /** Beginning of the file */
+ aiOrigin_SET = 0x0,
+
+ /** Current position of the file pointer */
+ aiOrigin_CUR = 0x1,
+
+ /** End of the file, offsets must be negative */
+ aiOrigin_END = 0x2,
+
+ /** @cond never
+ * Force 32-bit size enum
+ */
+ _AI_ORIGIN_ENFORCE_ENUM_SIZE = 0x7fffffff
+
+ /// @endcond
+}; // !enum aiOrigin
+
+// ----------------------------------------------------------------------------------
+/** @brief Enumerates predefined log streaming destinations.
+ * Logging to these streams can be enabled with a single call to
+ * #LogStream::createDefaultStream.
+ */
+enum aiDefaultLogStream
+{
+ /** Stream the log to a file */
+ aiDefaultLogStream_FILE = 0x1,
+
+ /** Stream the log to std::cout */
+ aiDefaultLogStream_STDOUT = 0x2,
+
+ /** Stream the log to std::cerr */
+ aiDefaultLogStream_STDERR = 0x4,
+
+ /** MSVC only: Stream the log the the debugger
+ * (this relies on OutputDebugString from the Win32 SDK)
+ */
+ aiDefaultLogStream_DEBUGGER = 0x8,
+
+ /** @cond never
+ * Force 32-bit size enum
+ */
+ _AI_DLS_ENFORCE_ENUM_SIZE = 0x7fffffff
+ /// @endcond
+}; // !enum aiDefaultLogStream
+
+// just for backwards compatibility, don't use these constants anymore
+#define DLS_FILE aiDefaultLogStream_FILE
+#define DLS_STDOUT aiDefaultLogStream_STDOUT
+#define DLS_STDERR aiDefaultLogStream_STDERR
+#define DLS_DEBUGGER aiDefaultLogStream_DEBUGGER
+
+// ----------------------------------------------------------------------------------
+/** Stores the memory requirements for different components (e.g. meshes, materials,
+ * animations) of an import. All sizes are in bytes.
+ * @see Importer::GetMemoryRequirements()
+*/
+struct aiMemoryInfo
+{
+#ifdef __cplusplus
+
+ /** Default constructor */
+ aiMemoryInfo() AI_NO_EXCEPT
+ : textures (0)
+ , materials (0)
+ , meshes (0)
+ , nodes (0)
+ , animations (0)
+ , cameras (0)
+ , lights (0)
+ , total (0)
+ {}
+
+#endif
+
+ /** Storage allocated for texture data */
+ unsigned int textures;
+
+ /** Storage allocated for material data */
+ unsigned int materials;
+
+ /** Storage allocated for mesh data */
+ unsigned int meshes;
+
+ /** Storage allocated for node data */
+ unsigned int nodes;
+
+ /** Storage allocated for animation data */
+ unsigned int animations;
+
+ /** Storage allocated for camera data */
+ unsigned int cameras;
+
+ /** Storage allocated for light data */
+ unsigned int lights;
+
+ /** Total storage allocated for the full import. */
+ unsigned int total;
+}; // !struct aiMemoryInfo
+
+#ifdef __cplusplus
+}
+#endif //! __cplusplus
+
+// Include implementation files
+#include "vector2.inl"
+#include "vector3.inl"
+#include "color4.inl"
+#include "quaternion.inl"
+#include "matrix3x3.inl"
+#include "matrix4x4.inl"
+
+#endif // AI_TYPES_H_INC
diff --git a/thirdparty/assimp/include/assimp/vector2.h b/thirdparty/assimp/include/assimp/vector2.h
new file mode 100644
index 0000000000..d5ef001542
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/vector2.h
@@ -0,0 +1,107 @@
+/*
+---------------------------------------------------------------------------
+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 vector2.h
+ * @brief 2D vector structure, including operators when compiling in C++
+ */
+#pragma once
+#ifndef AI_VECTOR2D_H_INC
+#define AI_VECTOR2D_H_INC
+
+#ifdef __cplusplus
+# include <cmath>
+#else
+# include <math.h>
+#endif
+
+#include "defs.h"
+
+// ----------------------------------------------------------------------------------
+/** Represents a two-dimensional vector.
+ */
+
+#ifdef __cplusplus
+template <typename TReal>
+class aiVector2t {
+public:
+ aiVector2t () : x(), y() {}
+ aiVector2t (TReal _x, TReal _y) : x(_x), y(_y) {}
+ explicit aiVector2t (TReal _xyz) : x(_xyz), y(_xyz) {}
+ aiVector2t (const aiVector2t& o) = default;
+
+ void Set( TReal pX, TReal pY);
+ TReal SquareLength() const ;
+ TReal Length() const ;
+ aiVector2t& Normalize();
+
+ const aiVector2t& operator += (const aiVector2t& o);
+ const aiVector2t& operator -= (const aiVector2t& o);
+ const aiVector2t& operator *= (TReal f);
+ const aiVector2t& operator /= (TReal f);
+
+ TReal operator[](unsigned int i) const;
+
+ bool operator== (const aiVector2t& other) const;
+ bool operator!= (const aiVector2t& other) const;
+
+ bool Equal(const aiVector2t& other, TReal epsilon = 1e-6) const;
+
+ aiVector2t& operator= (TReal f);
+ const aiVector2t SymMul(const aiVector2t& o);
+
+ template <typename TOther>
+ operator aiVector2t<TOther> () const;
+
+ TReal x, y;
+};
+
+typedef aiVector2t<ai_real> aiVector2D;
+
+#else
+
+struct aiVector2D {
+ ai_real x, y;
+};
+
+#endif // __cplusplus
+
+#endif // AI_VECTOR2D_H_INC
diff --git a/thirdparty/assimp/include/assimp/vector2.inl b/thirdparty/assimp/include/assimp/vector2.inl
new file mode 100644
index 0000000000..3b7a7beabb
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/vector2.inl
@@ -0,0 +1,244 @@
+/*
+---------------------------------------------------------------------------
+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 vector2.inl
+ * @brief Inline implementation of aiVector2t<TReal> operators
+ */
+#pragma once
+#ifndef AI_VECTOR2D_INL_INC
+#define AI_VECTOR2D_INL_INC
+
+#ifdef __cplusplus
+#include "vector2.h"
+
+#include <cmath>
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+template <typename TOther>
+aiVector2t<TReal>::operator aiVector2t<TOther> () const {
+ return aiVector2t<TOther>(static_cast<TOther>(x),static_cast<TOther>(y));
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+void aiVector2t<TReal>::Set( TReal pX, TReal pY) {
+ x = pX; y = pY;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+TReal aiVector2t<TReal>::SquareLength() const {
+ return x*x + y*y;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+TReal aiVector2t<TReal>::Length() const {
+ return std::sqrt( SquareLength());
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+aiVector2t<TReal>& aiVector2t<TReal>::Normalize() {
+ *this /= Length();
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+const aiVector2t<TReal>& aiVector2t<TReal>::operator += (const aiVector2t& o) {
+ x += o.x; y += o.y;
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+const aiVector2t<TReal>& aiVector2t<TReal>::operator -= (const aiVector2t& o) {
+ x -= o.x; y -= o.y;
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+const aiVector2t<TReal>& aiVector2t<TReal>::operator *= (TReal f) {
+ x *= f; y *= f;
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+const aiVector2t<TReal>& aiVector2t<TReal>::operator /= (TReal f) {
+ x /= f; y /= f;
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+TReal aiVector2t<TReal>::operator[](unsigned int i) const {
+ switch (i) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ default:
+ break;
+
+ }
+ return x;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+bool aiVector2t<TReal>::operator== (const aiVector2t& other) const {
+ return x == other.x && y == other.y;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+bool aiVector2t<TReal>::operator!= (const aiVector2t& other) const {
+ return x != other.x || y != other.y;
+}
+
+// ---------------------------------------------------------------------------
+template<typename TReal>
+inline
+bool aiVector2t<TReal>::Equal(const aiVector2t& other, TReal epsilon) const {
+ return
+ std::abs(x - other.x) <= epsilon &&
+ std::abs(y - other.y) <= epsilon;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+aiVector2t<TReal>& aiVector2t<TReal>::operator= (TReal f) {
+ x = y = f;
+ return *this;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+inline
+const aiVector2t<TReal> aiVector2t<TReal>::SymMul(const aiVector2t& o) {
+ return aiVector2t(x*o.x,y*o.y);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// symmetric addition
+template <typename TReal>
+inline
+aiVector2t<TReal> operator + (const aiVector2t<TReal>& v1, const aiVector2t<TReal>& v2) {
+ return aiVector2t<TReal>( v1.x + v2.x, v1.y + v2.y);
+}
+
+// ------------------------------------------------------------------------------------------------
+// symmetric subtraction
+template <typename TReal>
+inline
+aiVector2t<TReal> operator - (const aiVector2t<TReal>& v1, const aiVector2t<TReal>& v2) {
+ return aiVector2t<TReal>( v1.x - v2.x, v1.y - v2.y);
+}
+
+// ------------------------------------------------------------------------------------------------
+// scalar product
+template <typename TReal>
+inline
+TReal operator * (const aiVector2t<TReal>& v1, const aiVector2t<TReal>& v2) {
+ return v1.x*v2.x + v1.y*v2.y;
+}
+
+// ------------------------------------------------------------------------------------------------
+// scalar multiplication
+template <typename TReal>
+inline
+aiVector2t<TReal> operator * ( TReal f, const aiVector2t<TReal>& v) {
+ return aiVector2t<TReal>( f*v.x, f*v.y);
+}
+
+// ------------------------------------------------------------------------------------------------
+// and the other way around
+template <typename TReal>
+inline
+aiVector2t<TReal> operator * ( const aiVector2t<TReal>& v, TReal f) {
+ return aiVector2t<TReal>( f*v.x, f*v.y);
+}
+
+// ------------------------------------------------------------------------------------------------
+// scalar division
+template <typename TReal>
+inline
+aiVector2t<TReal> operator / ( const aiVector2t<TReal>& v, TReal f) {
+ return v * (1/f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// vector division
+template <typename TReal>
+inline
+aiVector2t<TReal> operator / ( const aiVector2t<TReal>& v, const aiVector2t<TReal>& v2) {
+ return aiVector2t<TReal>(v.x / v2.x,v.y / v2.y);
+}
+
+// ------------------------------------------------------------------------------------------------
+// vector negation
+template <typename TReal>
+inline
+aiVector2t<TReal> operator - ( const aiVector2t<TReal>& v) {
+ return aiVector2t<TReal>( -v.x, -v.y);
+}
+
+#endif
+
+#endif // AI_VECTOR2D_INL_INC
diff --git a/thirdparty/assimp/include/assimp/vector3.h b/thirdparty/assimp/include/assimp/vector3.h
new file mode 100644
index 0000000000..7ff25cf0ad
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/vector3.h
@@ -0,0 +1,146 @@
+/*
+---------------------------------------------------------------------------
+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 vector3.h
+ * @brief 3D vector structure, including operators when compiling in C++
+ */
+#pragma once
+#ifndef AI_VECTOR3D_H_INC
+#define AI_VECTOR3D_H_INC
+
+#ifdef __cplusplus
+# include <cmath>
+#else
+# include <math.h>
+#endif
+
+#include "defs.h"
+
+#ifdef __cplusplus
+
+template<typename TReal> class aiMatrix3x3t;
+template<typename TReal> class aiMatrix4x4t;
+
+// ---------------------------------------------------------------------------
+/** Represents a three-dimensional vector. */
+template <typename TReal>
+class aiVector3t
+{
+public:
+ aiVector3t() AI_NO_EXCEPT : x(), y(), z() {}
+ aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {}
+ explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {}
+ aiVector3t( const aiVector3t& o ) = default;
+
+public:
+
+ // combined operators
+ const aiVector3t& operator += (const aiVector3t& o);
+ const aiVector3t& operator -= (const aiVector3t& o);
+ const aiVector3t& operator *= (TReal f);
+ const aiVector3t& operator /= (TReal f);
+
+ // transform vector by matrix
+ aiVector3t& operator *= (const aiMatrix3x3t<TReal>& mat);
+ aiVector3t& operator *= (const aiMatrix4x4t<TReal>& mat);
+
+ // access a single element
+ TReal operator[](unsigned int i) const;
+ TReal& operator[](unsigned int i);
+
+ // comparison
+ bool operator== (const aiVector3t& other) const;
+ bool operator!= (const aiVector3t& other) const;
+ bool operator < (const aiVector3t& other) const;
+
+ bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const;
+
+ template <typename TOther>
+ operator aiVector3t<TOther> () const;
+
+public:
+ /** @brief Set the components of a vector
+ * @param pX X component
+ * @param pY Y component
+ * @param pZ Z component */
+ void Set( TReal pX, TReal pY, TReal pZ);
+
+ /** @brief Get the squared length of the vector
+ * @return Square length */
+ TReal SquareLength() const;
+
+ /** @brief Get the length of the vector
+ * @return length */
+ TReal Length() const;
+
+
+ /** @brief Normalize the vector */
+ aiVector3t& Normalize();
+
+ /** @brief Normalize the vector with extra check for zero vectors */
+ aiVector3t& NormalizeSafe();
+
+ /** @brief Componentwise multiplication of two vectors
+ *
+ * Note that vec*vec yields the dot product.
+ * @param o Second factor */
+ const aiVector3t SymMul(const aiVector3t& o);
+
+ TReal x, y, z;
+};
+
+
+typedef aiVector3t<ai_real> aiVector3D;
+
+#else
+
+struct aiVector3D {
+ ai_real x, y, z;
+};
+
+#endif // __cplusplus
+
+#ifdef __cplusplus
+
+#endif // __cplusplus
+
+#endif // AI_VECTOR3D_H_INC
diff --git a/thirdparty/assimp/include/assimp/vector3.inl b/thirdparty/assimp/include/assimp/vector3.inl
new file mode 100644
index 0000000000..2fce6eddee
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/vector3.inl
@@ -0,0 +1,309 @@
+/*
+---------------------------------------------------------------------------
+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 vector3.inl
+ * @brief Inline implementation of aiVector3t<TReal> operators
+ */
+#pragma once
+#ifndef AI_VECTOR3D_INL_INC
+#define AI_VECTOR3D_INL_INC
+
+#ifdef __cplusplus
+#include "vector3.h"
+
+#include <cmath>
+
+// ------------------------------------------------------------------------------------------------
+/** Transformation of a vector by a 3x3 matrix */
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator * (const aiMatrix3x3t<TReal>& pMatrix, const aiVector3t<TReal>& pVector) {
+ aiVector3t<TReal> res;
+ res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z;
+ res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z;
+ res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z;
+ return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+/** Transformation of a vector by a 4x4 matrix */
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator * (const aiMatrix4x4t<TReal>& pMatrix, const aiVector3t<TReal>& pVector) {
+ aiVector3t<TReal> res;
+ res.x = pMatrix.a1 * pVector.x + pMatrix.a2 * pVector.y + pMatrix.a3 * pVector.z + pMatrix.a4;
+ res.y = pMatrix.b1 * pVector.x + pMatrix.b2 * pVector.y + pMatrix.b3 * pVector.z + pMatrix.b4;
+ res.z = pMatrix.c1 * pVector.x + pMatrix.c2 * pVector.y + pMatrix.c3 * pVector.z + pMatrix.c4;
+ return res;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+template <typename TOther>
+aiVector3t<TReal>::operator aiVector3t<TOther> () const {
+ return aiVector3t<TOther>(static_cast<TOther>(x),static_cast<TOther>(y),static_cast<TOther>(z));
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+void aiVector3t<TReal>::Set( TReal pX, TReal pY, TReal pZ) {
+ x = pX;
+ y = pY;
+ z = pZ;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+TReal aiVector3t<TReal>::SquareLength() const {
+ return x*x + y*y + z*z;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+TReal aiVector3t<TReal>::Length() const {
+ return std::sqrt( SquareLength());
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal>& aiVector3t<TReal>::Normalize() {
+ *this /= Length();
+
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal>& aiVector3t<TReal>::NormalizeSafe() {
+ TReal len = Length();
+ if ( len > static_cast< TReal >( 0 ) ) {
+ *this /= len;
+ }
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+const aiVector3t<TReal>& aiVector3t<TReal>::operator += (const aiVector3t<TReal>& o) {
+ x += o.x;
+ y += o.y;
+ z += o.z;
+
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+const aiVector3t<TReal>& aiVector3t<TReal>::operator -= (const aiVector3t<TReal>& o) {
+ x -= o.x;
+ y -= o.y;
+ z -= o.z;
+
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+const aiVector3t<TReal>& aiVector3t<TReal>::operator *= (TReal f) {
+ x *= f;
+ y *= f;
+ z *= f;
+
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+const aiVector3t<TReal>& aiVector3t<TReal>::operator /= (TReal f) {
+ const TReal invF = (TReal) 1.0 / f;
+ x *= invF;
+ y *= invF;
+ z *= invF;
+
+ return *this;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal>& aiVector3t<TReal>::operator *= (const aiMatrix3x3t<TReal>& mat){
+ return (*this = mat * (*this));
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal>& aiVector3t<TReal>::operator *= (const aiMatrix4x4t<TReal>& mat){
+ return (*this = mat * (*this));
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+TReal aiVector3t<TReal>::operator[](unsigned int i) const {
+ switch (i) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ default:
+ break;
+ }
+ return x;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+TReal& aiVector3t<TReal>::operator[](unsigned int i) {
+// return *(&x + i);
+ switch (i) {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ default:
+ break;
+ }
+ return x;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+bool aiVector3t<TReal>::operator== (const aiVector3t<TReal>& other) const {
+ return x == other.x && y == other.y && z == other.z;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+bool aiVector3t<TReal>::operator!= (const aiVector3t<TReal>& other) const {
+ return x != other.x || y != other.y || z != other.z;
+}
+// ---------------------------------------------------------------------------
+template<typename TReal>
+AI_FORCE_INLINE
+bool aiVector3t<TReal>::Equal(const aiVector3t<TReal>& other, TReal epsilon) const {
+ return
+ std::abs(x - other.x) <= epsilon &&
+ std::abs(y - other.y) <= epsilon &&
+ std::abs(z - other.z) <= epsilon;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+bool aiVector3t<TReal>::operator < (const aiVector3t<TReal>& other) const {
+ return x != other.x ? x < other.x : y != other.y ? y < other.y : z < other.z;
+}
+// ------------------------------------------------------------------------------------------------
+template <typename TReal>
+AI_FORCE_INLINE
+const aiVector3t<TReal> aiVector3t<TReal>::SymMul(const aiVector3t<TReal>& o) {
+ return aiVector3t<TReal>(x*o.x,y*o.y,z*o.z);
+}
+// ------------------------------------------------------------------------------------------------
+// symmetric addition
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator + (const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) {
+ return aiVector3t<TReal>( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
+}
+// ------------------------------------------------------------------------------------------------
+// symmetric subtraction
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator - (const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) {
+ return aiVector3t<TReal>( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+}
+// ------------------------------------------------------------------------------------------------
+// scalar product
+template <typename TReal>
+AI_FORCE_INLINE
+TReal operator * (const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) {
+ return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
+}
+// ------------------------------------------------------------------------------------------------
+// scalar multiplication
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator * ( TReal f, const aiVector3t<TReal>& v) {
+ return aiVector3t<TReal>( f*v.x, f*v.y, f*v.z);
+}
+// ------------------------------------------------------------------------------------------------
+// and the other way around
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator * ( const aiVector3t<TReal>& v, TReal f) {
+ return aiVector3t<TReal>( f*v.x, f*v.y, f*v.z);
+}
+// ------------------------------------------------------------------------------------------------
+// scalar division
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator / ( const aiVector3t<TReal>& v, TReal f) {
+ return v * (1/f);
+}
+// ------------------------------------------------------------------------------------------------
+// vector division
+template <typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator / ( const aiVector3t<TReal>& v, const aiVector3t<TReal>& v2) {
+ return aiVector3t<TReal>(v.x / v2.x,v.y / v2.y,v.z / v2.z);
+}
+// ------------------------------------------------------------------------------------------------
+// cross product
+template<typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator ^ ( const aiVector3t<TReal>& v1, const aiVector3t<TReal>& v2) {
+ return aiVector3t<TReal>( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
+}
+// ------------------------------------------------------------------------------------------------
+// vector negation
+template<typename TReal>
+AI_FORCE_INLINE
+aiVector3t<TReal> operator - ( const aiVector3t<TReal>& v) {
+ return aiVector3t<TReal>( -v.x, -v.y, -v.z);
+}
+
+// ------------------------------------------------------------------------------------------------
+
+#endif // __cplusplus
+#endif // AI_VECTOR3D_INL_INC
diff --git a/thirdparty/assimp/include/assimp/version.h b/thirdparty/assimp/include/assimp/version.h
new file mode 100644
index 0000000000..c62a40e118
--- /dev/null
+++ b/thirdparty/assimp/include/assimp/version.h
@@ -0,0 +1,115 @@
+/*
+---------------------------------------------------------------------------
+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 version.h
+ * @brief Functions to query the version of the Assimp runtime, check
+ * compile flags, ...
+ */
+#pragma once
+#ifndef AI_VERSION_H_INC
+#define AI_VERSION_H_INC
+
+#include "defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------------
+/** @brief Returns a string with legal copyright and licensing information
+ * about Assimp. The string may include multiple lines.
+ * @return Pointer to static string.
+ */
+ASSIMP_API const char* aiGetLegalString (void);
+
+// ---------------------------------------------------------------------------
+/** @brief Returns the current minor version number of Assimp.
+ * @return Minor version of the Assimp runtime the application was
+ * linked/built against
+ */
+ASSIMP_API unsigned int aiGetVersionMinor (void);
+
+// ---------------------------------------------------------------------------
+/** @brief Returns the current major version number of Assimp.
+ * @return Major version of the Assimp runtime the application was
+ * linked/built against
+ */
+ASSIMP_API unsigned int aiGetVersionMajor (void);
+
+// ---------------------------------------------------------------------------
+/** @brief Returns the repository revision of the Assimp runtime.
+ * @return SVN Repository revision number of the Assimp runtime the
+ * application was linked/built against.
+ */
+ASSIMP_API unsigned int aiGetVersionRevision (void);
+
+// ---------------------------------------------------------------------------
+/** @brief Returns the branchname of the Assimp runtime.
+ * @return The current branch name.
+ */
+ASSIMP_API const char *aiGetBranchName();
+
+//! Assimp was compiled as a shared object (Windows: DLL)
+#define ASSIMP_CFLAGS_SHARED 0x1
+//! Assimp was compiled against STLport
+#define ASSIMP_CFLAGS_STLPORT 0x2
+//! Assimp was compiled as a debug build
+#define ASSIMP_CFLAGS_DEBUG 0x4
+
+//! Assimp was compiled with ASSIMP_BUILD_BOOST_WORKAROUND defined
+#define ASSIMP_CFLAGS_NOBOOST 0x8
+//! Assimp was compiled with ASSIMP_BUILD_SINGLETHREADED defined
+#define ASSIMP_CFLAGS_SINGLETHREADED 0x10
+
+// ---------------------------------------------------------------------------
+/** @brief Returns assimp's compile flags
+ * @return Any bitwise combination of the ASSIMP_CFLAGS_xxx constants.
+ */
+ASSIMP_API unsigned int aiGetCompileFlags (void);
+
+#ifdef __cplusplus
+} // end extern "C"
+#endif
+
+#endif // !! #ifndef AI_VERSION_H_INC
+